└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # Janet Features Demos 2 | 3 | Some demos of [Janet features listed at the main 4 | page](https://janet-lang.org/#Features), in order, on a Linux box. 5 | 6 | Links to [official docs](https://janet-lang.org/docs/index.html) 7 | should be provided along the way, highly recommended to check them out 8 | to start the assimila^H^H^H^H^H^H^H^H familiarization process :) 9 | 10 | Hint: if reading this on GitHub, consider using the Table of Contents 11 | icon to navigate to different sections. 12 | 13 | The intent is to touch on the features listed at the main page. This 14 | is a work-in-progress. Currently the list includes: 15 | 16 | * Minimal setup - one binary and you are good to go! 17 | * Builtin support for threads, networking, and an event loop 18 | * First class closures 19 | * Garbage collection 20 | * First class green threads (continuations) 21 | * Mutable and immutable arrays (array/tuple) 22 | * Mutable and immutable hashtables (table/struct) 23 | * Mutable and immutable strings (buffer/string) 24 | * Macros 25 | * Tail call optimization 26 | * Direct interop with C via abstract types and C functions 27 | * Dynamically load C libraries 28 | * Lexical scoping 29 | * REPL and interactive debugger 30 | * Parsing Expression Grammars built in to the core library 31 | * 500+ functions and macros in the core library 32 | * Export your projects to standalone executables with a companion build tool, jpm 33 | * Add to a project with just janet.c and janet.h 34 | 35 | Note that this list is not quite the same as [the one in the 36 | repository README](https://github.com/janet-lang/janet/#features). 37 | 38 | Some additional items from the longer list: 39 | 40 | * Configurable at build time - turn features on or off for a smaller or more featureful build 41 | * Python-style generators (implemented as a plain macro) 42 | 43 | ## Minimal setup - one binary and you are good to go! 44 | 45 | One way to experience this is to download one of the files from the 46 | [GitHub releases page](https://github.com/janet-lang/janet/releases/). 47 | 48 | At the time of this writing, for a Linux environment, I got 49 | [janet-v1.25.1-linux-x64.tar.gz](https://github.com/janet-lang/janet/releases/download/v1.25.1/janet-v1.25.1-linux-x64.tar.gz), 50 | uncompressed it, and was able to run the `janet` binary that lived in 51 | the `bin` subdirectory: 52 | 53 | ```shell 54 | wget --quiet https://github.com/janet-lang/janet/releases/download/v1.25.1/janet-v1.25.1-linux-x64.tar.gz 55 | tar xf janet-v1.25.1-linux-x64.tar.gz 56 | cd janet-v1.25.1-linux 57 | ./bin/janet 58 | ``` 59 | 60 | Personally, I prefer [a non-root local 61 | install](https://janet-lang.org/docs/index.html#Non-root-install-(macOS-and-Unix-like)) 62 | of the master branch (later content in this document will be using the 63 | local installation), but may be the earlier method is better if you 64 | want a quick taste. Some distributions carry packages, but I'm not 65 | sure how up-to-date they are. 66 | 67 | ## Builtin support for threads, networking, and an event loop 68 | 69 | ### Threads 70 | 71 | Here's a snippet from the [Multithreading 72 | docs](https://janet-lang.org/docs/threads.html): 73 | 74 | ```janet 75 | (ev/spawn-thread 76 | (print "New thread started!")) 77 | 78 | (ev/do-thread 79 | (print "New thread started!")) 80 | ``` 81 | 82 | Trying that at the repl: 83 | 84 | ``` 85 | $ janet 86 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 87 | repl:1:> (ev/spawn-thread (print "Thread one")) 88 | nil 89 | repl:2:> Thread one 90 | 91 | repl:3:> (ev/do-thread (print "Thread the second")) 92 | Thread the second 93 | nil 94 | ``` 95 | 96 | Not so clear in text perhaps -- I pressed the Enter key after the 97 | `Thread one` text appeared for aesthetic purposes. 98 | 99 | ### Networking 100 | 101 | Let's ask for some help from our snake friend: 102 | 103 | ```shell 104 | mkdir /tmp/fun 105 | cd /tmp/fun 106 | python3 -m http.server 107 | Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ... 108 | ``` 109 | 110 | Here's a snippet adapted from the [Networking docs](https://janet-lang.org/docs/networking.html): 111 | 112 | ```janet 113 | (with [conn (net/connect "127.0.0.1" "8000" :stream)] 114 | (printf "Connected to %q!" conn) 115 | (:write conn "GET / HTTP/1.0\n\n") 116 | (print "Wrote to connection...") 117 | (def res (:read conn 1024)) 118 | (pp res)) 119 | ``` 120 | 121 | Trying that at the repl: 122 | 123 | ``` 124 | $ janet 125 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 126 | repl:1:> (with [conn (net/connect "127.0.0.1" "8000" :stream)] 127 | repl:2:(> (printf "Connected to %q!" conn) 128 | repl:3:(> (:write conn "GET / HTTP/1.0\n\n") 129 | repl:4:(> (print "Wrote to connection...") 130 | repl:5:(> (def res (:read conn 1024)) 131 | repl:6:(> (pp res)) 132 | Connected to ! 133 | Wrote to connection... 134 | @"HTTP/1.0 200 OK\r\nServer: SimpleHTTP/0.6 Python/3.10.6\r\nDate: Sat, 31 Dec 2022 01:37:29 GMT\r\nContent-type: text/html; charset=utf-8\r\nContent-Length: 607\r\n\r\n" 135 | nil 136 | ``` 137 | 138 | [Servers are doable](https://janet-lang.org/docs/networking.html#Server) too. 139 | 140 | ### Event Loop 141 | 142 | Here is some code from [The Event Loop docs](https://janet-lang.org/docs/event_loop.html): 143 | 144 | ```janet 145 | (defn worker 146 | "Does some work." 147 | [name n] 148 | (for i 0 n 149 | (print name " working " i "...") 150 | (ev/sleep 0.5)) 151 | (print name " is done!")) 152 | 153 | # Start bob working in a new task with ev/call 154 | (ev/call worker "bob" 10) 155 | 156 | (ev/sleep 0.25) 157 | 158 | # Start sally working in a new task with ev/go 159 | (ev/go (fiber/new |(worker "sally" 20))) 160 | 161 | (ev/sleep 11) 162 | (print "Everyone should be done by now!") 163 | ``` 164 | 165 | Pasting the above to the repl a bit at a time. 166 | 167 | First we make a function: 168 | 169 | ``` 170 | $ janet 171 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 172 | repl:1:> (defn worker 173 | repl:2:(> "Does some work." 174 | repl:3:(> [name n] 175 | repl:4:(> (for i 0 n 176 | repl:5:((> (print name " working " i "...") 177 | repl:6:((> (ev/sleep 0.5)) 178 | repl:7:(> (print name " is done!")) 179 | 180 | ``` 181 | 182 | We pass the function to `ev/call` which gives us back a fiber. 183 | 184 | ``` 185 | repl:8:> (ev/call worker "bob" 10) 186 | 187 | ``` 188 | 189 | To get it to start, we need to let it run by temporarily relinquishing 190 | control via `ev/sleep`: 191 | 192 | ``` 193 | repl:9:> (ev/sleep 0.25) 194 | bob working 0... 195 | nil 196 | ``` 197 | 198 | Note the `bob working 0...`. 199 | 200 | Now arrange for another: 201 | 202 | ``` 203 | repl:10:> (ev/go (fiber/new |(worker "sally" 20))) 204 | 205 | ``` 206 | 207 | Relinquish control again: 208 | 209 | ``` 210 | repl:11:> (ev/sleep 11) 211 | sally working 0... 212 | bob working 1... 213 | sally working 1... 214 | bob working 2... 215 | sally working 2... 216 | bob working 3... 217 | sally working 3... 218 | bob working 4... 219 | sally working 4... 220 | bob working 5... 221 | sally working 5... 222 | bob working 6... 223 | sally working 6... 224 | bob working 7... 225 | sally working 7... 226 | bob working 8... 227 | sally working 8... 228 | bob working 9... 229 | sally working 9... 230 | bob is done! 231 | sally working 10... 232 | sally working 11... 233 | sally working 12... 234 | (print "Everyone should be done by now!")sally working 13... 235 | sally working 14... 236 | sally working 15... 237 | sally working 16... 238 | sally working 17... 239 | sally working 18... 240 | sally working 19... 241 | sally is done! 242 | nil 243 | ``` 244 | 245 | I was able to paste `(print "Everyone should be done by now!")` while 246 | output was appearing from ongoing activity. Notice how it appears 247 | between the text `sally working 12...` and `sally working 13...`. 248 | 249 | ## First class closures 250 | 251 | We'll make an "adder" maker: 252 | 253 | ``` 254 | $ janet 255 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 256 | repl:1:> (defn adder-maker [x] (fn [y] (+ x y))) 257 | 258 | ``` 259 | 260 | Now use it to make an adder and call it: 261 | 262 | ``` 263 | repl:2:> (def eight-adder (adder-maker 8)) 264 | 265 | repl:3:> (eight-adder 1) 266 | 9 267 | ``` 268 | 269 | Finally, pass the adder to another function for use: 270 | 271 | 272 | ``` 273 | repl:4:> (defn use-adder-to-increment [an-adder value] (an-adder value)) 274 | 275 | repl:5:> (use-adder-to-increment eight-adder 3) 276 | 11 277 | ``` 278 | 279 | ## Garbage collection 280 | 281 | Not sure how to demonstrate this...how about some 282 | [evidence](https://github.com/janet-lang/janet/blob/master/src/core/gc.c)? 283 | 284 | The [Janet's Memory Model 285 | docs](https://janet-lang.org/capi/memory-model.html) have some details. 286 | 287 | ## First class green threads (continuations) 288 | 289 | In Janet, I believe these are called "fibers". 290 | 291 | Adapting some code from the beginning of the [Fibers 292 | docs](https://janet-lang.org/docs/fibers/index.html): 293 | 294 | ```janet 295 | (def f 296 | (fiber/new (fn [] 297 | (yield 1) 298 | (yield 2) 299 | (yield 3) 300 | (yield 4) 301 | 5))) 302 | 303 | (fiber/status f) 304 | 305 | (resume f) 306 | 307 | (resume f) 308 | (resume f) 309 | (resume f) 310 | 311 | (fiber/status f) 312 | 313 | (resume f) 314 | 315 | (fiber/status f) 316 | 317 | (resume f) 318 | ``` 319 | 320 | Stepping through: 321 | 322 | ``` 323 | $ janet 324 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 325 | repl:1:> (def f 326 | repl:2:(> (fiber/new (fn [] 327 | repl:3:(((> (yield 1) 328 | repl:4:(((> (yield 2) 329 | repl:5:(((> (yield 3) 330 | repl:6:(((> (yield 4) 331 | repl:7:(((> 5))) 332 | 333 | ``` 334 | 335 | Can get the status of a fiber: 336 | 337 | ``` 338 | repl:7:> (fiber/status f) 339 | :new 340 | ``` 341 | 342 | Resume the fiber ("resume" also means start): 343 | 344 | ``` 345 | repl:8:> (resume f) 346 | 1 347 | ``` 348 | 349 | Resume 3 more times: 350 | 351 | ``` 352 | repl:9:> (resume f) 353 | 2 354 | repl:10:> (resume f) 355 | 3 356 | repl:11:> (resume f) 357 | 4 358 | ``` 359 | 360 | Check the fiber's status: 361 | ``` 362 | repl:12:> (fiber/status f) 363 | :pending 364 | ``` 365 | 366 | Resume it again: 367 | ``` 368 | repl:13:> (resume f) 369 | 5 370 | ``` 371 | 372 | Check the status again: 373 | ``` 374 | repl:14:> (fiber/status f) 375 | :dead 376 | ``` 377 | 378 | What if `resume` is called now? 379 | ``` 380 | repl:15:> (resume f) 381 | error: cannot resume fiber with status :dead 382 | in _thunk [repl] (tailcall) on line 15, column 1 383 | ``` 384 | 385 | Oops :) 386 | 387 | See the [Fibers docs](https://janet-lang.org/docs/fibers/index.html) 388 | and the [Fiber Module docs](https://janet-lang.org/api/fiber.html) for 389 | more info. 390 | 391 | ## Mutable and immutable arrays (array/tuple) 392 | 393 | There are two kinds of array-like data structures in Janet, mutable 394 | (array) and immutable (tuple). 395 | 396 | ### Mutable Arrays aka Arrays 397 | 398 | The [Arrays 399 | docs](https://janet-lang.org/docs/data_structures/arrays.html) cover 400 | mutable arrays. In Janet, "array" refers to the mutable type of 401 | array data structure. 402 | 403 | Mutability is typically expressed using a leading `@` character: 404 | 405 | ``` 406 | $ janet 407 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 408 | repl:1:> (def an-array @[1 3 8]) 409 | @[1 3 8] 410 | ``` 411 | 412 | Parentheses can be used as well, but this does not appear common: 413 | 414 | ``` 415 | repl:2:> (def another-array @(2 7 9)) 416 | @[2 7 9] 417 | ``` 418 | 419 | Let's add something: 420 | 421 | ``` 422 | repl:3:> (array/push an-array 2) 423 | @[1 3 8 2] 424 | ``` 425 | 426 | Now get something back: 427 | 428 | ``` 429 | repl:4:> (get an-array 0) 430 | 1 431 | ``` 432 | 433 | ...in a number of other ways: 434 | 435 | ``` 436 | repl:5:> (in an-array 0) 437 | 1 438 | repl:6:> (an-array 0) 439 | 1 440 | repl:7:> (0 an-array) 441 | 1 442 | ``` 443 | 444 | [`get`](https://janet-lang.org/api/index.html#get) and 445 | [`in`](https://janet-lang.org/api/index.html#in) are ordinary 446 | functions. 447 | 448 | See the [Array 449 | docs](https://janet-lang.org/docs/data_structures/arrays.html) and the 450 | [Array Module docs](https://janet-lang.org/api/array.html) for more 451 | info. 452 | 453 | ### Immutable Arrays aka Tuples 454 | 455 | The [Tuples 456 | docs](https://janet-lang.org/docs/data_structures/tuples.html) cover 457 | imumutable arrays. In Janet, "tuple" refers to an immutable type of 458 | array data structure. Note that this is not a "persistent" data 459 | structure as in Clojure. 460 | 461 | Let's make a tuple: 462 | 463 | ``` 464 | $ janet 465 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 466 | repl:1:> (def a-tuple [2 7 9]) 467 | (2 7 9) 468 | ``` 469 | 470 | It's also possible to make a tuple "directly" using parentheses, but 471 | quoting is necessary: 472 | 473 | ``` 474 | repl:2:> (def another-tuple '(3 8 0)) 475 | (3 8 0) 476 | ``` 477 | 478 | Retrieval can be done like: 479 | 480 | ``` 481 | repl:3:> (get a-tuple 0) 482 | 2 483 | ``` 484 | 485 | or in a number of other ways: 486 | 487 | 488 | ``` 489 | repl:4:> (in a-tuple 0) 490 | 2 491 | repl:5:> (a-tuple 0) 492 | 2 493 | repl:6:> (0 a-tuple) 494 | 2 495 | ``` 496 | 497 | [`get`](https://janet-lang.org/api/index.html#get) and 498 | [`in`](https://janet-lang.org/api/index.html#in) are ordinary 499 | functions. 500 | 501 | See the [Tuples 502 | docs](https://janet-lang.org/docs/data_structures/tuples.html) and the 503 | [Tuple Module docs](https://janet-lang.org/api/tuple.html) for more 504 | info. 505 | 506 | ## Mutable and immutable hashtables (table/struct) 507 | 508 | There are two kinds of hash table / associative array-like data 509 | structures in Janet, mutable (table) and immutable (struct). 510 | 511 | ### Tables 512 | 513 | The [Tables 514 | docs](https://janet-lang.org/docs/data_structures/tables.html) cover 515 | mutable hash tables. In Janet, "table" refers to the mutable type of 516 | hash table data structure. 517 | 518 | Mutability is typically expressed using a leading `@` character: 519 | 520 | ``` 521 | $ janet 522 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 523 | repl:1:> (def a-table @{:a 1 :b 2}) 524 | @{:a 1 :b 2} 525 | ``` 526 | Let's add something: 527 | 528 | ``` 529 | repl:2:> (put a-table :c 3) 530 | @{:a 1 :b 2 :c 3} 531 | repl:3:> a-table 532 | @{:a 1 :b 2 :c 3} 533 | ``` 534 | 535 | To get something out: 536 | 537 | ``` 538 | repl:4:> (get a-table :a) 539 | 1 540 | ``` 541 | 542 | This can also be accomplished by: 543 | 544 | ``` 545 | repl:5:> (in a-table :a) 546 | 1 547 | repl:6:> (a-table :a) 548 | 1 549 | ``` 550 | 551 | Note that the following doesn't work in the same way as for arrays and 552 | tuples: 553 | 554 | ``` 555 | repl:7:> (:a a-table) 556 | nil 557 | ``` 558 | 559 | For a hint as to why this is, see the [Object-Oriented Programming 560 | docs](https://janet-lang.org/docs/object_oriented.html). In short, 561 | `(:a a-table)` becomes `((get a-table :a) a-table)` which in turn 562 | leads to `(1 a-table)`. 563 | 564 | So if `1` were added to the table as a key with an associated value: 565 | 566 | ``` 567 | repl:8:> (put a-table 1 :surprise) 568 | @{1 :surprise :a 1 :b 2 :c 3} 569 | ``` 570 | 571 | and another attempt is made, we would get: 572 | 573 | ``` 574 | repl:9:> (:a a-table) 575 | :surprise 576 | ``` 577 | 578 | [`get`](https://janet-lang.org/api/index.html#get) and 579 | [`in`](https://janet-lang.org/api/index.html#in) are ordinary 580 | functions. 581 | 582 | See the [Tables 583 | docs](https://janet-lang.org/docs/data_structures/tables.html) and the 584 | [Table Module docs](https://janet-lang.org/api/table.html) for more 585 | info. 586 | 587 | ### Structs 588 | 589 | The [Structs 590 | docs](https://janet-lang.org/docs/data_structures/structs.html) cover 591 | immutable hash tables. In Janet, "struct" refers to an immutable type 592 | of hash table data structure. Note that this is not a "persistent" 593 | data structure as in Clojure. 594 | 595 | Let's make a struct: 596 | 597 | ``` 598 | $ janet 599 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 600 | repl:1:> (def a-struct {:a 1}) 601 | {:a 1} 602 | ``` 603 | 604 | Retrieval can be done like this: 605 | 606 | ``` 607 | repl:2:> (get a-struct :a) 608 | 1 609 | ``` 610 | 611 | or by either of: 612 | 613 | ``` 614 | repl:3:> (in a-struct :a) 615 | 1 616 | repl:4:> (a-struct :a) 617 | 1 618 | ``` 619 | 620 | Note that the following doesn't work in the same way as for arrays and 621 | tuples: 622 | 623 | ``` 624 | repl:5:> (:a a-struct) 625 | nil 626 | ``` 627 | 628 | For a hint as to why this is, see the [Object-Oriented Programming 629 | docs](https://janet-lang.org/docs/object_oriented.html). In short, 630 | `(:a a-struct)` becomes `((get a-struct :a) a-struct)` which in turn 631 | leads to `(1 a-struct)`. 632 | 633 | If `a-struct` had been defined to contain a key of value `1` with an 634 | associated value: 635 | 636 | ``` 637 | $ janet 638 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 639 | repl:1:> (def a-struct {:a 1 1 :relax}) 640 | {1 :relax :a 1} 641 | ``` 642 | 643 | and an attempt similar to the earlier one is made, we should get: 644 | 645 | ``` 646 | repl:2:> (:a a-struct) 647 | :relax 648 | ``` 649 | 650 | [`get`](https://janet-lang.org/api/index.html#get) and 651 | [`in`](https://janet-lang.org/api/index.html#in) are ordinary 652 | functions. 653 | 654 | See the [Structs 655 | docs](https://janet-lang.org/docs/data_structures/structs.html) and 656 | the `struct`-related calls starting at [the docs for 657 | struct](https://janet-lang.org/api/index.html#struct) for more info. 658 | 659 | ## Mutable and immutable strings (buffer/string) 660 | 661 | There are two kinds of string-like data structures in Janet, mutable 662 | (buffer) and immutable (string). 663 | 664 | ### Buffers 665 | 666 | The [Buffers 667 | docs](https://janet-lang.org/docs/data_structures/buffers.html) cover 668 | mutable strings. In Janet, "buffer" refers to a mutable type of 669 | string data structure. 670 | 671 | Mutability is typically expressed using a leading `@` character: 672 | 673 | ``` 674 | $ janet 675 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 676 | repl:1:> (def a-buffer @"hello") 677 | @"hello" 678 | ``` 679 | 680 | Let's add a character to the end of the buffer: 681 | 682 | ``` 683 | repl:2:> (put a-buffer (length a-buffer) 33) 684 | @"hello!" 685 | ``` 686 | 687 | How about multiple characters at once? 688 | 689 | ``` 690 | repl:3:> (buffer/push-string a-buffer " world?") 691 | @"hello! world?" 692 | ``` 693 | 694 | Retrieval of an individual character can be done by: 695 | 696 | ``` 697 | repl:4:> (get a-buffer 5) 698 | 33 699 | repl:5:> (chr "1") 700 | 33 701 | ``` 702 | 703 | Or: 704 | 705 | ``` 706 | repl:6:> (in a-buffer 5) 707 | 33 708 | repl:7:> (a-buffer 5) 709 | 33 710 | repl:8:> (5 a-buffer) 711 | 33 712 | ``` 713 | 714 | A "sub" buffer (though it's a different buffer) can be retrieved by: 715 | 716 | ``` 717 | repl:9:> (buffer/slice a-buffer 7) 718 | @"world?" 719 | ``` 720 | 721 | [`get`](https://janet-lang.org/api/index.html#get) and 722 | [`in`](https://janet-lang.org/api/index.html#in) are ordinary 723 | functions. 724 | 725 | See the [Buffers 726 | docs](https://janet-lang.org/docs/data_structures/buffers.html) and the 727 | [Buffer Module docs](https://janet-lang.org/api/buffer.html) for more 728 | info. 729 | 730 | ### Strings 731 | 732 | 733 | The [Strings docs](https://janet-lang.org/docs/strings.html#Strings) 734 | cover immutable strings. In Janet, "string" refers to the immutable 735 | type of string as in a number of other programming languages. 736 | 737 | One can make a string by: 738 | 739 | ``` 740 | $ janet 741 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 742 | repl:1:> (def a-string "sit") 743 | "sit" 744 | ``` 745 | 746 | To get a character: 747 | 748 | ``` 749 | repl:2:> (get a-string 2) 750 | 116 751 | repl:3:> (chr "t") 752 | 116 753 | ``` 754 | 755 | Or: 756 | 757 | ``` 758 | repl:4:> (in a-string 2) 759 | 116 760 | repl:5:> (a-string 2) 761 | 116 762 | repl:6:> (2 a-string) 763 | 116 764 | ``` 765 | 766 | Substrings can be retrieved by: 767 | 768 | ``` 769 | repl:7:> (string/slice a-string 1) 770 | "it" 771 | repl:8:> (string/slice a-string 1 2) 772 | "i" 773 | ``` 774 | 775 | [`get`](https://janet-lang.org/api/index.html#get) and 776 | [`in`](https://janet-lang.org/api/index.html#in) are ordinary 777 | functions. 778 | 779 | See the [Strings 780 | docs](https://janet-lang.org/docs/strings.html#Strings) and the 781 | [String Module docs](https://janet-lang.org/api/string.html) for more 782 | info. 783 | 784 | ## Macros 785 | 786 | Adapted from the [Macros 787 | docs](https://janet-lang.org/docs/macros.html): 788 | 789 | ```janet 790 | (defmacro my-defn 791 | "Defines a new function." 792 | [name args & body] 793 | ~(def ,name (fn ,name ,args ,;body))) 794 | ``` 795 | 796 | Let's try it out. 797 | 798 | Define the macro first: 799 | 800 | ``` 801 | $ janet 802 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 803 | repl:1:> (defmacro my-defn 804 | repl:2:(> "Defines a new function." 805 | repl:3:(> [name args & body] 806 | repl:4:(> ~(def ,name (fn ,name ,args ,;body))) 807 | 808 | ``` 809 | 810 | Now call it: 811 | 812 | ``` 813 | repl:5:> (my-defn my-name [x] (+ x 1)) 814 | 815 | repl:6:> (my-name 3) 816 | 4 817 | ``` 818 | 819 | Note that unlike Common Lisp, Scheme, or Clojure, Janet uses `~` for 820 | quasi-quoting / syntax-quoting. There is a corresponding special form 821 | named `quasiquote`. 822 | 823 | `;` is an abbreviation for using the `splice` special form. 824 | 825 | `,` is an abbreviation for using the `unquote` special form. 826 | 827 | See the [Special Forms 828 | docs](https://janet-lang.org/docs/specials.html) for more details about 829 | `~` (`quasiquote`), `;` (`splice`) , and `,` (`unquote`). 830 | 831 | For more info on macros, see the [Macros 832 | docs](https://janet-lang.org/docs/macros.html). 833 | 834 | ## Tail call optimization 835 | 836 | As a placeholder, here is some evidence: 837 | 838 | * https://github.com/janet-lang/janet/blob/a8a78d452506fdbfbf877379eba969e4efbef842/src/core/vm.c#L379 839 | * https://github.com/janet-lang/janet/blob/a8a78d452506fdbfbf877379eba969e4efbef842/src/core/vm.c#L1008-L1052 840 | * https://github.com/janet-lang/janet/blob/a8a78d452506fdbfbf877379eba969e4efbef842/src/core/vm.c#L270-L276 841 | * https://github.com/janet-lang/janet/blob/a8a78d452506fdbfbf877379eba969e4efbef842/src/core/vm.c#L606-L616 842 | 843 | ## Direct interop with C via abstract types and C functions 844 | 845 | See [The Janet C API docs](https://janet-lang.org/capi/index.html) for 846 | more details. 847 | 848 | ## Dynamically load C libraries 849 | 850 | There may be more than one way to interpret what this means. Since 851 | this item has been in the list longer than the recent addition of 852 | Janet's FFI capability, perhaps it didn't use to refer to FFI. 853 | 854 | ### Non-FFI 855 | 856 | I think this refers to Janet's native module capability. 857 | 858 | For a smallish example, see `spork`'s [utf8 859 | module](https://github.com/janet-lang/spork/blob/63f656e6f41b469b484903373187e96616eac837/src/utf8.c), 860 | and perhaps start by looking at the following code at the bottom of 861 | the file: 862 | 863 | ```c 864 | JANET_MODULE_ENTRY(JanetTable *env) { 865 | JanetRegExt cfuns[] = { 866 | JANET_REG("decode-rune", cfun_utf8_decode_rune), 867 | JANET_REG("encode-rune", cfun_utf8_encode_rune), 868 | JANET_REG("prefix->width", cfun_utf8_prefixtowidth), 869 | JANET_REG_END 870 | }; 871 | janet_cfuns_ext(env, "utf8", cfuns); 872 | } 873 | ``` 874 | 875 | Then take a look at: 876 | 877 | ```c 878 | JANET_FN(cfun_utf8_prefixtowidth, 879 | "(utf8/prefix->width c)", 880 | "Given the first byte in an UTF-8 sequence, get the number of bytes that the codepoint sequence takes up, including the prefix byte.") { 881 | janet_fixarity(argc, 1); 882 | uint32_t c = (uint32_t)janet_getinteger(argv, 0); 883 | int32_t n = ((c & 0xF8) == 0xF0) ? 4 : 884 | ((c & 0xF0) == 0xE0) ? 3 : 885 | ((c & 0xE0) == 0xC0) ? 2 : 886 | 1; 887 | return janet_wrap_integer(n); 888 | } 889 | ``` 890 | 891 | Pieces that might be worth investigating further include: 892 | 893 | * `janet_fixarity` 894 | * `janet_getinteger` 895 | * `janet_wrap_integer` 896 | 897 | See the [Writing a Module 898 | section](https://janet-lang.org/capi/index.html#Writing-a-Module) of 899 | [The Janet C API docs](https://janet-lang.org/capi/index.html), the 900 | [Writing C Functions 901 | docs](https://janet-lang.org/capi/writing-c-functions.html), and the 902 | [Wrapping Types docs](https://janet-lang.org/capi/wrapping.html) for 903 | details. 904 | 905 | ### FFI 906 | 907 | For a taste, consider the following from the [FFI 908 | docs](https://janet-lang.org/docs/ffi.html): 909 | 910 | ```janet 911 | (ffi/context nil) 912 | 913 | (ffi/defbind memcpy :ptr 914 | [dest :ptr src :ptr n :size]) 915 | 916 | (def buffer1 @"aaaa") 917 | (def buffer2 @"bbbb") 918 | 919 | (memcpy buffer1 buffer2 4) 920 | 921 | (print buffer1) 922 | ``` 923 | 924 | First, the path to the dynamic library (note that `nil` means to use 925 | the current binary) to bind is specified via `ffi/context`: 926 | 927 | ``` 928 | $ janet 929 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 930 | repl:1:> (ffi/context nil) 931 | @{:map-symbols :native } 932 | ``` 933 | 934 | Use `ffi/defbind` to generate bindings for a native function (in this 935 | case `memcpy`). We specify information about the name, return type and 936 | parameters of the function: 937 | 938 | ``` 939 | repl:2:> (ffi/defbind memcpy :ptr [dest :ptr src :ptr n :size]) 940 | 941 | ``` 942 | 943 | Note that the return value of `ffi/defbind` is a function. This new 944 | function will be used shortly. 945 | 946 | Now prepare some buffers, `buffer1` as a destination and `buffer2` as a source: 947 | 948 | ``` 949 | repl:3:> (def buffer1 @"aaaa") 950 | @"aaaa" 951 | repl:4:> (def buffer2 @"bbbb") 952 | @"bbbb" 953 | ``` 954 | 955 | Note that `buffer1` is initially `@"aaaa"`. 956 | 957 | Invoke `memcpy` (the function newly defined by `ffi/defbind` above): 958 | 959 | ``` 960 | repl:5:> (memcpy buffer1 buffer2 4) 961 | 962 | ``` 963 | 964 | This should have copied from `buffer2` to `buffer1`. The return value 965 | is not interesting in this case so we ignore it. 966 | 967 | Now observe the content of `buffer1`: 968 | 969 | ``` 970 | repl:8:> (print buffer1) 971 | bbbb 972 | nil 973 | ``` 974 | 975 | Note that the original "aaaa" is gone (`nil` is the return value of 976 | calling `print`). 977 | 978 | The content of `buffer` could have been determined as follows too: 979 | 980 | ``` 981 | repl:9:> buffer1 982 | @"bbbb" 983 | ``` 984 | 985 | For a more in-depth example, see the [gtk 986 | example](https://github.com/janet-lang/janet/blob/a8a78d452506fdbfbf877379eba969e4efbef842/examples/ffi/gtk.janet). 987 | 988 | Note, I had to tweak the location of `libgtk-3.so` in `gtk.janet` to 989 | be: 990 | 991 | ``` 992 | /usr/lib/x86_64-linux-gnu/libgtk-3.so 993 | ``` 994 | 995 | but after that, I had success via the invocation: 996 | 997 | ``` 998 | janet gtk.janet 999 | ``` 1000 | 1001 | See the [Foreign Function Interface 1002 | docs](https://janet-lang.org/docs/ffi.html) and the [FFI Module 1003 | docs](https://janet-lang.org/api/ffi.html) for more information. 1004 | 1005 | ## Lexical scoping 1006 | 1007 | Does the following count as evidence? 1008 | 1009 | ``` 1010 | $ janet 1011 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 1012 | repl:1:> (def a 2) 1013 | 2 1014 | repl:2:> (defn b [] (def a 3) a) 1015 | 1016 | repl:3:> (b) 1017 | 3 1018 | repl:4:> a 1019 | 2 1020 | ``` 1021 | 1022 | ## REPL and interactive debugger 1023 | 1024 | ### REPL 1025 | 1026 | Calling `janet` at the command line gives a repl: 1027 | 1028 | ``` 1029 | $ janet 1030 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 1031 | repl:1:> 1 1032 | 1 1033 | ``` 1034 | 1035 | There is a sometimes-helpful delimiter reminder feature: 1036 | 1037 | ``` 1038 | $ janet 1039 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 1040 | repl:1:> (defn my-fn 1041 | repl:2:(> [x] 1042 | repl:3:(> (+ x 1 1043 | repl:4:((> ) 1044 | repl:5:(> ) 1045 | 1046 | ``` 1047 | 1048 | Note that in the repl's prompt after the last colon `:` character and 1049 | before the greater-than `>` character, there are one or more opening 1050 | delimiters in some of the above lines: 1051 | 1052 | ``` 1053 | repl:3:(> (+ x 1 1054 | repl:4:((> ) 1055 | ``` 1056 | 1057 | These are reminders that there are unmatched opening delimiters 1058 | waiting to be closed. 1059 | 1060 | Simple completion is possible via the Tab key: 1061 | 1062 | ``` 1063 | $ janet 1064 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 1065 | repl:1:> (do 1066 | do doc doc* doc-format doc-of 1067 | dofile 1068 | ``` 1069 | 1070 | To get the above output, I pressed the Tab key after typing "(do". 1071 | 1072 | There is a way to quickly see docstrings for various things: 1073 | 1074 | ``` 1075 | $ janet 1076 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 1077 | repl:1:> array/push 1078 | 1079 | 1080 | cfunction 1081 | src/core/array.c on line 179, column 1 1082 | 1083 | (array/push arr x) 1084 | 1085 | Insert an element in the end of an array. Modifies the input array and 1086 | returns it. 1087 | ``` 1088 | 1089 | After typing "array/push", I entered the sequence Ctrl-G (holding the 1090 | Ctrl key down and pressing and releasing the G key). 1091 | 1092 | Some other key sequences such as Ctrl-A, Ctrl-E, etc. behave in 1093 | similar ways to what one might use in a shell such as bash. The 1094 | janet(1) man page has a longer listing. I don't know of a nicely 1095 | viewable list of the key sequences on the web, but there's [this bit 1096 | from the main 1097 | repository](https://github.com/janet-lang/janet/blob/a8a78d452506fdbfbf877379eba969e4efbef842/janet.1#L32-L141). 1098 | 1099 | ### Interactive Debugger 1100 | 1101 | One way to get access to the bytecode debugger is to use the `-d` 1102 | command line option to `janet`, then use the `debug` function: 1103 | 1104 | ``` 1105 | $ janet -d 1106 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 1107 | repl:1:> (debug) 1108 | debug: 1109 | in _thunk [repl] (tailcall) on line 1, column 1 1110 | entering debug[1] - (quit) to exit 1111 | debug[1]:1:> (quit) 1112 | nil 1113 | exiting debug[1] 1114 | nil 1115 | ``` 1116 | 1117 | Putting a call to `debug` in a function and calling that function can 1118 | be handy too: 1119 | 1120 | ``` 1121 | $ janet -d 1122 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 1123 | repl:1:> (defn my-fn [x] (debug) (+ x 1)) 1124 | 1125 | repl:2:> (my-fn 8) 1126 | debug: 1127 | in my-fn [repl] on line 1, column 17 1128 | in _thunk [repl] (tailcall) on line 2, column 1 1129 | entering debug[1] - (quit) to exit 1130 | ``` 1131 | 1132 | We can get a bytecode listing via `.ppasm`: 1133 | 1134 | ``` 1135 | debug[1]:1:> (.ppasm) 1136 | 1137 | signal: 1138 | status: debug 1139 | function: my-fn [repl] 1140 | constants: @[] 1141 | slots: @[8 nil nil] 1142 | 1143 | lds 1 # line 1, column 1 1144 | ldn 3 # line 1, column 17 1145 | > sig 2 3 2 1146 | addim 3 0 1 # line 1, column 25 1147 | ret 3 1148 | 1149 | nil 1150 | ``` 1151 | 1152 | Execute one bytecode instruction via `.step`: 1153 | ``` 1154 | debug[1]:2:> (.step) 1155 | nil 1156 | debug[1]:3:> (.ppasm) 1157 | 1158 | signal: 1159 | status: debug 1160 | function: my-fn [repl] 1161 | constants: @[] 1162 | slots: @[8 nil nil] 1163 | 1164 | lds 1 # line 1, column 1 1165 | ldn 3 # line 1, column 17 1166 | sig 2 3 2 1167 | > addim 3 0 1 # line 1, column 25 1168 | ret 3 1169 | 1170 | nil 1171 | ``` 1172 | 1173 | ...and keep going to see the function return: 1174 | 1175 | ``` 1176 | debug[1]:4:> (.step) 1177 | nil 1178 | debug[1]:5:> (.ppasm) 1179 | 1180 | signal: 1181 | status: debug 1182 | function: my-fn [repl] 1183 | constants: @[] 1184 | slots: @[8 nil 9] 1185 | 1186 | lds 1 # line 1, column 1 1187 | ldn 3 # line 1, column 17 1188 | sig 2 3 2 1189 | addim 3 0 1 # line 1, column 25 1190 | > ret 3 1191 | 1192 | nil 1193 | debug[1]:6:> (.step) 1194 | 9 1195 | ``` 1196 | 1197 | Note that the value `9` is the return value of the function call. 1198 | 1199 | When we're done we can exit the debugger to return to the original 1200 | repl context: 1201 | 1202 | ``` 1203 | debug[1]:7:> (quit) 1204 | nil 1205 | exiting debug[1] 1206 | ``` 1207 | 1208 | See [this part of 1209 | boot.janet](https://github.com/janet-lang/janet/blob/a8a78d452506fdbfbf877379eba969e4efbef842/src/boot/boot.janet#L3353-L3507) 1210 | for which functions are available for the debugger and [The Janet 1211 | Abstract Machine 1212 | docs](https://janet-lang.org/docs/abstract_machine.html) for more on 1213 | Janet bytecode and bytecode interpreter. 1214 | 1215 | Also of interest might be the [Debug Module 1216 | docs](https://janet-lang.org/api/debug.html). 1217 | 1218 | ## Parsing Expression Grammars built in to the core library 1219 | 1220 | From the [Parsing Expression Grammars docs](https://janet-lang.org/docs/peg.html) is: 1221 | 1222 | ```janet 1223 | (def ip-address 1224 | '{:dig (range "09") 1225 | :0-4 (range "04") 1226 | :0-5 (range "05") 1227 | :byte (choice 1228 | (sequence "25" :0-5) 1229 | (sequence "2" :0-4 :dig) 1230 | (sequence "1" :dig :dig) 1231 | (between 1 2 :dig)) 1232 | :main (sequence :byte "." :byte "." :byte "." :byte)}) 1233 | ``` 1234 | 1235 | Note that this expression refers to a plain Janet struct. 1236 | 1237 | Let's define it at the repl: 1238 | 1239 | ``` 1240 | $ janet 1241 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 1242 | repl:1:> (def ip-address 1243 | repl:2:(> '{:dig (range "09") 1244 | repl:3:({> :0-4 (range "04") 1245 | repl:4:({> :0-5 (range "05") 1246 | repl:5:({> :byte (choice 1247 | repl:6:({(> (sequence "25" :0-5) 1248 | repl:7:({(> (sequence "2" :0-4 :dig) 1249 | repl:8:({(> (sequence "1" :dig :dig) 1250 | repl:9:({(> (between 1 2 :dig)) 1251 | repl:10:({> :main (sequence :byte "." :byte "." :byte "." :byte)}) 1252 | {:0-4 (range "04") :0-5 (range "05") :byte (choice (sequence "25" :0-5) (sequence "2" :0-4 :dig) (sequence "1" :dig :dig) (between 1 2 :dig)) :dig (range "09") :main (sequence :byte "." :byte "." :byte "." :byte)} 1253 | ``` 1254 | 1255 | Typically one might use this via the function `peg/match`: 1256 | 1257 | ``` 1258 | repl:11:> (peg/match ip-address "1") 1259 | nil 1260 | ``` 1261 | 1262 | A `nil` return value means there was no match. 1263 | 1264 | For a match: 1265 | 1266 | ``` 1267 | repl:12:> (peg/match ip-address "127.0.0.1") 1268 | @[] 1269 | ``` 1270 | 1271 | One gets back an array of captures. In this case there was a match, 1272 | but nothing was captured, so the array is empty. 1273 | 1274 | For an example of a capturing PEG expression, try: 1275 | 1276 | ``` 1277 | repl:13:> (peg/match ~(capture 3) "127.0.0.1") 1278 | @["127"] 1279 | ``` 1280 | 1281 | The first 3 characters were captured as a unit and returned as an 1282 | element of the "capture stack". 1283 | 1284 | A slightly more complicated example is: 1285 | 1286 | ``` 1287 | repl:14:> (def p ~(sequence (any :s) (capture (some :d)) (any :s) (capture (some :d)))) 1288 | (sequence (any :s) (capture (some :d)) (any :s) (capture (some :d))) 1289 | repl:15:> (peg/match p " 12 89.") 1290 | @["12" "89"] 1291 | ``` 1292 | 1293 | The PEG might be more readily perceived as: 1294 | 1295 | ```janet 1296 | (def p 1297 | ~(sequence (any :s) 1298 | (capture (some :d)) 1299 | (any :s) 1300 | (capture (some :d)))) 1301 | ``` 1302 | 1303 | Above, initial whitespace is skipped (matched but not captured), a 1304 | sequence of digits is captured, more whitespace is skipped and then 1305 | finally a sequence of digts is captured again. 1306 | 1307 | Note that there are 2 captures on the capture stack "12" and "89" and 1308 | the trailing period has not been captured. 1309 | 1310 | Also note that the definition of `ip-address` from earlier can be 1311 | condensed (and the key-value pairs reordered) as follows: 1312 | 1313 | ```janet 1314 | (def ip-address 1315 | '{:main (* :byte "." :byte "." :byte "." :byte) 1316 | :byte (+ (* "25" :0-5) 1317 | (* "2" :0-4 :d) 1318 | (* "1" :d :d) 1319 | (between 1 2 :d)) 1320 | :0-5 (range "05") 1321 | :0-4 (range "04")}) 1322 | ``` 1323 | 1324 | `*` is an alias for `sequence`. 1325 | 1326 | `+` is an alias for `choice`. 1327 | 1328 | `:d` is defined in `default-peg-grammar`...along with a number of other things: 1329 | 1330 | ``` 1331 | $ janet 1332 | Janet 1.26.0-dev-a8a78d45 linux/x64 - '(doc)' for help 1333 | repl:1:> default-peg-grammar 1334 | @{:A (if-not :a 1) :D (if-not :d 1) :H (if-not :h 1) :S (if-not :s 1) :W (if-not :w 1) :a (range "az" "AZ") :a* (any :a) :a+ (some :a) :d (range "09") :d* (any :d) :d+ (some :d) :h (range "09" "af" "AF") :h* (any :h) :h+ (some :h) :s (set " \t\r\n\0\f\v") :s* (any :s) :s+ (some :s) :w (range "az" "AZ" "09") :w* (any :w) :w+ (some :w)} 1335 | ``` 1336 | 1337 | FWIW, [its definition in 1338 | `boot.janet`](https://github.com/janet-lang/janet/blob/a8a78d452506fdbfbf877379eba969e4efbef842/src/boot/boot.janet#L2221-L2243) 1339 | is prettier. 1340 | 1341 | Especially while learning I recommend using the longer names as: 1342 | 1343 | * some of the aliases might be confusing if you are used to regular 1344 | expressions (I'm looking at you `*` and `+` -- though I take it 1345 | these are based on those from Lua's LPEG), and 1346 | 1347 | * if you return to looking at Janet code after not having looked in a while 1348 | you might not immediately recall the aliases 1349 | 1350 | See the [Parsing Expression Grammars 1351 | docs](https://janet-lang.org/docs/peg.html) and the [PEG 1352 | Module](https://janet-lang.org/api/peg.html) for official docs. 1353 | 1354 | Highly recommended is pyrmont's excellent [How-To: Using PEGs in 1355 | Janet](https://articles.inqk.net/2020/09/19/how-to-use-pegs-in-janet.html) 1356 | article. 1357 | 1358 | For simple examples and docs, follow individual links at [this listing 1359 | of the PEG 1360 | specials](https://github.com/sogaiu/margaret#specials-implementation-status) 1361 | along with [some extracted real-world 1362 | usage](https://github.com/sogaiu/margaret/tree/master/samples/pegs). 1363 | 1364 | Yes, that last bit is a shameless plug :) 1365 | 1366 | ## 500+ functions and macros in the core library 1367 | 1368 | See the [Core API docs](https://janet-lang.org/api/index.html), but 1369 | also [JanetDocs](https://janetdocs.com/), a community documentation 1370 | site. 1371 | 1372 | ## Export your projects to standalone executables with a companion build tool, jpm 1373 | 1374 | Install [jpm](https://github.com/janet-lang/jpm) first. 1375 | 1376 | Create a new project: 1377 | 1378 | ```shell 1379 | cd /tmp 1380 | jpm new-project yo 1381 | ``` 1382 | 1383 | Answer some questions (if you want): 1384 | 1385 | ```shell 1386 | author? yours truly 1387 | description? yo 1388 | creating project directory for yo 1389 | ``` 1390 | 1391 | Look inside: 1392 | 1393 | ```shell 1394 | cd yo 1395 | tree yo 1396 | yo 1397 | ├── bin 1398 | ├── CHANGELOG.md 1399 | ├── LICENSE 1400 | ├── project.janet 1401 | ├── README.md 1402 | ├── test 1403 | │   └── basic.janet 1404 | └── yo 1405 | └── init.janet 1406 | 1407 | 3 directories, 6 files 1408 | ``` 1409 | 1410 | Make an executable with jpm's `quickbin` subcommand: 1411 | 1412 | ```shell 1413 | jpm quickbin yo/init.janet executable-yo 1414 | generating executable c source executable-yo.c from yo/init.janet... 1415 | compiling executable-yo.c to build/executable-yo.o... 1416 | linking executable-yo... 1417 | ``` 1418 | 1419 | Try it out: 1420 | 1421 | ```shell 1422 | ./executable-yo 1423 | Hello! 1424 | ``` 1425 | 1426 | Get curious: 1427 | 1428 | ```shell 1429 | cat yo/init.janet 1430 | ``` 1431 | 1432 | ```janet 1433 | (defn hello 1434 | `Evaluates to "Hello!"` 1435 | [] 1436 | "Hello!") 1437 | 1438 | (defn main 1439 | [& args] 1440 | (print (hello))) 1441 | ``` 1442 | 1443 | See the [jpm docs](https://janet-lang.org/docs/jpm.html), the [jpm 1444 | repository](https://github.com/janet-lang/jpm), and the generated 1445 | [JPM](https://janet-lang.org/api/jpm/index.html) reference for more info. 1446 | 1447 | 1448 | ## Add to a project with just janet.c and janet.h 1449 | 1450 | Some examples of doing this (or similar) include: 1451 | 1452 | * The [janet-lang.org website](https://github.com/janet-lang/janet-lang.org/) 1453 | * ahungry's [puny-gui](https://github.com/ahungry/puny-gui/) - a small 1454 | cross-platform (native) GUI setup (GNU/Linux + Windows) 1455 | * cfoust's [cy](https://github.com/cfoust/cy) - time travel in the 1456 | terminal (mostly in go) 1457 | * greenfork's [jzignet](https://github.com/greenfork/jzignet) - Zig 1458 | library to connect Janet and Zig together 1459 | * gwegash's [trane](https://github.com/gwegash/trane) - a lispy 1460 | livecoding environment 1461 | * ianthehenry's [bauble](https://github.com/ianthehenry/bauble) - for 1462 | composing signed distance functions in a high-level language 1463 | (Janet), compiling them to GLSL, and rendering them via WebGL 1464 | * MikeBeller's 1465 | [janet-playground](https://github.com/MikeBeller/janet-playground) - 1466 | A WebAssembly based playground for Janet 1467 | * s5bug's [sys-script](https://github.com/s5bug/sys-script) - 1468 | Controlling the Nintendo Switch with Lisp 1469 | * sepisoad's 1470 | [super-janet-typist](https://github.com/sepisoad/super-janet-typist/) - 1471 | a short typing game made with janet lisp 1472 | * yumaikas' 1473 | [janet-peg-playground](https://github.com/yumaikas/janet-peg-playground) - 1474 | A WebAssembly based playground for Janet's peg/match ([demo 1475 | site](https://junglecoder.com/playgrounds/PEGs/)) 1476 | * zacharycarter's [voodoo](https://github.com/zacharycarter/voodoo) - 1477 | 3D game programming and rapid prototyping library 1478 | * [jaylib-wasm-demo](https://github.com/sogaiu/jaylib-wasm-demo) - A 1479 | demo of using jaylib in a web browser 1480 | 1481 | See [The Janet C API docs](https://janet-lang.org/capi/index.html) and 1482 | the [Embedding docs](https://janet-lang.org/capi/embedding.html) for 1483 | more details. 1484 | 1485 | --- 1486 | 1487 | ## Additional Items 1488 | 1489 | * Configurable at build time - turn features on or off for a smaller or more featureful build 1490 | 1491 | See the [Configuration docs](https://janet-lang.org/capi/configuration.html). 1492 | 1493 | * Python-style generators (implemented as a plain macro) 1494 | 1495 | See the [generate](https://janet-lang.org/api/index.html#generate) macro and at 1496 | least [one example at JanetDocs](https://janetdocs.com/generate). 1497 | 1498 | 1499 | --------------------------------------------------------------------------------