aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml112
-rw-r--r--.travis.yml45
-rw-r--r--ChangeLog39
-rw-r--r--README.md206
-rw-r--r--TODO7
-rw-r--r--VERSION1
-rw-r--r--api/api.c2506
-rw-r--r--api/api_atom.c1214
-rw-r--r--api/api_atom.h205
-rw-r--r--api/api_forge.c2152
-rw-r--r--api/api_forge.h55
-rw-r--r--api/api_midi.c237
-rw-r--r--api/api_midi.h38
-rw-r--r--api/api_osc.c334
-rw-r--r--api/api_osc.h30
-rw-r--r--api/api_parameter.c113
-rw-r--r--api/api_parameter.h28
-rw-r--r--api/api_stash.c101
-rw-r--r--api/api_stash.h49
-rw-r--r--api/api_state.c847
-rw-r--r--api/api_state.h28
-rw-r--r--api/api_time.c328
-rw-r--r--api/api_time.h34
-rw-r--r--api/api_vm.c383
-rw-r--r--api/api_vm.h88
-rw-r--r--canvas.lv2/COPYING201
-rw-r--r--canvas.lv2/README.md18
-rw-r--r--canvas.lv2/canvas.lv2/canvas.h (renamed from canvas.lv2/canvas.h)0
-rw-r--r--canvas.lv2/canvas.lv2/forge.h (renamed from canvas.lv2/forge.h)0
-rw-r--r--canvas.lv2/canvas.lv2/idisp.h (renamed from canvas.lv2/idisp.h)0
-rw-r--r--canvas.lv2/canvas.lv2/lv2_extensions.h (renamed from canvas.lv2/lv2_extensions.h)0
-rw-r--r--canvas.lv2/canvas.lv2/render.h (renamed from canvas.lv2/render.h)0
-rw-r--r--canvas.lv2/canvas.lv2/render_cairo.h (renamed from canvas.lv2/render_cairo.h)0
-rw-r--r--canvas.lv2/canvas.lv2/render_nanovg.h (renamed from canvas.lv2/render_nanovg.h)0
-rw-r--r--cmake/arm-linux-gnueabihf.cmake20
-rw-r--r--cmake/i686-linux-gnu.cmake7
-rw-r--r--cmake/i686-w64-mingw32.cmake24
-rw-r--r--cmake/universal-apple-darwin.cmake20
-rw-r--r--cmake/x86_64-linux-gnu.cmake4
-rw-r--r--cmake/x86_64-w64-mingw32.cmake24
-rwxr-xr-xcoverage.sh17
-rwxr-xr-xdocker_build.sh25
-rwxr-xr-xdocker_run.sh5
-rw-r--r--ext_ui.lv2/lv2_external_ui.h109
-rw-r--r--include/moony.h474
-rw-r--r--laes128/laes128.c158
-rw-r--r--laes128/laes128.h21
-rw-r--r--lascii85/Makefile48
-rw-r--r--lascii85/README20
-rw-r--r--lascii85/lascii85.c130
-rw-r--r--lascii85/test.lua43
-rw-r--r--lbase64/Makefile48
-rw-r--r--lbase64/README20
-rw-r--r--lbase64/lbase64.c119
-rw-r--r--lbase64/test.lua65
-rw-r--r--lcomplex/Makefile48
-rw-r--r--lcomplex/README29
-rw-r--r--lcomplex/lcomplex.c172
-rw-r--r--lcomplex/test.lua41
-rw-r--r--lmathx/Makefile48
-rw-r--r--lmathx/README34
-rw-r--r--lmathx/lmathx.c438
-rw-r--r--lmathx/test.lua87
-rw-r--r--logo/COPYING6
-rw-r--r--logo/moony_logo.pngbin0 -> 9344 bytes
-rw-r--r--logo/moony_logo.svg61
-rw-r--r--logo/omk_logo_256x256.pngbin0 -> 6916 bytes
-rw-r--r--lpeg-1.0.1/HISTORY96
-rw-r--r--lpeg-1.0.1/lpcap.c537
-rw-r--r--lpeg-1.0.1/lpcap.h56
-rw-r--r--lpeg-1.0.1/lpcode.c1014
-rw-r--r--lpeg-1.0.1/lpcode.h40
-rw-r--r--lpeg-1.0.1/lpeg-128.gifbin0 -> 4923 bytes
-rw-r--r--lpeg-1.0.1/lpeg.html1445
-rw-r--r--lpeg-1.0.1/lpprint.c244
-rw-r--r--lpeg-1.0.1/lpprint.h36
-rw-r--r--lpeg-1.0.1/lptree.c1303
-rw-r--r--lpeg-1.0.1/lptree.h82
-rw-r--r--lpeg-1.0.1/lptypes.h149
-rw-r--r--lpeg-1.0.1/lpvm.c364
-rw-r--r--lpeg-1.0.1/lpvm.h58
-rw-r--r--lpeg-1.0.1/makefile55
-rw-r--r--lpeg-1.0.1/re.html498
-rw-r--r--lpeg-1.0.1/re.lua259
-rwxr-xr-xlpeg-1.0.1/test.lua1503
-rw-r--r--lrandom/Makefile50
-rw-r--r--lrandom/README25
-rw-r--r--lrandom/lrandom.c120
-rw-r--r--lrandom/random.c142
-rw-r--r--lrandom/test.lua74
-rw-r--r--lua-5.3.4/lapi.c1298
-rw-r--r--lua-5.3.4/lapi.h24
-rw-r--r--lua-5.3.4/lauxlib.c1043
-rw-r--r--lua-5.3.4/lauxlib.h264
-rw-r--r--lua-5.3.4/lbaselib.c498
-rw-r--r--lua-5.3.4/lbitlib.c233
-rw-r--r--lua-5.3.4/lcode.c1203
-rw-r--r--lua-5.3.4/lcode.h88
-rw-r--r--lua-5.3.4/lcorolib.c168
-rw-r--r--lua-5.3.4/lctype.c55
-rw-r--r--lua-5.3.4/lctype.h95
-rw-r--r--lua-5.3.4/ldblib.c456
-rw-r--r--lua-5.3.4/ldebug.c698
-rw-r--r--lua-5.3.4/ldebug.h39
-rw-r--r--lua-5.3.4/ldo.c802
-rw-r--r--lua-5.3.4/ldo.h58
-rw-r--r--lua-5.3.4/ldump.c215
-rw-r--r--lua-5.3.4/lfunc.c151
-rw-r--r--lua-5.3.4/lfunc.h61
-rw-r--r--lua-5.3.4/lgc.c1178
-rw-r--r--lua-5.3.4/lgc.h147
-rw-r--r--lua-5.3.4/linit.c68
-rw-r--r--lua-5.3.4/liolib.c771
-rw-r--r--lua-5.3.4/llex.c565
-rw-r--r--lua-5.3.4/llex.h85
-rw-r--r--lua-5.3.4/llimits.h323
-rw-r--r--lua-5.3.4/lmathlib.c410
-rw-r--r--lua-5.3.4/lmem.c100
-rw-r--r--lua-5.3.4/lmem.h69
-rw-r--r--lua-5.3.4/loadlib.c790
-rw-r--r--lua-5.3.4/lobject.c521
-rw-r--r--lua-5.3.4/lobject.h549
-rw-r--r--lua-5.3.4/lopcodes.c124
-rw-r--r--lua-5.3.4/lopcodes.h297
-rw-r--r--lua-5.3.4/loslib.c407
-rw-r--r--lua-5.3.4/lparser.c1650
-rw-r--r--lua-5.3.4/lparser.h133
-rw-r--r--lua-5.3.4/lprefix.h45
-rw-r--r--lua-5.3.4/lstate.c347
-rw-r--r--lua-5.3.4/lstate.h235
-rw-r--r--lua-5.3.4/lstring.c248
-rw-r--r--lua-5.3.4/lstring.h49
-rw-r--r--lua-5.3.4/lstrlib.c1584
-rw-r--r--lua-5.3.4/ltable.c669
-rw-r--r--lua-5.3.4/ltable.h66
-rw-r--r--lua-5.3.4/ltablib.c450
-rw-r--r--lua-5.3.4/ltm.c165
-rw-r--r--lua-5.3.4/ltm.h76
-rw-r--r--lua-5.3.4/lua.h486
-rw-r--r--lua-5.3.4/luaconf.h784
-rw-r--r--lua-5.3.4/lualib.h61
-rw-r--r--lua-5.3.4/lundump.c279
-rw-r--r--lua-5.3.4/lundump.h32
-rw-r--r--lua-5.3.4/lutf8lib.c256
-rw-r--r--lua-5.3.4/lvm.c1322
-rw-r--r--lua-5.3.4/lvm.h113
-rw-r--r--lua-5.3.4/lzio.c68
-rw-r--r--lua-5.3.4/lzio.h66
-rw-r--r--manual/hilight.lua57
-rw-r--r--manual/manual.html.in4881
-rw-r--r--meson.build523
-rw-r--r--meson_options.txt3
-rw-r--r--nk_pugl/COPYING201
-rw-r--r--nk_pugl/nk_pugl.h1371
-rw-r--r--nuklear/.gitattributes3
-rw-r--r--nuklear/.gitignore8
-rw-r--r--nuklear/.gitmodules0
-rw-r--r--nuklear/.travis.yml16
-rw-r--r--nuklear/Readme.md165
-rw-r--r--nuklear/demo/allegro5/KeyboardHandleriOS.h10
-rw-r--r--nuklear/demo/allegro5/KeyboardHandleriOS.m120
-rw-r--r--nuklear/demo/allegro5/Makefile22
-rw-r--r--nuklear/demo/allegro5/Readme.md35
-rw-r--r--nuklear/demo/allegro5/main.c165
-rw-r--r--nuklear/demo/allegro5/nuklear_allegro5.h459
-rw-r--r--nuklear/demo/calculator.c64
-rw-r--r--nuklear/demo/d3d11/build.bat9
-rw-r--r--nuklear/demo/d3d11/main.c299
-rw-r--r--nuklear/demo/d3d11/nuklear_d3d11.h622
-rw-r--r--nuklear/demo/d3d11/nuklear_d3d11.hlsl36
-rw-r--r--nuklear/demo/d3d11/nuklear_d3d11_pixel_shader.h179
-rw-r--r--nuklear/demo/d3d11/nuklear_d3d11_vertex_shader.h350
-rw-r--r--nuklear/demo/d3d9/build.bat6
-rw-r--r--nuklear/demo/d3d9/main.c313
-rw-r--r--nuklear/demo/d3d9/nuklear_d3d9.h543
-rw-r--r--nuklear/demo/gdi/build.bat6
-rw-r--r--nuklear/demo/gdi/main.c184
-rw-r--r--nuklear/demo/gdi/nuklear_gdi.h842
-rw-r--r--nuklear/demo/gdip/build.bat5
-rw-r--r--nuklear/demo/gdip/main.c178
-rw-r--r--nuklear/demo/gdip/nuklear_gdip.h1175
-rw-r--r--nuklear/demo/glfw_opengl2/Makefile25
-rw-r--r--nuklear/demo/glfw_opengl2/main.c182
-rw-r--r--nuklear/demo/glfw_opengl2/nuklear_glfw_gl2.h381
-rw-r--r--nuklear/demo/glfw_opengl3/Makefile26
-rw-r--r--nuklear/demo/glfw_opengl3/main.c200
-rw-r--r--nuklear/demo/glfw_opengl3/nuklear_glfw_gl3.h492
-rw-r--r--nuklear/demo/node_editor.c343
-rw-r--r--nuklear/demo/overview.c1249
-rw-r--r--nuklear/demo/sdl_opengl2/Makefile25
-rw-r--r--nuklear/demo/sdl_opengl2/main.c198
-rw-r--r--nuklear/demo/sdl_opengl2/nuklear_sdl_gl2.h346
-rw-r--r--nuklear/demo/sdl_opengl3/Makefile25
-rw-r--r--nuklear/demo/sdl_opengl3/main.c208
-rw-r--r--nuklear/demo/sdl_opengl3/nuklear_sdl_gl3.h442
-rw-r--r--nuklear/demo/sdl_opengles2/Makefile25
-rw-r--r--nuklear/demo/sdl_opengles2/main.c191
-rw-r--r--nuklear/demo/sdl_opengles2/nuklear_sdl_gles2.h443
-rw-r--r--nuklear/demo/sfml_opengl2/Makefile33
-rw-r--r--nuklear/demo/sfml_opengl2/Readme.md9
-rw-r--r--nuklear/demo/sfml_opengl2/main.cpp181
-rw-r--r--nuklear/demo/sfml_opengl2/nuklear_sfml_gl2.h359
-rw-r--r--nuklear/demo/sfml_opengl3/Makefile37
-rw-r--r--nuklear/demo/sfml_opengl3/Readme.md11
-rw-r--r--nuklear/demo/sfml_opengl3/main.cpp189
-rw-r--r--nuklear/demo/sfml_opengl3/nuklear_sfml_gl3.h463
-rw-r--r--nuklear/demo/style.c132
-rw-r--r--nuklear/demo/x11/Makefile13
-rw-r--r--nuklear/demo/x11/main.c224
-rw-r--r--nuklear/demo/x11/nuklear_xlib.h957
-rw-r--r--nuklear/demo/x11_opengl2/Makefile26
-rw-r--r--nuklear/demo/x11_opengl2/main.c345
-rw-r--r--nuklear/demo/x11_opengl2/nuklear_xlib_gl2.h376
-rw-r--r--nuklear/demo/x11_opengl3/Makefile26
-rw-r--r--nuklear/demo/x11_opengl3/main.c342
-rw-r--r--nuklear/demo/x11_opengl3/nuklear_xlib_gl3.h743
-rw-r--r--nuklear/demo/x11_rawfb/Makefile13
-rw-r--r--nuklear/demo/x11_rawfb/main.c263
-rw-r--r--nuklear/demo/x11_rawfb/nuklear_rawfb.h986
-rw-r--r--nuklear/demo/x11_rawfb/nuklear_xlib.h283
-rw-r--r--nuklear/doc/Makefile24
-rwxr-xr-xnuklear/doc/build.sh4
-rw-r--r--nuklear/doc/nuklear.html2533
-rw-r--r--nuklear/doc/stddoc.c141
-rw-r--r--nuklear/example/Makefile42
-rw-r--r--nuklear/example/canvas.c489
-rw-r--r--nuklear/example/extended.c906
-rw-r--r--nuklear/example/file_browser.c910
-rw-r--r--nuklear/example/icon/checked.pngbin0 -> 1813 bytes
-rw-r--r--nuklear/example/icon/cloud.pngbin0 -> 7509 bytes
-rw-r--r--nuklear/example/icon/computer.pngbin0 -> 620 bytes
-rw-r--r--nuklear/example/icon/copy.pngbin0 -> 655 bytes
-rw-r--r--nuklear/example/icon/default.pngbin0 -> 460 bytes
-rw-r--r--nuklear/example/icon/delete.pngbin0 -> 11040 bytes
-rw-r--r--nuklear/example/icon/desktop.pngbin0 -> 583 bytes
-rw-r--r--nuklear/example/icon/directory.pngbin0 -> 533 bytes
-rw-r--r--nuklear/example/icon/edit.pngbin0 -> 14998 bytes
-rw-r--r--nuklear/example/icon/export.pngbin0 -> 13336 bytes
-rw-r--r--nuklear/example/icon/font.pngbin0 -> 561 bytes
-rw-r--r--nuklear/example/icon/home.pngbin0 -> 819 bytes
-rw-r--r--nuklear/example/icon/img.pngbin0 -> 648 bytes
-rw-r--r--nuklear/example/icon/movie.pngbin0 -> 626 bytes
-rw-r--r--nuklear/example/icon/music.pngbin0 -> 610 bytes
-rw-r--r--nuklear/example/icon/next.pngbin0 -> 703 bytes
-rw-r--r--nuklear/example/icon/pause.pngbin0 -> 1338 bytes
-rw-r--r--nuklear/example/icon/pen.pngbin0 -> 5949 bytes
-rw-r--r--nuklear/example/icon/phone.pngbin0 -> 15778 bytes
-rw-r--r--nuklear/example/icon/plane.pngbin0 -> 13546 bytes
-rw-r--r--nuklear/example/icon/play.pngbin0 -> 566 bytes
-rw-r--r--nuklear/example/icon/prev.pngbin0 -> 701 bytes
-rw-r--r--nuklear/example/icon/rocket.pngbin0 -> 1121 bytes
-rw-r--r--nuklear/example/icon/settings.pngbin0 -> 15671 bytes
-rw-r--r--nuklear/example/icon/stop.pngbin0 -> 520 bytes
-rw-r--r--nuklear/example/icon/text.pngbin0 -> 601 bytes
-rw-r--r--nuklear/example/icon/tools.pngbin0 -> 24483 bytes
-rw-r--r--nuklear/example/icon/unchecked.pngbin0 -> 1044 bytes
-rw-r--r--nuklear/example/icon/volume.pngbin0 -> 25438 bytes
-rw-r--r--nuklear/example/icon/wifi.pngbin0 -> 18857 bytes
-rw-r--r--nuklear/example/images/image1.pngbin0 -> 42882 bytes
-rw-r--r--nuklear/example/images/image2.pngbin0 -> 5671 bytes
-rw-r--r--nuklear/example/images/image3.pngbin0 -> 131502 bytes
-rw-r--r--nuklear/example/images/image4.pngbin0 -> 185821 bytes
-rw-r--r--nuklear/example/images/image5.pngbin0 -> 98475 bytes
-rw-r--r--nuklear/example/images/image6.pngbin0 -> 35633 bytes
-rw-r--r--nuklear/example/images/image7.pngbin0 -> 13960 bytes
-rw-r--r--nuklear/example/images/image8.pngbin0 -> 45987 bytes
-rw-r--r--nuklear/example/images/image9.pngbin0 -> 30759 bytes
-rw-r--r--nuklear/example/skinning.c824
-rw-r--r--nuklear/example/skins/gwen.pngbin0 -> 24565 bytes
-rw-r--r--nuklear/example/stb_image.h6509
-rw-r--r--nuklear/extra_font/Cousine-Regular.ttfbin0 -> 43912 bytes
-rw-r--r--nuklear/extra_font/DroidSans.ttfbin0 -> 190044 bytes
-rw-r--r--nuklear/extra_font/Karla-Regular.ttfbin0 -> 16848 bytes
-rw-r--r--nuklear/extra_font/ProggyClean.ttfbin0 -> 41208 bytes
-rw-r--r--nuklear/extra_font/ProggyTiny.ttfbin0 -> 35656 bytes
-rw-r--r--nuklear/extra_font/Raleway-Bold.ttfbin0 -> 176280 bytes
-rw-r--r--nuklear/extra_font/Roboto-Bold.ttfbin0 -> 135820 bytes
-rw-r--r--nuklear/extra_font/Roboto-Light.ttfbin0 -> 140276 bytes
-rw-r--r--nuklear/extra_font/Roboto-Regular.ttfbin0 -> 145348 bytes
-rw-r--r--nuklear/extra_font/kenvector_future.ttfbin0 -> 34136 bytes
-rw-r--r--nuklear/extra_font/kenvector_future_thin.ttfbin0 -> 34100 bytes
-rw-r--r--nuklear/nuklear.h25596
-rw-r--r--nuklear/package.json8
-rw-r--r--osc.lv2/.gitlab-ci.yml62
-rw-r--r--osc.lv2/COPYING201
-rw-r--r--osc.lv2/README.md33
-rw-r--r--osc.lv2/VERSION1
-rw-r--r--osc.lv2/lv2-osc.doap.ttl40
-rw-r--r--osc.lv2/manifest.ttl23
-rw-r--r--osc.lv2/meson.build36
-rw-r--r--osc.lv2/osc.lv2/endian.h120
-rw-r--r--osc.lv2/osc.lv2/forge.h474
-rw-r--r--osc.lv2/osc.lv2/osc.h192
-rw-r--r--osc.lv2/osc.lv2/reader.h571
-rw-r--r--osc.lv2/osc.lv2/stream.h1379
-rw-r--r--osc.lv2/osc.lv2/util.h505
-rw-r--r--osc.lv2/osc.lv2/writer.h579
-rw-r--r--osc.lv2/osc.ttl42
-rw-r--r--osc.lv2/test/osc_test.c968
-rw-r--r--plugin/axa.c335
-rw-r--r--plugin/caxca.c332
-rw-r--r--plugin/cxc.c279
-rw-r--r--plugin/lexer.lua1634
-rw-r--r--plugin/lock_stash.h49
-rw-r--r--plugin/manifest.ttl.in447
-rw-r--r--plugin/moony.c54
-rw-r--r--plugin/moony.lua899
-rw-r--r--plugin/moony.ttl1113
-rw-r--r--plugin/moony_ui.c39
-rw-r--r--plugin/moony_ui.ttl146
-rw-r--r--plugin/nk_ui.c3732
-rw-r--r--plugin/presets.ttl1462
-rw-r--r--plugin/private_ui.h360
-rw-r--r--plugin/simple_ui.c474
-rw-r--r--png/COPYING6
-rw-r--r--png/bell.pngbin0 -> 3254 bytes
-rw-r--r--png/cancel-1.pngbin0 -> 3804 bytes
-rw-r--r--png/cancel.pngbin0 -> 2827 bytes
-rw-r--r--png/checked.pngbin0 -> 3186 bytes
-rw-r--r--png/download.pngbin0 -> 2699 bytes
-rw-r--r--png/envelope.pngbin0 -> 2847 bytes
-rw-r--r--png/house.pngbin0 -> 2914 bytes
-rw-r--r--png/layers.pngbin0 -> 3788 bytes
-rw-r--r--png/menu.pngbin0 -> 2478 bytes
-rw-r--r--png/next.pngbin0 -> 2548 bytes
-rw-r--r--png/pencil.pngbin0 -> 2663 bytes
-rw-r--r--png/plus.pngbin0 -> 2421 bytes
-rw-r--r--png/question.pngbin0 -> 3339 bytes
-rw-r--r--png/reload.pngbin0 -> 3653 bytes
-rw-r--r--png/screen.pngbin0 -> 2506 bytes
-rw-r--r--png/settings.pngbin0 -> 3570 bytes
-rw-r--r--png/sort.pngbin0 -> 2648 bytes
-rw-r--r--png/upload.pngbin0 -> 2716 bytes
-rw-r--r--png/user.pngbin0 -> 3636 bytes
-rw-r--r--pugl/.gitignore3
-rw-r--r--pugl/AUTHORS11
-rw-r--r--pugl/COPYING13
-rw-r--r--pugl/Doxyfile.in2415
-rw-r--r--pugl/README.md28
-rw-r--r--pugl/pugl.pc.in10
-rw-r--r--pugl/pugl/cairo_gl.h106
-rw-r--r--pugl/pugl/gl.h32
-rw-r--r--pugl/pugl/glew.h32
-rw-r--r--pugl/pugl/glu.h32
-rw-r--r--pugl/pugl/pugl.h634
-rw-r--r--pugl/pugl/pugl.hpp108
-rw-r--r--pugl/pugl/pugl_internal.h273
-rw-r--r--pugl/pugl/pugl_osx.m746
-rw-r--r--pugl/pugl/pugl_win.cpp704
-rw-r--r--pugl/pugl/pugl_x11.c810
-rw-r--r--pugl/pugl_cairo_test.c205
-rw-r--r--pugl/pugl_test.c261
-rwxr-xr-xpugl/wafbin0 -> 97489 bytes
-rw-r--r--pugl/wscript203
-rw-r--r--screenshots/screenshot_1.pngbin0 -> 188847 bytes
-rw-r--r--test/moony_manual.lua165
-rw-r--r--test/moony_overflow.lua231
-rw-r--r--test/moony_presets.lua91
-rw-r--r--test/moony_test.c312
-rw-r--r--test/moony_test.lua2324
-rw-r--r--timely.lv2/.gitlab-ci.yml72
-rw-r--r--timely.lv2/COPYING201
-rw-r--r--timely.lv2/README.md20
-rw-r--r--timely.lv2/VERSION1
-rw-r--r--timely.lv2/meson.build66
-rw-r--r--timely.lv2/test/manifest.ttl.in28
-rw-r--r--timely.lv2/test/timely.c218
-rw-r--r--timely.lv2/test/timely.ttl65
-rw-r--r--timely.lv2/timely.h404
-rw-r--r--tiny-AES128-C/Makefile40
-rw-r--r--tiny-AES128-C/README.md65
-rw-r--r--tiny-AES128-C/aes.c564
-rw-r--r--tiny-AES128-C/aes.h59
-rw-r--r--tiny-AES128-C/test.c193
-rw-r--r--tiny-AES128-C/unlicense.txt24
-rw-r--r--tlsf-3.0/Readme.txt102
-rw-r--r--tlsf-3.0/tlsf.c1051
-rw-r--r--tlsf-3.0/tlsf.h68
-rw-r--r--tlsf-3.0/tlsfbits.h180
-rw-r--r--varchunk/.gitlab-ci.yml63
-rw-r--r--varchunk/COPYING201
-rw-r--r--varchunk/README.md112
-rw-r--r--varchunk/VERSION1
-rw-r--r--varchunk/meson.build30
-rw-r--r--varchunk/test_varchunk.c254
-rw-r--r--varchunk/varchunk.h384
-rw-r--r--whitelist.stoat8
-rw-r--r--xpress.lv2/.gitlab-ci.yml69
-rw-r--r--xpress.lv2/COPYING201
-rw-r--r--xpress.lv2/README.md20
-rw-r--r--xpress.lv2/VERSION1
-rw-r--r--xpress.lv2/meson.build81
-rw-r--r--xpress.lv2/test/manifest.ttl.in28
-rw-r--r--xpress.lv2/test/xpress.c285
-rw-r--r--xpress.lv2/test/xpress.ttl82
-rw-r--r--xpress.lv2/test/xpress_test.c206
-rw-r--r--xpress.lv2/xpress.lv2/xpress.h839
397 files changed, 137219 insertions, 2 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..b78b8d6
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,112 @@
+stages:
+ - build
+ - deploy
+
+.variables_template: &variables_definition
+ variables:
+ BASE_NAME: "moony.lv2"
+ PKG_CONFIG_PATH: "/opt/lv2/lib/pkgconfig:/opt/${CI_BUILD_NAME}/lib/pkgconfig:/usr/lib/${CI_BUILD_NAME}/pkgconfig"
+
+.common_template: &common_definition
+ <<: *variables_definition
+ stage: build
+ artifacts:
+ name: "${BASE_NAME}-$(cat VERSION)-${CI_BUILD_NAME}"
+ paths:
+ - "${BASE_NAME}-$(cat VERSION)/"
+
+.build_template: &build_definition
+ <<: *common_definition
+ script:
+ - meson --prefix="/opt/${CI_BUILD_NAME}" --libdir="lib" --cross-file "${CI_BUILD_NAME}" build
+ - sed -i -e '/framework/s/-Wl,-O1//g' -e '/framework/s/-Wl,--start-group//g' -e '/framework/s/-Wl,--end-group//g' -e '/framework/s/-Wl,-soname,.*dylib//g' build/build.ninja
+ - ninja -C build
+ - ninja -C build install
+ - mkdir -p "${BASE_NAME}-$(cat VERSION)/${CI_BUILD_NAME}/${BASE_NAME}"
+ - cp -r "/opt/${CI_BUILD_NAME}/lib/lv2/${BASE_NAME}/" "${BASE_NAME}-$(cat VERSION)/${CI_BUILD_NAME}/"
+
+.test_template: &test_definition
+ <<: *common_definition
+ script:
+ - meson --prefix="/opt/${CI_BUILD_NAME}" --libdir="lib" --cross-file "${CI_BUILD_NAME}" build
+ - sed -i -e '/framework/s/-Wl,-O1//g' -e '/framework/s/-Wl,--start-group//g' -e '/framework/s/-Wl,--end-group//g' -e '/framework/s/-Wl,-soname,.*dylib//g' build/build.ninja
+ - ninja -C build
+ - ninja -C build install
+ - mkdir -p "${BASE_NAME}-$(cat VERSION)/${CI_BUILD_NAME}/${BASE_NAME}"
+ - cp -r "/opt/${CI_BUILD_NAME}/lib/lv2/${BASE_NAME}/" "${BASE_NAME}-$(cat VERSION)/${CI_BUILD_NAME}/"
+ - ninja -C build test
+
+.analyze_template: &analyze_definition
+ <<: *common_definition
+ script:
+ - meson --prefix="/opt/${CI_BUILD_NAME}" --libdir="lib" --cross-file "${CI_BUILD_NAME}" build
+ - sed -i -e '/framework/s/-Wl,-O1//g' -e '/framework/s/-Wl,--start-group//g' -e '/framework/s/-Wl,--end-group//g' -e '/framework/s/-Wl,-soname,.*dylib//g' build/build.ninja
+ - ninja -C build
+ - ninja -C build install
+ - mkdir -p "${BASE_NAME}-$(cat VERSION)/${CI_BUILD_NAME}/${BASE_NAME}"
+ - cp -r "/opt/${CI_BUILD_NAME}/lib/lv2/${BASE_NAME}/" "${BASE_NAME}-$(cat VERSION)/${CI_BUILD_NAME}/"
+ - ninja -C build test
+
+ - CC=clang CXX=clang++ meson --prefix="/opt/${CI_BUILD_NAME}" --libdir="lib" --cross-file "${CI_BUILD_NAME}" clang
+ - ninja -C clang
+ - ninja -C clang test
+
+ - scan-build --status-bugs meson --prefix="/opt/${CI_BUILD_NAME}" --libdir="lib" --cross-file "${CI_BUILD_NAME}" scanbuild
+ - scan-build --status-bugs ninja -C scanbuild
+ - scan-build --status-bugs ninja -C scanbuild test
+
+.universal_linux_template: &universal_linux_definition
+ image: ventosus/universal-linux-gnu
+ <<: *analyze_definition
+
+.arm_linux_template: &arm_linux_definition
+ image: ventosus/arm-linux-gnueabihf
+ <<: *test_definition
+
+.universal_w64_template: &universal_w64_definition
+ image: ventosus/universal-w64-mingw32
+ <<: *test_definition
+
+.universal_apple_template: &universal_apple_definition
+ image: ventosus/universal-apple-darwin
+ <<: *build_definition
+
+# building in docker
+x86_64-linux-gnu:
+ <<: *universal_linux_definition
+
+i686-linux-gnu:
+ <<: *universal_linux_definition
+
+arm-linux-gnueabihf:
+ <<: *arm_linux_definition
+
+x86_64-w64-mingw32:
+ <<: *universal_w64_definition
+
+i686-w64-mingw32:
+ <<: *universal_w64_definition
+
+universal-apple-darwin:
+ <<: *universal_apple_definition
+
+pack:
+ <<: *variables_definition
+ stage: deploy
+ script:
+ - echo 'packing up...'
+ artifacts:
+ name: "${BASE_NAME}-$(cat VERSION)"
+ paths:
+ - "${BASE_NAME}-$(cat VERSION)/"
+
+pages:
+ <<: *variables_definition
+ stage: deploy
+ script:
+ - mkdir -p public
+ - cp "${BASE_NAME}-$(cat VERSION)/x86_64-linux-gnu/${BASE_NAME}/manual.html" public/index.html
+ - cp logo/moony_logo.png public/.
+ artifacts:
+ paths:
+ - public/
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..5965f0f
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,45 @@
+sudo: required
+dist: trusty
+language:
+ - c
+os:
+ - linux
+compiler:
+ - gcc
+ # - clang
+before_install:
+ - wget http://lv2plug.in/spec/lv2-1.12.0.tar.bz2
+ - wget http://download.drobilla.net/serd-0.22.0.tar.bz2
+ - wget http://download.drobilla.net/sord-0.14.0.tar.bz2
+ - wget http://download.drobilla.net/sratom-0.4.6.tar.bz2
+ - wget http://download.drobilla.net/lilv-0.22.0.tar.bz2
+ - wget https://github.com/nanomsg/nanomsg/releases/download/0.8-beta/nanomsg-0.8-beta.tar.gz
+ - wget https://github.com/warmcat/libwebsockets/archive/v1.7.5.tar.gz
+ - tar xjf lv2-1.12.0.tar.bz2
+ - tar xjf serd-0.22.0.tar.bz2
+ - tar xjf sord-0.14.0.tar.bz2
+ - tar xjf sratom-0.4.6.tar.bz2
+ - tar xjf lilv-0.22.0.tar.bz2
+ - tar xzf nanomsg-0.8-beta.tar.gz
+ - tar xzf v1.7.5.tar.gz
+ - if [ "$CC" = "clang" ]; then sudo add-apt-repository -y ppa:h-rayflood/llvm-upper; fi
+ - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
+ - sudo add-apt-repository -y ppa:andykimpe/cmake
+ - sudo apt-get update -qq
+install:
+ - sudo apt-get install xsltproc
+ - if [ "$CC" = "clang" ]; then sudo apt-get install -y clang-3.6 libstdc++-5-dev; fi
+ - if [ "$CC" = "gcc" ]; then sudo apt-get install -y gcc-5 g++-5; fi
+ - pushd lv2-1.12.0 && ./waf configure --no-plugins --prefix=/usr && ./waf build && sudo ./waf install && popd
+ - pushd serd-0.22.0 && ./waf configure --no-utils --prefix=/usr && ./waf build && sudo ./waf install && popd
+ - pushd sord-0.14.0 && ./waf configure --no-utils --prefix=/usr && ./waf build && sudo ./waf install && popd
+ - pushd sratom-0.4.6 && ./waf configure --prefix=/usr && ./waf build && sudo ./waf install && popd
+ - pushd lilv-0.22.0 && ./waf configure --no-utils --prefix=/usr && ./waf build && sudo ./waf install && popd
+ - pushd nanomsg-0.8-beta && ./configure --prefix=/usr && make && sudo make install && popd
+ - pushd libwebsockets-1.7.5 && mkdir build && pushd build && cmake -DCMAKE_INSTALL_PREFIX=/usr .. && make && sudo make install && popd && popd
+before_script:
+ - if [ "$CC" = "clang" ]; then export CXX="clang++-3.6" CC="clang-3.6" CFLAGS="-ffreestanding"; fi
+ - if [ "$CC" = "gcc" ]; then export CXX="g++-5" CC="gcc-5"; fi
+ - mkdir build && pushd build && cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=1 -DBUILD_SIMPLE_UI=1 -DBUILD_WEB_UI=1 .. && popd
+script:
+ - pushd build && make && ARGS="-VV" make test && sudo make install && popd
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..f0533b0
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,39 @@
+# Changelog
+
+## [0.26.0] - 15 Apr 2019
+
+### Added
+
+* use custom environment in code injection example preset
+* global moony:error parameter
+* global moony:code parameter
+* external editor ui
+* supported options to turtle metadata
+* HTML reference manual to plugin bundle
+* button for opening HTML reference manual
+* support for plain atoms as rdf:value in state responder
+* support decoding patch:Put events in state responder
+
+### Changed
+
+* from Cmake to meson build system
+* enable manual garbage collection by default
+* internal Lua registry code cleanup
+* from lv2_atom_object_query to lv2_atom_object_get (GCC bug)
+* from strtok to strsep (the latter is rt-safe)
+* to updated (experimental) canvas extension
+* use strpbrk to match OSC path wildcards
+* always return params:sampleRate from options interface
+* render instructions in presets to be compatible with nanovg backend
+
+### Fixed
+
+* automatic scaling for hiDPI displays
+* initial focus on text edit fields
+* text edit for onon-single line edits
+* log auto-clear upon code send
+* always overwrite error message with latest
+
+### Deprecated
+
+* math.random and math.randomseed (use rt-safe 'random' module instead)
diff --git a/README.md b/README.md
index 06b87f2..bbc4feb 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,210 @@
-# Canvas LV2 plugin extension
+## Moony
+
+### Realtime Lua as programmable glue in LV2
+
+#### Build status
+
+[![build status](https://gitlab.com/OpenMusicKontrollers/moony.lv2/badges/master/build.svg)](https://gitlab.com/OpenMusicKontrollers/moony.lv2/commits/master)
+
+### Binaries
+
+For GNU/Linux (64-bit, 32-bit, armv7), Windows (64-bit, 32-bit) and MacOS
+(64/32-bit univeral).
+
+To install the plugin bundle on your system, simply copy the __moony.lv2__
+folder out of the platform folder of the downloaded package into your
+[LV2 path](http://lv2plug.in/pages/filesystem-hierarchy-standard.html).
+
+#### Stable release
+
+* [moony.lv2-0.22.0.zip](https://dl.open-music-kontrollers.ch/moony.lv2/stable/moony.lv2-0.22.0.zip) ([sig](https://dl.open-music-kontrollers.ch/moony.lv2/stable/moony.lv2-0.22.0.zip.sig))
+
+#### Unstable (nightly) release
+
+* [moony.lv2-latest-unstable.zip](https://dl.open-music-kontrollers.ch/moony.lv2/unstable/moony.lv2-latest-unstable.zip) ([sig](https://dl.open-music-kontrollers.ch/moony.lv2/unstable/moony.lv2-latest-unstable.zip.sig))
+
+### Sources
+
+#### Stable release
+
+* [moony.lv2-0.22.0.tar.xz](https://git.open-music-kontrollers.ch/lv2/moony.lv2/snapshot/moony.lv2-0.22.0.tar.xz)
+
+#### Git repository
+
+* <https://git.open-music-kontrollers.ch/lv2/moony.lv2>
+
+### Packages
+
+* [ArchLinux](https://www.archlinux.org/packages/community/x86_64/moony.lv2/)
+
+### Bugs and feature requests
+
+* [Gitlab](https://gitlab.com/OpenMusicKontrollers/moony.lv2)
+* [Github](https://github.com/OpenMusicKontrollers/moony.lv2)
+
+### General Overview
+
+The Moony plugins come in three flavours, whereby some of them are more and
+others less suitable for linear plugin hosts (e.g. DAWs). All of them are
+suitable for non-linear hosts (NLH), e.g. [Ingen](https://drobilla.net/software/ingen/)
+or [Synthpod](/lv2/synthpod/#).
+
+* Control to control port conversion (NLH)
+* Atom to atom port conversion (DAW, NLH)
+* Control+atom to control+atom port conversion (DAW, NLH)
+
+The design goal of the plugin bundle was to create a tool to easily add
+realtime programmable logic glue in LV2 plugin graphs.
+
+To have plugins which do a specific task efficiently is great, especially
+for audio plugins. LV2 stands apart from other audio plugin specifications
+with its extentable event system based on Atoms. As events can be much more
+varied in nature and represent pretty much anything (NOT ONLY MIDI), it would
+be useful to have a tool to create arbitrary event filters for a given setup
+on-the-fly.
+
+For a given setup, one may need a special event filter only once and it
+seems to be overkill to write a native LV2 event filter in C/C++ just for
+that. It would also be nice to have a tool for fast prototyping
+of new event filters.
+
+A scripting language seems to be ideal for these cases, where the user can
+write an event filter on a higher level of abstraction on-the-fly.
+The scripting language needs to be realtime safe, though, which restricts
+the choices dramatically.
+
+One such scripting language is [Lua](https://www.lua.org). It is
+small, fast, easily embeddable and realtime-safe if coupled to a realtime-safe
+memory allocator like [TLSF](http://www.gii.upv.es/tlsf/).
+
+The Moony plugins can handle LV2 control and atom event ports, only. They do not
+handle LV2 audio ports. They may eventually handle LV2 control-voltage ports
+in the future, though. Control port values are internally
+handled as simple floating point numbers, whereas the atom event ports
+build on top of the LV2 atom and atom forge C headers.
+
+The control port plugins are simple to script and need only low level
+programming skills.
+
+The atom event port plugins are more complex. You may want to first understand
+the underlying concepts of LV2 atom and atom forge in the official resources:
+
+* <http://lv2plug.in/ns/ext/atom/>
+* <http://lv2plug.in/doc/html/group__atom.html>
+* <http://lv2plug.in/doc/html/group__forge.html>
+
+### API
+
+_Note: The Moony Lua API is not stable yet, expect changes to happen until
+an official release._ Preview the manual here:
+
+<https://openmusickontrollers.gitlab.io/moony.lv2>
+
+### Plugins
+
+![Screenshot](https://git.open-music-kontrollers.ch/lv2/moony.lv2/plain/screenshots/screenshot_1.png)
+
+#### Control to control port
+
+There are different plugins which feature different numbers of input and
+output ports. These plugins are simple to use: Define a __run__ function
+which receives the right number of input values and returns the desired
+output values. Missing output values will be set to 0.0 automatically.
+
+##### C1 x C1
+
+1x control input to 1x control output.
+
+##### C2 x C2
+
+2x control input to 2x control output.
+
+##### C4 x C4
+
+4x control input to 4x control output.
+
+#### Atom to atom port
+
+All atom containers (sequence, object, tuple, vector) implement a
+__foreach__ method to be iterated over with Lua's __for__. The number
+of children in each container is returned with Lua's length operator __#__.
+Child elements can also be queried individually with an integer key
+representing position (sequence, tuple, vector) or URID (object).
+
+With an atom sequence as output, the plugins use the atom forge infrastructure
+underneath. Each event added to the sequence consists of a frame time and
+and a given atom type. The Lua atom forge API closely follows the C API.
+
+##### A1 x A1
+
+1x atom input to 1x atom output.
+
+##### A2 x A2
+
+2x atom input to 2x atom output.
+
+##### A4 x A4
+
+4x atom input to 4x atom output.
+
+#### Control+atom to control+atom port
+
+And if you need both atom input/output and control input/output, then there
+is this here:
+
+##### C1+A1 x C1+A1
+
+1x control + 1x atom input to 1x control + 1x atom output.
+
+##### C2+A1 x C2+A1
+
+2x control + 1x atom input to 2x control + 1x atom output.
+
+##### C4+A1 x C4+A1
+
+4x control + 1x atom input to 4x control + 1x atom output.
+
+### Dependencies
+
+* [LV2](http://lv2plug.in) (LV2 Plugin Standard)
+
+### Build / install
+
+ git clone https://git.open-music-kontrollers.ch/lv2/moony.lv2
+ cd moony.lv2
+ meson -Dbuild-zimple-ui=true build
+ cd build
+ ninja -j4
+ sudo ninja install
+ ninja test
+
+### GUI
+
+This plugin features an external LV2 plugin GUI, which does nothing else than
+just opening the plugin's Lua source in your favorite editor and monitor its
+modification state.
+
+*The external GUI is optionally built only.*
+
+Currently, the editor has to be defined via an environment variable. You can
+use either the environment varialbe *EDITOR* or *MOONY_EDITOR*, whereby the
+latter will take precedence over the former.
+
+ export EDITOR='urxvt -e nvim'
+
+If no environment variable is defined, the default fallback invocation commands
+are defined as follows:
+
+* 'xterm -e vi' (Unix)
+* 'open -nW' (MacOS)
+* 'cmd /c start /wait' (Windows)
+
+Whenever you save the Lua source, the plugin will try to just-in-time compile and
+inject it. Potential warnings and errors are reported in the plugin host's log.
### License
-Copyright (c) 2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+Copyright (c) 2015-2017 Hanspeter Portner (dev@open-music-kontrollers.ch)
This is free software: you can redistribute it and/or modify
it under the terms of the Artistic License 2.0 as published by
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..55bb926
--- /dev/null
+++ b/TODO
@@ -0,0 +1,7 @@
+=== Low priority
+* api: xpress
+* api: CanvasResponder
+
+=== High priority
+* properly catch forge errors in 'notify'
+* return matched state in MIDIResponder
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..9fa4a93
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.25.393
diff --git a/api/api.c b/api/api.c
new file mode 100644
index 0000000..7fa34ed
--- /dev/null
+++ b/api/api.c
@@ -0,0 +1,2506 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <limits.h> // INT_MAX
+#include <math.h> // INFINITY
+#include <inttypes.h>
+#include <stdatomic.h>
+
+#include <osc.lv2/endian.h>
+#include <xpress.lv2/xpress.h>
+
+#include <api_atom.h>
+#include <api_forge.h>
+#include <api_stash.h>
+#include <api_midi.h>
+#include <api_osc.h>
+#include <api_time.h>
+#include <api_state.h>
+#include <api_parameter.h>
+
+#define RDF_PREFIX "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+#define RDFS_PREFIX "http://www.w3.org/2000/01/rdf-schema#"
+
+#define RDF__value RDF_PREFIX"value"
+#define RDF__type RDF_PREFIX"type"
+#define RDFS__label RDFS_PREFIX"label"
+#define RDFS__range RDFS_PREFIX"range"
+#define RDFS__comment RDFS_PREFIX"comment"
+
+#ifndef LV2_PATCH__Copy
+# define LV2_PATCH__Copy LV2_PATCH_PREFIX "Copy"
+#endif
+
+#ifndef LV2_PATCH__Insert
+# define LV2_PATCH__Insert LV2_PATCH_PREFIX "Insert"
+#endif
+
+#ifndef LV2_PATCH__accept
+# define LV2_PATCH__accept LV2_PATCH_PREFIX "accept"
+#endif
+
+#ifndef LV2_PATCH__context
+# define LV2_PATCH__context LV2_PATCH_PREFIX "context"
+#endif
+
+#ifndef LV2_UNITS__midiController
+# define LV2_UNITS__midiController LV2_UNITS_PREFIX "midiController"
+#endif
+
+static const char *moony_ref [MOONY_UDATA_COUNT] = {
+ [MOONY_UDATA_ATOM] = "latom",
+ [MOONY_UDATA_FORGE] = "lforge",
+ [MOONY_UDATA_STASH] = "lstash"
+};
+
+static const size_t moony_sz [MOONY_UDATA_COUNT] = {
+ [MOONY_UDATA_ATOM] = sizeof(latom_t),
+ [MOONY_UDATA_FORGE] = sizeof(lforge_t),
+ [MOONY_UDATA_STASH] = sizeof(lstash_t)
+};
+
+__non_realtime static int
+_hash_sort(const void *itm1, const void *itm2)
+{
+ const latom_driver_hash_t *hash1 = itm1;
+ const latom_driver_hash_t *hash2 = itm2;
+
+ if(hash1->type < hash2->type)
+ return -1;
+ else if(hash1->type > hash2->type)
+ return 1;
+ return 0;
+}
+
+__realtime static int // map is not really realtime in most hosts
+_lmap__index(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ const char *uri = luaL_checkstring(L, 2);
+ LV2_URID urid = moony->map->map(moony->map->handle, uri); // non-rt
+ if(urid)
+ {
+ lua_pushinteger(L, urid);
+
+ // cache it
+ lua_pushvalue(L, 2); // uri
+ lua_pushvalue(L, -2); // urid
+ lua_rawset(L, 1); // self
+ }
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static const luaL_Reg lmap_mt [] = {
+ {"__index", _lmap__index},
+ {"__call", _lmap__index},
+ {NULL, NULL}
+};
+
+__realtime static int // unmap is not really realtime in most hosts
+_lunmap__index(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ LV2_URID urid = luaL_checkinteger(L, 2);
+ const char *uri = moony->unmap->unmap(moony->unmap->handle, urid); // non-rt
+ if(uri)
+ {
+ lua_pushstring(L, uri);
+
+ // cache it
+ lua_pushvalue(L, 2); // urid
+ lua_pushvalue(L, -2); // uri
+ lua_rawset(L, 1); // self
+ }
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static const luaL_Reg lunmap_mt [] = {
+ {"__index", _lunmap__index},
+ {"__call", _lunmap__index},
+ {NULL, NULL}
+};
+
+__realtime static int // map is not really realtime in most hosts
+_lmapper__index(lua_State *L)
+{
+ //moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ if(lua_isstring(L, 2))
+ {
+ lua_getglobal(L, "Map");
+ lua_pushvalue(L, lua_upvalueindex(2)); // uri.prefix
+ lua_pushvalue(L, 2); // uri.postfix
+ lua_concat(L, 2); // uri
+ lua_gettable(L, -2); // Map[uri];
+ if(lua_isinteger(L, -1))
+ {
+ // cache it
+ lua_pushvalue(L, 2); // uri
+ lua_pushvalue(L, -2); // urid
+ lua_rawset(L, 1); // self
+ }
+ else
+ lua_pushnil(L);
+ }
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static const luaL_Reg lhash_map_mt [] = {
+ {"__index", _lmapper__index},
+ {"__call", _lmapper__index},
+ {NULL, NULL}
+};
+
+__realtime static int // map is not really realtime in most hosts
+_lmapper(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ if(lua_isstring(L, 1))
+ {
+ lua_newtable(L);
+ lua_newtable(L);
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ lua_pushvalue(L, 1); // uri.prefix @ upvalueindex 2
+ luaL_setfuncs(L, lhash_map_mt, 2);
+ //_protect_metatable(L, -1); //TODO
+ lua_setmetatable(L, -2);
+ }
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+__realtime static int
+_lvoice_map(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ lua_pushinteger(L, xpress_map(&moony->xpress));
+ return 1;
+}
+
+__realtime static int
+_lmidi2cps(lua_State *L)
+{
+ const lua_Number note = luaL_checknumber(L, 1);
+ const lua_Number base = luaL_optnumber(L, 2, 69.0);
+ const lua_Number noct = luaL_optnumber(L, 3, 12.0);
+ const lua_Number fref = luaL_optnumber(L, 4, 440.0);
+
+ const lua_Number cps = exp2( (note - base) / noct) * fref;
+
+ lua_pushnumber(L, cps);
+ return 1;
+}
+
+__realtime static int
+_lcps2midi(lua_State *L)
+{
+ const lua_Number cps = luaL_checknumber(L, 1);
+ const lua_Number base = luaL_optnumber(L, 2, 69.0);
+ const lua_Number noct = luaL_optnumber(L, 3, 12.0);
+ const lua_Number fref = luaL_optnumber(L, 4, 440.0);
+
+ const lua_Number note = log2(cps / fref) * noct + base;
+
+ lua_pushnumber(L, note);
+ return 1;
+}
+
+static const char *note_keys [12] = {
+ "C", "C#",
+ "D", "D#",
+ "E",
+ "F", "F#",
+ "G", "G#",
+ "A", "A#",
+ "B"
+};
+
+__realtime static int
+_lnote__index(lua_State *L)
+{
+ //moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ lua_settop(L, 2); // ignore superfluous arguments
+
+ const int type = lua_type(L, 2);
+ if(type == LUA_TNUMBER)
+ {
+ const int note = lua_tointeger(L, 2);
+
+ if( (note >= 0) && (note < 0x80) )
+ {
+ char name [16];
+ const int8_t octave = note / 12 - 1;
+ const uint8_t key = note % 12;
+ snprintf(name, 16, "%s%+"PRIi8, note_keys[key], octave);
+
+ lua_pushstring(L, name);
+ return 1;
+ }
+ }
+ else if(type == LUA_TSTRING)
+ {
+ size_t str_len;
+ const char *str = lua_tolstring(L, 2, &str_len);
+
+ for(int i=0; i<12; i++)
+ {
+ const char *key = note_keys[i];
+ const size_t key_len = strlen(key);
+
+ if( (str_len - 2 == key_len) && !strncmp(str, key, key_len) )
+ {
+ const int octave = atoi(str + key_len);
+ const int note = (octave + 1)*12 + i;
+ if( (note >= 0) && (note < 0x80) )
+ {
+ lua_pushinteger(L, note);
+ return 1;
+ }
+ }
+ }
+ }
+
+ lua_pushnil(L);
+ return 1;
+}
+
+static const luaL_Reg lnote_mt [] = {
+ {"__index", _lnote__index},
+ {"__call", _lnote__index},
+ {NULL, NULL}
+};
+
+__realtime static int
+_lopts__index(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ const LV2_URID key = luaL_checkinteger(L, 2);
+
+ if(moony->opts)
+ {
+ for(LV2_Options_Option *opt = moony->opts;
+ (opt->key != 0) && (opt->value != NULL);
+ opt++)
+ {
+ if(opt->key == key)
+ {
+ const LV2_Atom *atom = (const LV2_Atom *)&opt->size;
+ _latom_body_new(L, atom, opt->value, false);
+ return 1;
+ }
+ }
+ }
+
+ if(key == moony->uris.param_sampleRate)
+ {
+ _latom_body_new(L, &moony->sample_rate.atom, &moony->sample_rate.body, false);
+ return 1;
+ }
+
+ lua_pushnil(L); // not found
+ return 1;
+}
+
+__realtime static int
+_lopts_itr(lua_State *L)
+{
+ LV2_Options_Option **opt_ptr = lua_touserdata(L, lua_upvalueindex(1));
+ LV2_Options_Option *opt = *opt_ptr;
+
+ if( (opt->key != 0) && (opt->value != NULL) )
+ {
+ const LV2_Atom *atom = (const LV2_Atom *)&opt->size;
+ lua_pushinteger(L, opt->key);
+ _latom_body_new(L, atom, opt->value, false);
+
+ *opt_ptr = opt + 1;
+ return 2;
+ }
+
+ //FIXME invalidate atom after use?
+ lua_pushnil(L);
+ return 1;
+}
+
+__realtime static int
+_lopts__pairs(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ if(moony->opts)
+ {
+ LV2_Options_Option **opt_ptr = lua_newuserdata(L, sizeof(LV2_Options_Option **));
+ *opt_ptr = moony->opts;
+ lua_pushcclosure(L, _lopts_itr, 1);
+ return 1;
+ }
+
+ lua_pushnil(L);
+ return 1;
+}
+
+static const luaL_Reg lopts_mt [] = {
+ {"__index", _lopts__index},
+ {"__call", _lopts__index},
+ {"__pairs", _lopts__pairs},
+ {NULL, NULL}
+};
+
+__realtime static int
+_log(lua_State *L)
+{
+ int n = lua_gettop(L);
+
+ if(!n)
+ return 0;
+
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ moony_vm_t *vm = lua_touserdata(L, lua_upvalueindex(2));
+
+ luaL_Buffer buf;
+ luaL_buffinit(L, &buf);
+
+ lua_getglobal(L, "tostring"); //TODO cache this
+ for(int i=1; i<=n; i++)
+ {
+ if(i>1)
+ luaL_addchar(&buf, '\t');
+
+ if(lua_type(L, i) == LUA_TSTRING)
+ {
+ const size_t max_len = 512;
+ size_t len;
+ const char *s = lua_tolstring(L, i, &len);
+ if(len > max_len) // truncate if string too long
+ {
+ luaL_addlstring(&buf, s, max_len);
+ const size_t trunc_len = 32;
+ char trunc [trunc_len];
+ snprintf(trunc, trunc_len, " [+%zu chars]", len - max_len);
+ luaL_addstring(&buf, trunc);
+ }
+ else // use whole string
+ {
+ luaL_addlstring(&buf, s, len);
+ }
+ }
+ else // !LUA-TSTRING
+ {
+ lua_pushvalue(L, -1); // function to be called
+ lua_pushvalue(L, i); // value to print
+ lua_call(L, 1, 1);
+
+ size_t len;
+ const char *s = lua_tolstring(L, -1, &len);
+ luaL_addlstring(&buf, s, len);
+ lua_pop(L, 1); // pop result
+ }
+ }
+
+ luaL_pushresult(&buf);
+
+ size_t len;
+ const char *res = lua_tolstring(L, -1, &len);
+
+ if(moony->log)
+ {
+ if(vm->nrt) // we're running in worker thread
+ lv2_log_note(&moony->logger, "%s\n", res);
+ else // we're running in rt-thread
+ lv2_log_trace(&moony->logger, "%s\n", res);
+ }
+
+ // feedback to UI
+ if(!vm->trace_overflow)
+ {
+ const size_t sz = strlen(vm->trace);
+ if(sz + len + 2 < MOONY_MAX_TRACE_LEN)
+ {
+ char *end = vm->trace + sz; // end of string
+ snprintf(end, len + 2, "%s\n", res);
+ vm->trace_out = true; // set flag
+ }
+ else
+ vm->trace_overflow = true;
+ }
+
+ return 0;
+}
+
+__realtime LV2_Atom_Forge_Ref
+_sink_rt(LV2_Atom_Forge_Sink_Handle handle, const void *buf, uint32_t size)
+{
+ atom_ser_t *ser = handle;
+
+ const LV2_Atom_Forge_Ref ref = ser->offset + 1;
+
+ const uint32_t new_offset = ser->offset + size;
+ if(new_offset > ser->size)
+ {
+ uint32_t new_size = ser->size << 1;
+ while(new_offset > new_size)
+ new_size <<= 1;
+
+ assert(ser->data);
+ if(!(ser->buf = moony_rt_realloc(ser->data, ser->buf, ser->size, new_size)))
+ return 0; // realloc failed
+
+ ser->size = new_size;
+ }
+
+ memcpy(ser->buf + ser->offset, buf, size);
+ ser->offset = new_offset;
+
+ return ref;
+}
+
+__non_realtime LV2_Atom_Forge_Ref
+_sink_non_rt(LV2_Atom_Forge_Sink_Handle handle, const void *buf, uint32_t size)
+{
+ atom_ser_t *ser = handle;
+
+ const LV2_Atom_Forge_Ref ref = ser->offset + 1;
+
+ const uint32_t new_offset = ser->offset + size;
+ if(new_offset > ser->size)
+ {
+ uint32_t new_size = ser->size << 1;
+ while(new_offset > new_size)
+ new_size <<= 1;
+
+ if(!(ser->buf = realloc(ser->buf, new_size)))
+ return 0; // realloc failed
+
+ ser->size = new_size;
+ }
+
+ memcpy(ser->buf + ser->offset, buf, size);
+ ser->offset = new_offset;
+
+ return ref;
+}
+
+__realtime LV2_Atom *
+_deref(LV2_Atom_Forge_Sink_Handle handle, LV2_Atom_Forge_Ref ref)
+{
+ atom_ser_t *ser = handle;
+
+ const uint32_t offset = ref - 1;
+
+ return (LV2_Atom *)(ser->buf + offset);
+}
+
+__realtime static int
+_stash(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ lua_getglobal(L, "stash");
+ if(lua_isfunction(L, -1))
+ {
+ lforge_t *lframe = moony_newuserdata(L, moony, MOONY_UDATA_FORGE, true);
+ lframe->depth = 0;
+ lframe->last.frames = 0;
+ lframe->forge = &moony->stash_forge;
+
+ atom_ser_t *ser = &moony->vm->ser;
+ ser->data = moony->vm;
+ ser->size = 1024;
+ ser->offset = 0;
+ ser->buf = moony_rt_alloc(moony->vm, ser->size);
+
+ if(ser->buf)
+ {
+ memset(ser->buf, 0x0, sizeof(LV2_Atom));
+
+ lv2_atom_forge_set_sink(lframe->forge, _sink_rt, _deref, ser);
+ lua_call(L, 1, 0);
+
+ LV2_Atom *atom = (LV2_Atom *)ser->buf;
+ moony->stash_atom = atom;
+ moony->stash_size = ser->size;
+
+ // invalidate ser_atom
+ ser->size = 0;
+ ser->buf = NULL;
+ }
+ }
+ else
+ lua_pop(L, 1);
+
+ return 0;
+}
+
+__realtime static int
+_apply(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ lua_getglobal(L, "apply");
+ if(lua_isfunction(L, -1))
+ {
+ _latom_new(L, moony->stash_atom, true);
+ lua_call(L, 1, 0);
+ }
+ else
+ lua_pop(L, 1);
+
+ return 0;
+}
+
+__non_realtime static int
+_save(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ if(lua_getglobal(L, "save") == LUA_TFUNCTION)
+ {
+ lforge_t *lframe = moony_newuserdata(L, moony, MOONY_UDATA_FORGE, true);
+ lframe->depth = 0;
+ lframe->last.frames = 0;
+ lframe->forge = &moony->state_forge;
+
+ lua_call(L, 1, 0);
+ }
+
+ return 0;
+}
+
+__non_realtime static int
+_restore(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ if(lua_getglobal(L, "restore") == LUA_TFUNCTION)
+ {
+ _latom_new(L, moony->state_atom, false);
+ lua_call(L, 1, 0);
+ }
+
+ return 0;
+}
+
+__non_realtime static LV2_State_Status
+_state_save(LV2_Handle instance,
+ LV2_State_Store_Function store, LV2_State_Handle state,
+ uint32_t flags, const LV2_Feature *const *features)
+{
+ moony_t *moony = (moony_t *)instance;
+
+ LV2_State_Status status = LV2_STATE_SUCCESS;
+
+ if(moony->chunk_nrt)
+ {
+ status = store(
+ state,
+ moony->uris.moony_code,
+ moony->chunk_nrt,
+ strlen(moony->chunk_nrt) + 1,
+ moony->forge.String,
+ LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
+ (void)status; //TODO check status
+ }
+
+ const int32_t minor_version = MOONY_MINOR_VERSION;
+ status = store(
+ state,
+ moony->uris.lv2_minor_version,
+ &minor_version,
+ sizeof(int32_t),
+ moony->forge.Int,
+ LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
+ (void)status; //TODO check status
+
+ const int32_t micro_version = MOONY_MICRO_VERSION;
+ status = store(
+ state,
+ moony->uris.lv2_micro_version,
+ &micro_version,
+ sizeof(int32_t),
+ moony->forge.Int,
+ LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
+ (void)status; //TODO check status
+
+ int32_t i32 = atomic_load_explicit(&moony->editor_hidden, memory_order_acquire);
+ status = store(
+ state,
+ moony->uris.moony_editorHidden,
+ &i32,
+ sizeof(int32_t),
+ moony->forge.Bool,
+ LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
+ (void)status; //TODO check status
+
+ i32 = atomic_load_explicit(&moony->log_hidden, memory_order_acquire);
+ status = store(
+ state,
+ moony->uris.moony_logHidden,
+ &i32,
+ sizeof(int32_t),
+ moony->forge.Bool,
+ LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
+ (void)status; //TODO check status
+
+ i32 = atomic_load_explicit(&moony->log_follow, memory_order_acquire);
+ status = store(
+ state,
+ moony->uris.moony_logFollow,
+ &i32,
+ sizeof(int32_t),
+ moony->forge.Bool,
+ LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
+ (void)status; //TODO check status
+
+ i32 = atomic_load_explicit(&moony->log_reset, memory_order_acquire);
+ status = store(
+ state,
+ moony->uris.moony_logReset,
+ &i32,
+ sizeof(int32_t),
+ moony->forge.Bool,
+ LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
+ (void)status; //TODO check status
+
+ i32 = atomic_load_explicit(&moony->param_hidden, memory_order_acquire);
+ status = store(
+ state,
+ moony->uris.moony_paramHidden,
+ &i32,
+ sizeof(int32_t),
+ moony->forge.Bool,
+ LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
+ (void)status; //TODO check status
+
+ i32 = atomic_load_explicit(&moony->param_cols, memory_order_acquire);
+ status = store(
+ state,
+ moony->uris.moony_paramCols,
+ &i32,
+ sizeof(int32_t),
+ moony->forge.Int,
+ LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
+ (void)status; //TODO check status
+
+ i32 = atomic_load_explicit(&moony->param_rows, memory_order_acquire);
+ status = store(
+ state,
+ moony->uris.moony_paramRows,
+ &i32,
+ sizeof(int32_t),
+ moony->forge.Int,
+ LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
+ (void)status; //TODO check status
+
+ atom_ser_t ser = {
+ .data = NULL,
+ .size = 1024,
+ .offset = 0
+ };
+ ser.buf = malloc(ser.size);
+
+ if(ser.buf)
+ {
+ memset(ser.buf, 0x0, sizeof(LV2_Atom));
+
+ lv2_atom_forge_set_sink(&moony->state_forge, _sink_non_rt, _deref, &ser);
+
+ // lock Lua state, so it cannot be accessed by realtime thread
+ _spin_lock(&moony->state_lock);
+ {
+ // restore Lua defined properties
+ lua_State *L = moony_current(moony);
+ lua_rawgetp(L, LUA_REGISTRYINDEX, _save);
+ if(lua_pcall(L, 0, 0, 0))
+ {
+ moony_err_async(moony, lua_tostring(L, -1));
+ lua_pop(L, 1);
+ }
+#ifdef USE_MANUAL_GC
+ lua_gc(L, LUA_GCSTEP, 0);
+#endif
+ }
+ _unlock(&moony->state_lock);
+
+ LV2_Atom *state_atom_new = (LV2_Atom *)ser.buf;
+ if( (state_atom_new->type) && (state_atom_new->size) )
+ {
+ status = store(
+ state,
+ moony->uris.moony_state,
+ LV2_ATOM_BODY(state_atom_new),
+ state_atom_new->size,
+ state_atom_new->type,
+ LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
+ }
+
+ LV2_Atom *state_atom_old = (LV2_Atom *)atomic_exchange_explicit(&moony->state_atom_new, (uintptr_t)state_atom_new, memory_order_relaxed);
+ if(state_atom_old)
+ free(state_atom_old);
+ }
+
+ return status;
+}
+
+__non_realtime static moony_vm_t *
+_compile(moony_t *moony, const char *chunk)
+{
+ // save a copy for _state_save
+ if(moony->chunk_nrt)
+ free(moony->chunk_nrt);
+ moony->chunk_nrt = strdup(chunk);
+ if(!moony->chunk_nrt)
+ return NULL;
+
+ char *chunk_new = strdup(chunk);
+ if(!chunk_new)
+ return NULL;
+
+ // chunk is always updated, even if compilation should fail
+ char *chunk_old = (char *)atomic_exchange_explicit(&moony->chunk_new, (uintptr_t)chunk_new, memory_order_relaxed);
+ if(chunk_old)
+ free(chunk_old);
+
+ moony_vm_t *vm = moony_vm_new(moony->mem_size, moony->testing, moony);
+ if(!vm)
+ {
+ moony_err_async(moony, "moony_vm_new failed");
+ return NULL;
+ }
+
+ moony_vm_nrt_enter(vm);
+ moony_open(moony, vm, vm->L);
+ if(luaL_dostring(vm->L, chunk))
+ {
+ moony_err_async(moony, lua_tostring(vm->L, -1));
+ lua_pop(vm->L, 1);
+
+ moony_vm_free(vm);
+ return NULL;
+ }
+ moony_vm_nrt_leave(vm);
+
+ return vm;
+}
+
+__non_realtime static LV2_State_Status
+_state_restore(LV2_Handle instance,
+ LV2_State_Retrieve_Function retrieve, LV2_State_Handle state,
+ uint32_t flags, const LV2_Feature *const *features)
+{
+ moony_t *moony = (moony_t *)instance;
+ const LV2_Worker_Schedule *work_sched = NULL;
+
+ for(unsigned i = 0; features[i]; i++)
+ {
+ if(!strcmp(features[i]->URI, LV2_WORKER__schedule))
+ work_sched = features[i]->data;
+ }
+
+ (void)work_sched; //FIXME
+
+ size_t size;
+ uint32_t type;
+ uint32_t flags2;
+
+ // get minor version
+ const int32_t *minor_version = retrieve(
+ state,
+ moony->uris.lv2_minor_version,
+ &size,
+ &type,
+ &flags2);
+ if(!minor_version || (size != sizeof(int32_t)) || (type != moony->forge.Int) )
+ minor_version = NULL;
+
+ // get micro version
+ const int32_t *micro_version = retrieve(
+ state,
+ moony->uris.lv2_micro_version,
+ &size,
+ &type,
+ &flags2);
+ if(!micro_version || (size != sizeof(int32_t)) || (type != moony->forge.Int) )
+ micro_version = NULL;
+
+ //TODO check preset version with current plugin version for API compatibility
+
+ // get moony:editorHidden
+ const int32_t *i32 = retrieve(
+ state,
+ moony->uris.moony_editorHidden,
+ &size,
+ &type,
+ &flags2);
+ if(i32 && (size == sizeof(int32_t)) && (type == moony->forge.Bool) )
+ {
+ atomic_store_explicit(&moony->editor_hidden, *i32, memory_order_release);
+ }
+
+ // get moony:logHidden
+ i32 = retrieve(
+ state,
+ moony->uris.moony_logHidden,
+ &size,
+ &type,
+ &flags2);
+ if(i32 && (size == sizeof(int32_t)) && (type == moony->forge.Bool) )
+ {
+ atomic_store_explicit(&moony->log_hidden, *i32, memory_order_release);
+ }
+
+ // get moony:logFollow
+ i32 = retrieve(
+ state,
+ moony->uris.moony_logFollow,
+ &size,
+ &type,
+ &flags2);
+ if(i32 && (size == sizeof(int32_t)) && (type == moony->forge.Bool) )
+ {
+ atomic_store_explicit(&moony->log_follow, *i32, memory_order_release);
+ }
+
+ // get moony:logReset
+ i32 = retrieve(
+ state,
+ moony->uris.moony_logReset,
+ &size,
+ &type,
+ &flags2);
+ if(i32 && (size == sizeof(int32_t)) && (type == moony->forge.Bool) )
+ {
+ atomic_store_explicit(&moony->log_reset, *i32, memory_order_release);
+ }
+
+ // get moony:paramHidden
+ i32 = retrieve(
+ state,
+ moony->uris.moony_paramHidden,
+ &size,
+ &type,
+ &flags2);
+ if(i32 && (size == sizeof(int32_t)) && (type == moony->forge.Bool) )
+ {
+ atomic_store_explicit(&moony->param_hidden, *i32, memory_order_release);
+ }
+
+ // get moony:paramCols
+ i32 = retrieve(
+ state,
+ moony->uris.moony_paramCols,
+ &size,
+ &type,
+ &flags2);
+ if(i32 && (size == sizeof(int32_t)) && (type == moony->forge.Int) )
+ {
+ atomic_store_explicit(&moony->param_cols, *i32, memory_order_release);
+ }
+
+ // get moony:paramRows
+ i32 = retrieve(
+ state,
+ moony->uris.moony_paramRows,
+ &size,
+ &type,
+ &flags2);
+ if(i32 && (size == sizeof(int32_t)) && (type == moony->forge.Int) )
+ {
+ atomic_store_explicit(&moony->param_rows, *i32, memory_order_release);
+ }
+
+ // get state
+ const uint8_t *body = retrieve(
+ state,
+ moony->uris.moony_state,
+ &size,
+ &type,
+ &flags2
+ );
+
+ if(body && size && type)
+ {
+ // allocate new state_atom
+ LV2_Atom *state_atom_new = malloc(sizeof(LV2_Atom) + size);
+ if(state_atom_new)
+ {
+ state_atom_new->size = size;
+ state_atom_new->type = type;
+ memcpy(LV2_ATOM_BODY(state_atom_new), body, size);
+
+ LV2_Atom *state_atom_old = (LV2_Atom *)atomic_exchange_explicit(&moony->state_atom_new, (uintptr_t)state_atom_new, memory_order_relaxed);
+ if(state_atom_old)
+ free(state_atom_old);
+ }
+ }
+
+ // get code chunk
+ const char *chunk = retrieve(
+ state,
+ moony->uris.moony_code,
+ &size,
+ &type,
+ &flags2);
+
+ if(chunk && size && (type == moony->forge.String) )
+ {
+ if(size <= MOONY_MAX_CHUNK_LEN)
+ {
+ moony_vm_t *vm_new = _compile(moony, chunk);
+ if(vm_new)
+ {
+ moony_vm_t *vm_old = (moony_vm_t *)atomic_exchange_explicit(&moony->vm_new, (uintptr_t)vm_new, memory_order_relaxed);
+ if(vm_old)
+ moony_vm_free(vm_old);
+ }
+ }
+ else
+ {
+ moony_err_async(moony, "restore: moony:code too long");
+ }
+ }
+ else
+ {
+ moony_err_async(moony, "restore: moony:code property not found");
+ }
+
+ return LV2_STATE_SUCCESS;
+}
+
+static const LV2_State_Interface state_iface = {
+ .save = _state_save,
+ .restore = _state_restore
+};
+
+__non_realtime static LV2_Worker_Status
+_work_job(moony_t *moony,
+ LV2_Worker_Respond_Function respond,
+ LV2_Worker_Respond_Handle target,
+ const moony_job_t *job)
+{
+ //printf("_work: %i\n", job->type);
+
+ switch(job->type)
+ {
+ case MOONY_JOB_MEM_ALLOC:
+ {
+ const moony_job_t req = {
+ .type = MOONY_JOB_MEM_ALLOC,
+ .mem.size = job->mem.size,
+ .mem.ptr = moony_vm_mem_alloc(job->mem.size)
+ };
+
+ return respond(target, sizeof(moony_job_t), &req); // signal to _work_response
+ } break;
+ case MOONY_JOB_MEM_FREE:
+ {
+ moony_vm_mem_free(job->mem.ptr, job->mem.size);
+ } break;
+ case MOONY_JOB_VM_ALLOC:
+ {
+ moony_vm_t *vm_new = _compile(moony, job->chunk);
+ if(vm_new)
+ {
+ moony_vm_t *vm_old = (moony_vm_t *)atomic_exchange_explicit(&moony->vm_new, (uintptr_t)vm_new, memory_order_relaxed);
+ if(vm_old)
+ moony_vm_free(vm_old);
+ }
+ else
+ {
+ return LV2_WORKER_ERR_UNKNOWN;
+ }
+ } break;
+ case MOONY_JOB_VM_FREE:
+ {
+ moony_vm_free(job->vm);
+ } break;
+ case MOONY_JOB_PTR_FREE:
+ {
+ free(job->ptr);
+ } break;
+ }
+
+ return LV2_WORKER_SUCCESS;
+}
+
+__non_realtime static LV2_Worker_Status
+_work(LV2_Handle instance,
+ LV2_Worker_Respond_Function respond,
+ LV2_Worker_Respond_Handle target,
+ uint32_t size,
+ const void *body)
+{
+ moony_t *moony = instance;
+
+ LV2_Worker_Status status = LV2_WORKER_SUCCESS;
+
+ size_t sz;
+ const moony_job_t *job;
+ while((job = varchunk_read_request(moony->from_dsp, &sz)))
+ {
+ status = _work_job(moony, respond, target, job);
+
+ varchunk_read_advance(moony->from_dsp);
+ }
+
+ return status;
+}
+
+__realtime static LV2_Worker_Status
+_work_response(LV2_Handle instance, uint32_t size, const void *body)
+{
+ moony_t *moony = instance;
+ const moony_job_t *job = body;
+
+ //printf("_work_response: %i\n", job->type);
+ switch(job->type)
+ {
+ case MOONY_JOB_MEM_ALLOC:
+ {
+ moony->vm->allocating = false;
+
+ if(!job->mem.ptr) // allocation failed
+ return LV2_WORKER_ERR_UNKNOWN;
+
+ // search for next free pool
+ int i;
+ for(i=1; i<MOONY_POOL_NUM; i++)
+ {
+ if(!moony->vm->area[i])
+ break;
+ }
+
+ bool failed = true;
+ if(i != MOONY_POOL_NUM) // no overflow
+ {
+ // tlsf add pool
+ moony->vm->size[i] = job->mem.size;
+ moony->vm->area[i] = job->mem.ptr;
+ moony->vm->pool[i] = tlsf_add_pool(moony->vm->tlsf,
+ moony->vm->area[i], moony->vm->size[i]); //FIXME stoat complains about printf
+
+ if(moony->vm->pool[i])
+ failed = false;
+ }
+
+ if(failed) // pool addition failed
+ {
+ moony_job_t *req;
+ if((req = varchunk_write_request(moony->from_dsp, sizeof(moony_job_t))))
+ {
+ req->type = MOONY_JOB_MEM_FREE;
+ req->mem.size = job->mem.size;
+ req->mem.ptr = job->mem.ptr;
+
+ moony->vm->size[i] = 0;
+ moony->vm->area[i] = NULL;
+ moony->vm->pool[i] = NULL;
+
+ varchunk_write_advance(moony->from_dsp, sizeof(moony_job_t));
+ if(moony_wake_worker(moony->sched) != LV2_WORKER_SUCCESS)
+ moony_trace(moony, "waking worker failed");
+ }
+
+ return LV2_WORKER_ERR_UNKNOWN;
+ }
+
+ moony->vm->space += moony->vm->size[i];
+ //printf("mem extended to %zu KB\n", moony->vm->space / 1024);
+ } break;
+
+ case MOONY_JOB_VM_ALLOC:
+ case MOONY_JOB_MEM_FREE:
+ case MOONY_JOB_VM_FREE:
+ case MOONY_JOB_PTR_FREE:
+ break; // never reached
+ }
+
+ return LV2_WORKER_SUCCESS;
+}
+
+__realtime static LV2_Worker_Status
+_end_run(LV2_Handle instance)
+{
+ moony_t *moony = instance;
+
+ return LV2_WORKER_SUCCESS;
+}
+
+static const LV2_Worker_Interface work_iface = {
+ .work = _work,
+ .work_response = _work_response,
+ .end_run = _end_run
+};
+
+__non_realtime const void*
+extension_data(const char* uri)
+{
+ if(!strcmp(uri, LV2_WORKER__interface))
+ return &work_iface;
+ else if(!strcmp(uri, LV2_STATE__interface))
+ return &state_iface;
+ else
+ return NULL;
+}
+
+__non_realtime int
+moony_init(moony_t *moony, const char *subject, double sample_rate,
+ const LV2_Feature *const *features, size_t mem_size, bool testing)
+{
+ atomic_init(&moony->state_atom_new, 0);
+ atomic_init(&moony->vm_new, 0);
+ atomic_init(&moony->err_new, 0);
+ atomic_init(&moony->chunk_new, 0);
+ moony->state_lock = (atomic_flag)ATOMIC_FLAG_INIT;
+
+ moony->from_dsp = varchunk_new(MOONY_MAX_CHUNK_LEN * 2, true);
+ if(!moony->from_dsp)
+ {
+ fprintf(stderr, "varchunk_new failed\n");
+ return -1;
+ }
+
+ moony->mem_size = mem_size;
+ moony->testing = testing;
+ moony->vm = moony_vm_new(moony->mem_size, testing, moony);
+ if(!moony->vm)
+ {
+ fprintf(stderr, "Lua VM cannot be initialized\n");
+ return -1;
+ }
+
+ bool load_default_state = false;
+ xpress_map_t *voice_map = NULL;
+
+ for(unsigned i=0; features[i]; i++)
+ {
+ if(!strcmp(features[i]->URI, LV2_URID__map))
+ moony->map = features[i]->data;
+ else if(!strcmp(features[i]->URI, LV2_URID__unmap))
+ moony->unmap = features[i]->data;
+ else if(!strcmp(features[i]->URI, LV2_WORKER__schedule))
+ moony->sched = features[i]->data;
+ else if(!strcmp(features[i]->URI, LV2_LOG__log))
+ moony->log = features[i]->data;
+ else if(!strcmp(features[i]->URI, LV2_OPTIONS__options))
+ moony->opts = features[i]->data;
+ else if(!strcmp(features[i]->URI, LV2_OSC__schedule))
+ moony->osc_sched = features[i]->data;
+ else if(!strcmp(features[i]->URI, LV2_STATE__loadDefaultState))
+ load_default_state = true;
+ else if(!strcmp(features[i]->URI, XPRESS__voiceMap))
+ voice_map = features[i]->data;
+ }
+
+ if(!moony->map)
+ {
+ fprintf(stderr, "Host does not support urid:map\n");
+ return -1;
+ }
+ if(!moony->unmap)
+ {
+ fprintf(stderr, "Host does not support urid:unmap\n");
+ return -1;
+ }
+ if(!moony->sched)
+ {
+ fprintf(stderr, "Host does not support worker:schedule\n");
+ return -1;
+ }
+ if(!load_default_state)
+ {
+ strcpy(moony->chunk,
+ "-- host does not support state:loadDefaultState feature\n\n"
+ "function run(n, control, notify, ...)\n"
+ "end");
+ moony->chunk_nrt = strdup(moony->chunk);
+ }
+
+ xpress_init(&moony->xpress, 0, moony->map, voice_map, XPRESS_EVENT_NONE,
+ NULL, NULL, NULL);
+
+ moony->uris.moony_code = moony->map->map(moony->map->handle, MOONY_CODE_URI);
+ moony->uris.moony_error = moony->map->map(moony->map->handle, MOONY_ERROR_URI);
+ moony->uris.moony_trace = moony->map->map(moony->map->handle, MOONY_TRACE_URI);
+ moony->uris.moony_panic = moony->map->map(moony->map->handle, MOONY_PANIC_URI);
+ moony->uris.moony_state = moony->map->map(moony->map->handle, MOONY_STATE_URI);
+ moony->uris.moony_editorHidden = moony->map->map(moony->map->handle, MOONY_EDITOR_HIDDEN_URI);
+ moony->uris.moony_logHidden = moony->map->map(moony->map->handle, MOONY_LOG_HIDDEN_URI);
+ moony->uris.moony_logFollow = moony->map->map(moony->map->handle, MOONY_LOG_FOLLOW_URI);
+ moony->uris.moony_logReset = moony->map->map(moony->map->handle, MOONY_LOG_RESET_URI);
+ moony->uris.moony_paramHidden = moony->map->map(moony->map->handle, MOONY_PARAM_HIDDEN_URI);
+ moony->uris.moony_paramCols = moony->map->map(moony->map->handle, MOONY_PARAM_COLS_URI);
+ moony->uris.moony_paramRows = moony->map->map(moony->map->handle, MOONY_PARAM_ROWS_URI);
+ moony->uris.moony_color = moony->map->map(moony->map->handle, MOONY__color);
+ moony->uris.moony_syntax = moony->map->map(moony->map->handle, MOONY__syntax);
+
+ moony->uris.midi_event = moony->map->map(moony->map->handle, LV2_MIDI__MidiEvent);
+
+ moony->uris.patch.self = moony->map->map(moony->map->handle, subject);
+
+ moony->uris.patch.get = moony->map->map(moony->map->handle, LV2_PATCH__Get);
+ moony->uris.patch.set = moony->map->map(moony->map->handle, LV2_PATCH__Set);
+ moony->uris.patch.put = moony->map->map(moony->map->handle, LV2_PATCH__Put);
+ moony->uris.patch.patch = moony->map->map(moony->map->handle, LV2_PATCH__Patch);
+ moony->uris.patch.body = moony->map->map(moony->map->handle, LV2_PATCH__body);
+ moony->uris.patch.subject = moony->map->map(moony->map->handle, LV2_PATCH__subject);
+ moony->uris.patch.property = moony->map->map(moony->map->handle, LV2_PATCH__property);
+ moony->uris.patch.value = moony->map->map(moony->map->handle, LV2_PATCH__value);
+ moony->uris.patch.add = moony->map->map(moony->map->handle, LV2_PATCH__add);
+ moony->uris.patch.remove = moony->map->map(moony->map->handle, LV2_PATCH__remove);
+ moony->uris.patch.wildcard = moony->map->map(moony->map->handle, LV2_PATCH__wildcard);
+ moony->uris.patch.writable = moony->map->map(moony->map->handle, LV2_PATCH__writable);
+ moony->uris.patch.readable = moony->map->map(moony->map->handle, LV2_PATCH__readable);
+ moony->uris.patch.destination = moony->map->map(moony->map->handle, LV2_PATCH__destination);
+ moony->uris.patch.sequence = moony->map->map(moony->map->handle, LV2_PATCH__sequenceNumber);
+ moony->uris.patch.error = moony->map->map(moony->map->handle, LV2_PATCH__Error);
+ moony->uris.patch.ack = moony->map->map(moony->map->handle, LV2_PATCH__Ack);
+ moony->uris.patch.delete = moony->map->map(moony->map->handle, LV2_PATCH__Delete);
+ moony->uris.patch.copy = moony->map->map(moony->map->handle, LV2_PATCH__Copy);
+ moony->uris.patch.move = moony->map->map(moony->map->handle, LV2_PATCH__Move);
+ moony->uris.patch.insert = moony->map->map(moony->map->handle, LV2_PATCH__Insert);
+
+ moony->uris.rdfs_label = moony->map->map(moony->map->handle, RDFS__label);
+ moony->uris.rdfs_range = moony->map->map(moony->map->handle, RDFS__range);
+ moony->uris.rdfs_comment = moony->map->map(moony->map->handle, RDFS__comment);
+
+ moony->uris.rdf_value = moony->map->map(moony->map->handle, RDF__value);
+
+ moony->uris.lv2_minimum = moony->map->map(moony->map->handle, LV2_CORE__minimum);
+ moony->uris.lv2_maximum = moony->map->map(moony->map->handle, LV2_CORE__maximum);
+ moony->uris.lv2_scale_point = moony->map->map(moony->map->handle, LV2_CORE__scalePoint);
+ moony->uris.lv2_minor_version= moony->map->map(moony->map->handle, LV2_CORE__minorVersion);
+ moony->uris.lv2_micro_version= moony->map->map(moony->map->handle, LV2_CORE__microVersion);
+
+ moony->uris.units_unit = moony->map->map(moony->map->handle, LV2_UNITS__unit);
+ moony->uris.units_symbol = moony->map->map(moony->map->handle, LV2_UNITS__symbol);
+
+ moony->uris.atom_frame_time = moony->map->map(moony->map->handle, LV2_ATOM__frameTime);
+ moony->uris.atom_beat_time = moony->map->map(moony->map->handle, LV2_ATOM__beatTime);
+ moony->uris.atom_child_type = moony->map->map(moony->map->handle, LV2_ATOM__childType);
+
+ moony->uris.xpress_Token = moony->map->map(moony->map->handle, XPRESS__Token);
+ moony->uris.xpress_Alive = moony->map->map(moony->map->handle, XPRESS__Alive);
+ moony->uris.xpress_source = moony->map->map(moony->map->handle, XPRESS__source);
+ moony->uris.xpress_uuid = moony->map->map(moony->map->handle, XPRESS__uuid);
+ moony->uris.xpress_zone = moony->map->map(moony->map->handle, XPRESS__zone);
+ moony->uris.xpress_body = moony->map->map(moony->map->handle, XPRESS__body);
+ moony->uris.xpress_pitch = moony->map->map(moony->map->handle, XPRESS__pitch);
+ moony->uris.xpress_pressure = moony->map->map(moony->map->handle, XPRESS__pressure);
+ moony->uris.xpress_timbre = moony->map->map(moony->map->handle, XPRESS__timbre);
+ moony->uris.xpress_dPitch = moony->map->map(moony->map->handle, XPRESS__dPitch);
+ moony->uris.xpress_dPressure = moony->map->map(moony->map->handle, XPRESS__dPressure);
+ moony->uris.xpress_dTimbre = moony->map->map(moony->map->handle, XPRESS__dTimbre);
+
+ lv2_canvas_urid_init(&moony->canvas_urid, moony->map);
+
+ lv2_osc_urid_init(&moony->osc_urid, moony->map);
+ lv2_atom_forge_init(&moony->forge, moony->map);
+ lv2_atom_forge_init(&moony->state_forge, moony->map);
+ lv2_atom_forge_init(&moony->stash_forge, moony->map);
+ lv2_atom_forge_init(&moony->notify_forge, moony->map);
+ if(moony->log)
+ lv2_log_logger_init(&moony->logger, moony->map, moony->log);
+
+ moony->sample_rate.atom.size = sizeof(float);
+ moony->sample_rate.atom.type = moony->forge.Float;
+ moony->sample_rate.body = sample_rate;
+
+ latom_driver_hash_t *latom_driver_hash = moony->atom_driver_hash;
+ unsigned pos = 0;
+
+ latom_driver_hash[pos].type = 0;
+ latom_driver_hash[pos++].driver = &latom_nil_driver;
+
+ latom_driver_hash[pos].type = moony->forge.Int;
+ latom_driver_hash[pos++].driver = &latom_int_driver;
+
+ latom_driver_hash[pos].type = moony->forge.Long;
+ latom_driver_hash[pos++].driver = &latom_long_driver;
+
+ latom_driver_hash[pos].type = moony->forge.Float;
+ latom_driver_hash[pos++].driver = &latom_float_driver;
+
+ latom_driver_hash[pos].type = moony->forge.Double;
+ latom_driver_hash[pos++].driver = &latom_double_driver;
+
+ latom_driver_hash[pos].type = moony->forge.Bool;
+ latom_driver_hash[pos++].driver = &latom_bool_driver;
+
+ latom_driver_hash[pos].type = moony->forge.URID;
+ latom_driver_hash[pos++].driver = &latom_urid_driver;
+
+ latom_driver_hash[pos].type = moony->forge.String;
+ latom_driver_hash[pos++].driver = &latom_string_driver;
+
+ latom_driver_hash[pos].type = moony->forge.URI;
+ latom_driver_hash[pos++].driver = &latom_string_driver;
+
+ latom_driver_hash[pos].type = moony->forge.Path;
+ latom_driver_hash[pos++].driver = &latom_string_driver;
+
+ latom_driver_hash[pos].type = moony->forge.Literal;
+ latom_driver_hash[pos++].driver = &latom_literal_driver;
+
+ latom_driver_hash[pos].type = moony->forge.Tuple;
+ latom_driver_hash[pos++].driver = &latom_tuple_driver;
+
+ latom_driver_hash[pos].type = moony->forge.Object;
+ latom_driver_hash[pos++].driver = &latom_object_driver;
+
+ latom_driver_hash[pos].type = moony->forge.Vector;
+ latom_driver_hash[pos++].driver = &latom_vector_driver;
+
+ latom_driver_hash[pos].type = moony->forge.Sequence;
+ latom_driver_hash[pos++].driver = &latom_sequence_driver;
+
+ assert(pos == DRIVER_HASH_MAX);
+ qsort(latom_driver_hash, DRIVER_HASH_MAX, sizeof(latom_driver_hash_t), _hash_sort);
+
+ moony_freeuserdata(moony);
+
+ moony->editor_hidden = ATOMIC_VAR_INIT(0);
+ moony->log_hidden = ATOMIC_VAR_INIT(1);
+ moony->log_follow = ATOMIC_VAR_INIT(1);
+ moony->log_reset = ATOMIC_VAR_INIT(0);
+ moony->param_hidden = ATOMIC_VAR_INIT(1);
+ moony->param_cols = ATOMIC_VAR_INIT(3);
+ moony->param_rows = ATOMIC_VAR_INIT(4);
+
+ return 0;
+}
+
+__non_realtime void
+moony_deinit(moony_t *moony)
+{
+ LV2_Atom *state_atom_old = (LV2_Atom *)atomic_load_explicit(&moony->state_atom_new, memory_order_relaxed);
+ if(state_atom_old)
+ free(state_atom_old);
+ if(moony->state_atom)
+ free(moony->state_atom);
+
+ xpress_deinit(&moony->xpress);
+
+ moony_vm_t *vm_old = (moony_vm_t *)atomic_load_explicit(&moony->vm_new, memory_order_relaxed);
+ if(vm_old)
+ moony_vm_free(vm_old);
+ if(moony->vm)
+ moony_vm_free(moony->vm);
+
+ char *err_old = (char *)atomic_load_explicit(&moony->err_new, memory_order_relaxed);
+ if(err_old)
+ free(err_old);
+
+ char *chunk_old = (char *)atomic_load_explicit(&moony->chunk_new, memory_order_relaxed);
+ if(chunk_old)
+ free(chunk_old);
+
+ if(moony->chunk_nrt)
+ free(moony->chunk_nrt);
+
+ if(moony->from_dsp)
+ varchunk_free(moony->from_dsp);
+}
+
+#define _protect_metatable(L, idx) \
+ lua_pushboolean(L, 0); \
+ lua_setfield(L, idx - 1, "__metatable");
+
+#define _index_metatable(L, idx) \
+ lua_pushvalue(L, idx); \
+ lua_setfield(L, idx - 1, "__index");
+
+__non_realtime void
+moony_open(moony_t *moony, moony_vm_t *vm, lua_State *L)
+{
+ luaL_newmetatable(L, "latom");
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ luaL_setfuncs (L, latom_mt, 1);
+ _protect_metatable(L, -1);
+ lua_pop(L, 1);
+
+ luaL_newmetatable(L, "lforge");
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ luaL_setfuncs (L, lforge_mt, 1);
+ _protect_metatable(L, -1);
+ _index_metatable(L, -1);
+ lua_pop(L, 1);
+
+ luaL_newmetatable(L, "lstash");
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ luaL_setfuncs (L, lstash_mt, 1);
+ _protect_metatable(L, -1);
+ _index_metatable(L, -1);
+ lua_pop(L, 1);
+
+ // lv2.map
+ lua_newtable(L);
+ lua_newtable(L);
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ luaL_setfuncs(L, lmap_mt, 1);
+ _protect_metatable(L, -1);
+ lua_setmetatable(L, -2);
+ lua_setglobal(L, "Map");
+
+ // lv2.unmap
+ lua_newtable(L);
+ lua_newtable(L);
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ luaL_setfuncs(L, lunmap_mt, 1);
+ _protect_metatable(L, -1);
+ lua_setmetatable(L, -2);
+ lua_setglobal(L, "Unmap");
+
+ // Note
+ lua_newtable(L);
+ lua_newtable(L);
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ luaL_setfuncs(L, lnote_mt, 1);
+ _protect_metatable(L, -1);
+ lua_setmetatable(L, -2);
+ lua_setglobal(L, "Note");
+
+ // lv2.opts
+ lua_newtable(L);
+ lua_newtable(L);
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ luaL_setfuncs(L, lopts_mt, 1);
+ _protect_metatable(L, -1);
+ lua_setmetatable(L, -2);
+ lua_setglobal(L, "Options");
+
+ // overwrite print function with LV2 log
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ lua_pushlightuserdata(L, vm); // @ upvalueindex 2
+ lua_pushcclosure(L, _log, 2);
+ lua_setglobal(L, "print");
+
+ // lv2.hash
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ lua_pushcclosure(L, _lmapper, 1);
+ lua_pushvalue(L, -1);
+ lua_setglobal(L, "HashMap"); // FIXME deprecated
+ lua_setglobal(L, "Mapper");
+
+ // lv2.voiceMap
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ lua_pushcclosure(L, _lvoice_map, 1);
+ lua_setglobal(L, "Blank");
+
+ // lv2.midi2cps
+ lua_pushcclosure(L, _lmidi2cps, 0);
+ lua_setglobal(L, "midi2cps");
+
+ // lv2cps2midi.
+ lua_pushcclosure(L, _lcps2midi, 0);
+ lua_setglobal(L, "cps2midi");
+
+#define SET_MAP(L, PREFIX, PROPERTY) \
+({ \
+ lua_getglobal(L, "Map"); \
+ lua_getfield(L, -1, PREFIX ## PROPERTY); \
+ LV2_URID urid = luaL_checkinteger(L, -1); \
+ lua_remove(L, -2); /* Map */ \
+ lua_setfield(L, -2, #PROPERTY); \
+ urid; \
+})
+
+ lua_newtable(L);
+ {
+ //SET_MAP(L, LV2_ATOM__, Blank); // is depracated
+ SET_MAP(L, LV2_ATOM__, Bool);
+ SET_MAP(L, LV2_ATOM__, Chunk);
+ SET_MAP(L, LV2_ATOM__, Double);
+ SET_MAP(L, LV2_ATOM__, Float);
+ SET_MAP(L, LV2_ATOM__, Int);
+ SET_MAP(L, LV2_ATOM__, Long);
+ SET_MAP(L, LV2_ATOM__, Literal);
+ SET_MAP(L, LV2_ATOM__, Object);
+ SET_MAP(L, LV2_ATOM__, Path);
+ SET_MAP(L, LV2_ATOM__, Property);
+ //SET_MAP(L, LV2_ATOM__, Resource); // is deprecated
+ SET_MAP(L, LV2_ATOM__, Sequence);
+ SET_MAP(L, LV2_ATOM__, String);
+ SET_MAP(L, LV2_ATOM__, Tuple);
+ SET_MAP(L, LV2_ATOM__, URI);
+ SET_MAP(L, LV2_ATOM__, URID);
+ SET_MAP(L, LV2_ATOM__, Vector);
+ SET_MAP(L, LV2_ATOM__, beatTime);
+ SET_MAP(L, LV2_ATOM__, frameTime);
+ SET_MAP(L, LV2_ATOM__, childType);
+ }
+ lua_setglobal(L, "Atom");
+
+ lua_newtable(L);
+ {
+ SET_MAP(L, LV2_MIDI__, MidiEvent);
+
+ for(const midi_msg_t *msg=midi_msgs; msg->key; msg++)
+ {
+ lua_pushinteger(L, msg->type);
+ lua_setfield(L, -2, msg->key);
+ }
+ for(const midi_msg_t *msg=controllers; msg->key; msg++)
+ {
+ lua_pushinteger(L, msg->type);
+ lua_setfield(L, -2, msg->key);
+ }
+ }
+ lua_setglobal(L, "MIDI");
+
+ lua_newtable(L);
+ {
+ SET_MAP(L, LV2_TIME__, Position);
+ SET_MAP(L, LV2_TIME__, barBeat);
+ SET_MAP(L, LV2_TIME__, bar);
+ SET_MAP(L, LV2_TIME__, beat);
+ SET_MAP(L, LV2_TIME__, beatUnit);
+ SET_MAP(L, LV2_TIME__, beatsPerBar);
+ SET_MAP(L, LV2_TIME__, beatsPerMinute);
+ SET_MAP(L, LV2_TIME__, frame);
+ SET_MAP(L, LV2_TIME__, framesPerSecond);
+ SET_MAP(L, LV2_TIME__, speed);
+ }
+ lua_setglobal(L, "Time");
+
+ lua_newtable(L);
+ {
+ SET_MAP(L, LV2_OSC__, Event);
+ SET_MAP(L, LV2_OSC__, Packet);
+ SET_MAP(L, LV2_OSC__, Bundle);
+ SET_MAP(L, LV2_OSC__, bundleTimetag);
+ SET_MAP(L, LV2_OSC__, bundleItems);
+ SET_MAP(L, LV2_OSC__, Message);
+ SET_MAP(L, LV2_OSC__, messagePath);
+ SET_MAP(L, LV2_OSC__, messageArguments);
+ SET_MAP(L, LV2_OSC__, Timetag);
+ SET_MAP(L, LV2_OSC__, timetagIntegral);
+ SET_MAP(L, LV2_OSC__, timetagFraction);
+ SET_MAP(L, LV2_OSC__, Nil);
+ SET_MAP(L, LV2_OSC__, Impulse);
+ SET_MAP(L, LV2_OSC__, Char);
+ SET_MAP(L, LV2_OSC__, RGBA);
+ }
+ lua_setglobal(L, "OSC");
+
+ lua_newtable(L);
+ {
+ SET_MAP(L, LV2_CORE__, minimum);
+ SET_MAP(L, LV2_CORE__, maximum);
+ SET_MAP(L, LV2_CORE__, scalePoint);
+ }
+ lua_setglobal(L, "LV2");
+
+ lua_newtable(L);
+ {
+ SET_MAP(L, LV2_BUF_SIZE__, minBlockLength);
+ SET_MAP(L, LV2_BUF_SIZE__, maxBlockLength);
+ SET_MAP(L, LV2_BUF_SIZE__, sequenceSize);
+ }
+ lua_setglobal(L, "Buf_Size");
+
+ lua_newtable(L);
+ {
+ SET_MAP(L, LV2_PATCH__, Ack);
+ SET_MAP(L, LV2_PATCH__, Delete);
+ SET_MAP(L, LV2_PATCH__, Copy);
+ SET_MAP(L, LV2_PATCH__, Error);
+ SET_MAP(L, LV2_PATCH__, Get);
+ SET_MAP(L, LV2_PATCH__, Message);
+ SET_MAP(L, LV2_PATCH__, Move);
+ SET_MAP(L, LV2_PATCH__, Insert);
+ SET_MAP(L, LV2_PATCH__, Patch);
+ SET_MAP(L, LV2_PATCH__, Post);
+ SET_MAP(L, LV2_PATCH__, Put);
+ SET_MAP(L, LV2_PATCH__, Request);
+ SET_MAP(L, LV2_PATCH__, Response);
+ SET_MAP(L, LV2_PATCH__, Set);
+ SET_MAP(L, LV2_PATCH__, accept);
+ SET_MAP(L, LV2_PATCH__, add);
+ SET_MAP(L, LV2_PATCH__, body);
+ SET_MAP(L, LV2_PATCH__, context);
+ SET_MAP(L, LV2_PATCH__, destination);
+ SET_MAP(L, LV2_PATCH__, property);
+ SET_MAP(L, LV2_PATCH__, readable);
+ SET_MAP(L, LV2_PATCH__, remove);
+ SET_MAP(L, LV2_PATCH__, request);
+ SET_MAP(L, LV2_PATCH__, subject);
+ SET_MAP(L, LV2_PATCH__, sequenceNumber);
+ SET_MAP(L, LV2_PATCH__, value);
+ SET_MAP(L, LV2_PATCH__, wildcard);
+ SET_MAP(L, LV2_PATCH__, writable);
+ }
+ lua_setglobal(L, "Patch");
+
+ lua_newtable(L);
+ {
+ SET_MAP(L, LV2_UI__, updateRate);
+ //TODO add more and document
+ }
+ lua_setglobal(L, "Ui");
+
+ lua_newtable(L);
+ {
+ SET_MAP(L, RDF__, value);
+ SET_MAP(L, RDF__, type);
+ }
+ lua_setglobal(L, "RDF");
+
+ lua_newtable(L);
+ {
+ SET_MAP(L, RDFS__, label);
+ SET_MAP(L, RDFS__, range);
+ SET_MAP(L, RDFS__, comment);
+ }
+ lua_setglobal(L, "RDFS");
+
+ lua_newtable(L);
+ {
+ SET_MAP(L, LV2_UNITS__, Conversion);
+ SET_MAP(L, LV2_UNITS__, Unit);
+ SET_MAP(L, LV2_UNITS__, bar);
+ SET_MAP(L, LV2_UNITS__, beat);
+ SET_MAP(L, LV2_UNITS__, bpm);
+ SET_MAP(L, LV2_UNITS__, cent);
+ SET_MAP(L, LV2_UNITS__, cm);
+ SET_MAP(L, LV2_UNITS__, coef);
+ SET_MAP(L, LV2_UNITS__, conversion);
+ SET_MAP(L, LV2_UNITS__, db);
+ SET_MAP(L, LV2_UNITS__, degree);
+ SET_MAP(L, LV2_UNITS__, frame);
+ SET_MAP(L, LV2_UNITS__, hz);
+ SET_MAP(L, LV2_UNITS__, inch);
+ SET_MAP(L, LV2_UNITS__, khz);
+ SET_MAP(L, LV2_UNITS__, km);
+ SET_MAP(L, LV2_UNITS__, m);
+ SET_MAP(L, LV2_UNITS__, mhz);
+ SET_MAP(L, LV2_UNITS__, midiNote);
+ SET_MAP(L, LV2_UNITS__, midiController);
+ SET_MAP(L, LV2_UNITS__, mile);
+ SET_MAP(L, LV2_UNITS__, min);
+ SET_MAP(L, LV2_UNITS__, mm);
+ SET_MAP(L, LV2_UNITS__, ms);
+ SET_MAP(L, LV2_UNITS__, name);
+ SET_MAP(L, LV2_UNITS__, oct);
+ SET_MAP(L, LV2_UNITS__, pc);
+ SET_MAP(L, LV2_UNITS__, prefixConversion);
+ SET_MAP(L, LV2_UNITS__, render);
+ SET_MAP(L, LV2_UNITS__, s);
+ SET_MAP(L, LV2_UNITS__, semitone12TET);
+ SET_MAP(L, LV2_UNITS__, symbol);
+ SET_MAP(L, LV2_UNITS__, unit);
+ }
+ lua_setglobal(L, "Units");
+
+ lua_newtable(L);
+ {
+ SET_MAP(L, CANVAS__, graph);
+ SET_MAP(L, CANVAS__, aspectRatio);
+ SET_MAP(L, CANVAS__, body);
+ SET_MAP(L, CANVAS__, BeginPath);
+ SET_MAP(L, CANVAS__, ClosePath);
+ SET_MAP(L, CANVAS__, Arc);
+ SET_MAP(L, CANVAS__, CurveTo);
+ SET_MAP(L, CANVAS__, LineTo);
+ SET_MAP(L, CANVAS__, MoveTo);
+ SET_MAP(L, CANVAS__, Rectangle);
+ SET_MAP(L, CANVAS__, PolyLine);
+ SET_MAP(L, CANVAS__, Style);
+ SET_MAP(L, CANVAS__, LineWidth);
+ SET_MAP(L, CANVAS__, LineDash);
+ SET_MAP(L, CANVAS__, LineCap);
+ SET_MAP(L, CANVAS__, LineJoin);
+ SET_MAP(L, CANVAS__, MiterLimit);
+ SET_MAP(L, CANVAS__, Stroke);
+ SET_MAP(L, CANVAS__, Fill);
+ SET_MAP(L, CANVAS__, Clip);
+ SET_MAP(L, CANVAS__, Save);
+ SET_MAP(L, CANVAS__, Restore);
+ SET_MAP(L, CANVAS__, Translate);
+ SET_MAP(L, CANVAS__, Scale);
+ SET_MAP(L, CANVAS__, Rotate);
+ SET_MAP(L, CANVAS__, Transform);
+ SET_MAP(L, CANVAS__, Reset);
+ SET_MAP(L, CANVAS__, FontSize);
+ SET_MAP(L, CANVAS__, FillText);
+ SET_MAP(L, CANVAS__, lineCapButt);
+ SET_MAP(L, CANVAS__, lineCapRound);
+ SET_MAP(L, CANVAS__, lineCapSquare);
+ SET_MAP(L, CANVAS__, lineJoinMiter);
+ SET_MAP(L, CANVAS__, lineJoinRound);
+ SET_MAP(L, CANVAS__, lineJoinBevel);
+ SET_MAP(L, CANVAS__, mouseButtonLeft);
+ SET_MAP(L, CANVAS__, mouseButtonMiddle);
+ SET_MAP(L, CANVAS__, mouseButtonRight);
+ SET_MAP(L, CANVAS__, mouseWheelX);
+ SET_MAP(L, CANVAS__, mouseWheelY);
+ SET_MAP(L, CANVAS__, mousePositionX);
+ SET_MAP(L, CANVAS__, mousePositionY);
+ SET_MAP(L, CANVAS__, mouseFocus);
+ }
+ lua_setglobal(L, "Canvas");
+
+ lua_newtable(L);
+ {
+ SET_MAP(L, XPRESS__, Token);
+ SET_MAP(L, XPRESS__, Alive);
+ SET_MAP(L, XPRESS__, source);
+ SET_MAP(L, XPRESS__, zone);
+ SET_MAP(L, XPRESS__, uuid);
+ SET_MAP(L, XPRESS__, body);
+ SET_MAP(L, XPRESS__, pitch);
+ SET_MAP(L, XPRESS__, pressure);
+ SET_MAP(L, XPRESS__, timbre);
+ SET_MAP(L, XPRESS__, dPitch);
+ SET_MAP(L, XPRESS__, dPressure);
+ SET_MAP(L, XPRESS__, dTimbre);
+ }
+ lua_setglobal(L, "Xpress");
+
+ lua_newtable(L);
+ {
+ SET_MAP(L, MOONY__, color);
+ SET_MAP(L, MOONY__, syntax);
+ //TODO more
+ }
+ lua_setglobal(L, "Moony");
+
+ lua_newtable(L);
+ {
+ moony->uris.param_sampleRate = SET_MAP(L, LV2_PARAMETERS__, sampleRate);
+ //TODO more
+ }
+ lua_setglobal(L, "Param");
+
+ lua_newtable(L);
+ {
+ SET_MAP(L, LUA__, lang);
+ }
+ lua_setglobal(L, "Lua");
+
+ // create userdata caches
+ lua_newtable(L);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, &moony_ref[MOONY_UDATA_ATOM]);
+
+ lua_newtable(L);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, &moony_ref[MOONY_UDATA_FORGE]);
+
+ lua_newtable(L);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, &moony_ref[MOONY_UDATA_STASH]);
+
+ // MIDIResponder metatable
+ luaL_newmetatable(L, "lmidiresponder");
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ luaL_setfuncs (L, lmidiresponder_mt, 1);
+ _protect_metatable(L, -1);
+ _index_metatable(L, -1);
+ lua_pop(L, 1);
+
+ // MIDIResponder factory
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ lua_pushcclosure(L, _lmidiresponder, 1);
+ lua_setglobal(L, "MIDIResponder");
+
+ // OSCResponder metatable
+ luaL_newmetatable(L, "loscresponder");
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ luaL_setfuncs (L, loscresponder_mt, 1);
+ _protect_metatable(L, -1);
+ _index_metatable(L, -1);
+ lua_pop(L, 1);
+
+ // OSCResponder factory
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ lua_pushcclosure(L, _loscresponder, 1);
+ lua_setglobal(L, "OSCResponder");
+
+ // OSCResponder pattern matcher
+ luaL_dostring(L, loscresponder_match); // cannot fail
+
+ // TimeResponder metatable
+ luaL_newmetatable(L, "ltimeresponder");
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ luaL_setfuncs (L, ltimeresponder_mt, 1);
+ _protect_metatable(L, -1);
+ // we have a __index function, thus no __index table here
+ lua_pop(L, 1);
+
+ // TimeResponder factory
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ lua_pushcclosure(L, _ltimeresponder, 1);
+ lua_setglobal(L, "TimeResponder");
+
+ // StateResponder metatable
+ luaL_newmetatable(L, "lstateresponder");
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ luaL_setfuncs (L, lstateresponder_mt, 1);
+ _protect_metatable(L, -1);
+ _index_metatable(L, -1);
+ lua_pop(L, 1);
+
+ // StateResponder factory
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ lua_pushcclosure(L, _lstateresponder, 1);
+ lua_setglobal(L, "StateResponder");
+
+ // Parameter metatable
+ luaL_newmetatable(L, "lparameter");
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ luaL_setfuncs (L, lparameter_mt, 1);
+ _protect_metatable(L, -1);
+ //_index_metatable(L, -1);
+ lua_pop(L, 1);
+
+ // Parameter factory
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ lua_pushcclosure(L, _lparameter, 1);
+ lua_setglobal(L, "Parameter");
+
+ // Stash factory
+ lua_pushlightuserdata(L, moony); // @ upvalueindex 1
+ lua_pushlightuserdata(L, vm); // @ upvalueindex 2
+ lua_pushcclosure(L, _lstash, 2);
+ lua_setglobal(L, "Stash");
+
+ // create cclosure caches
+ lua_pushlightuserdata(L, moony);
+ lua_pushcclosure(L, _save, 1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, _save);
+
+ lua_pushlightuserdata(L, moony);
+ lua_pushcclosure(L, _restore, 1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, _restore);
+
+ lua_pushlightuserdata(L, moony);
+ lua_pushcclosure(L, _stash, 1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, _stash);
+
+ lua_pushlightuserdata(L, moony);
+ lua_pushcclosure(L, _apply, 1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, _apply);
+
+ lua_pushlightuserdata(L, moony);
+ lua_pushcclosure(L, _ltimeresponder_stash, 1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, _ltimeresponder_stash);
+
+ lua_pushlightuserdata(L, moony);
+ lua_pushcclosure(L, _ltimeresponder_apply, 1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, _ltimeresponder_apply);
+
+ lua_pushlightuserdata(L, moony);
+ lua_pushcclosure(L, _latom_clone, 1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, _latom_clone);
+
+ lua_pushlightuserdata(L, moony);
+ lua_pushcclosure(L, _lstash_write, 1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, _lstash_write);
+
+ lua_pushlightuserdata(L, moony);
+ lua_pushcclosure(L, _lstash_read, 1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, _lstash_read);
+
+ lua_pushlightuserdata(L, moony);
+ lua_pushcclosure(L, _latom_literal_unpack, 1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, _latom_literal_unpack);
+
+ lua_pushlightuserdata(L, moony);
+ lua_pushcclosure(L, _latom_tuple_unpack, 1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, _latom_tuple_unpack);
+
+ lua_pushlightuserdata(L, moony);
+ lua_pushcclosure(L, _latom_vec_unpack, 1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, _latom_vec_unpack);
+
+ lua_pushlightuserdata(L, moony);
+ lua_pushcclosure(L, _latom_chunk_unpack, 1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, _latom_chunk_unpack);
+
+ lua_pushlightuserdata(L, moony);
+ lua_pushcclosure(L, _latom_tuple_foreach, 1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, _latom_tuple_foreach);
+
+ lua_pushlightuserdata(L, moony);
+ lua_pushcclosure(L, _latom_vec_foreach, 1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, _latom_vec_foreach);
+
+ lua_pushlightuserdata(L, moony);
+ lua_pushcclosure(L, _latom_obj_foreach, 1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, _latom_obj_foreach);
+
+ lua_pushlightuserdata(L, moony);
+ lua_pushcclosure(L, _latom_seq_foreach, 1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, _latom_seq_foreach);
+
+ lua_pushlightuserdata(L, moony);
+ lua_pushcclosure(L, _lforge_autopop_itr, 1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, _lforge_autopop_itr);
+
+ lua_newtable(L);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, &upclosures[MOONY_UPCLOSURE_TUPLE_FOREACH]);
+
+ lua_newtable(L);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, &upclosures[MOONY_UPCLOSURE_VECTOR_FOREACH]);
+
+ lua_newtable(L);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, &upclosures[MOONY_UPCLOSURE_OBJECT_FOREACH]);
+
+ lua_newtable(L);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, &upclosures[MOONY_UPCLOSURE_SEQUENCE_FOREACH]);
+
+ lua_newtable(L);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, &upclosures[MOONY_UPCLOSURE_SEQUENCE_MULTIPLEX]);
+
+#undef SET_MAP
+}
+
+__realtime void *
+moony_newuserdata(lua_State *L, moony_t *moony, moony_udata_t type, bool cache)
+{
+ assert( (type >= MOONY_UDATA_ATOM) && (type < MOONY_UDATA_COUNT) );
+
+ int *itr = &moony->itr[type];
+ void *data = NULL;
+
+ if(cache) // do cash this!
+ {
+ lua_rawgetp(L, LUA_REGISTRYINDEX, &moony_ref[type]); // ref
+ if(lua_rawgeti(L, -1, *itr) == LUA_TNIL) // no cached udata, create one!
+ {
+#if 0
+ if(moony->log)
+ lv2_log_trace(&moony->logger, "moony_newuserdata: %s\n", moony_ref[type]);
+#endif
+ lua_pop(L, 1); // nil
+
+ data = lua_newuserdata(L, moony_sz[type]);
+ lheader_t *lheader = data;
+ lheader->type = type;
+ lheader->cache = cache;
+ luaL_getmetatable(L, moony_ref[type]);
+ lua_setmetatable(L, -2);
+ lua_pushvalue(L, -1);
+ lua_rawseti(L, -3, *itr); // store in cache
+ }
+ else // there is a cached udata, use it!
+ {
+ data = lua_touserdata(L, -1);
+ //printf("moony_newuserdata: %s %"PRIi32" %p\n", moony_ref[type], *itr, data);
+ }
+ lua_remove(L, -2); // ref
+ *itr += 1;
+ }
+ else // do not cash this!
+ {
+ data = lua_newuserdata(L, moony_sz[type]);
+ lheader_t *lheader = data;
+ lheader->type = type;
+ lheader->cache = cache;
+ luaL_getmetatable(L, moony_ref[type]);
+ lua_setmetatable(L, -2);
+ }
+
+ return data;
+}
+
+__realtime void
+moony_pre(moony_t *moony, LV2_Atom_Sequence *notify)
+{
+ // initialize notify forge
+ const uint32_t capacity = notify->atom.size;
+ lv2_atom_forge_set_buffer(&moony->notify_forge, (uint8_t *)notify, capacity);
+ moony->notify_ref = lv2_atom_forge_sequence_head(&moony->notify_forge, &moony->notify_frame, 0);
+}
+
+__realtime static LV2_Atom_Forge_Ref
+_patch_set(patch_t *patch, LV2_Atom_Forge *forge, LV2_URID property, uint32_t size, LV2_URID type, const void *body)
+{
+ LV2_Atom_Forge_Frame frame;
+ LV2_Atom_Forge_Ref ref = lv2_atom_forge_object(forge, &frame, 0, patch->set);
+
+ if(ref)
+ ref = lv2_atom_forge_key(forge, patch->property);
+ if(ref)
+ ref = lv2_atom_forge_urid(forge, property);
+
+ if(ref)
+ ref = lv2_atom_forge_key(forge, patch->value);
+ if(ref)
+ ref = lv2_atom_forge_atom(forge, size, type);
+ if(ref)
+ ref = lv2_atom_forge_write(forge, body, size);
+
+ if(ref)
+ lv2_atom_forge_pop(forge, &frame);
+
+ return ref;
+}
+
+__realtime static inline LV2_Atom_Forge_Ref
+_moony_chunk_out(moony_t *moony, uint32_t frames, LV2_Atom_Forge *forge)
+{
+ const uint32_t len = strlen(moony->chunk);
+
+ LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
+ if(ref)
+ ref = _moony_patch(&moony->uris.patch, forge, moony->uris.moony_code, moony->chunk, len);
+
+ return ref;
+}
+
+__realtime static inline LV2_Atom_Forge_Ref
+_moony_props_out(moony_t *moony, uint32_t frames, LV2_Atom_Forge *forge)
+{
+ // clear all properties in UI
+ LV2_Atom_Forge_Frame obj_frame, add_frame, rem_frame;
+ LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
+ if(ref)
+ ref = lv2_atom_forge_object(forge, &obj_frame, 0, moony->uris.patch.patch);
+ if(ref)
+ ref = lv2_atom_forge_key(forge, moony->uris.patch.subject);
+ if(ref)
+ ref = lv2_atom_forge_urid(forge, moony->uris.patch.self);
+ if(ref)
+ ref = lv2_atom_forge_key(forge, moony->uris.patch.sequence);
+ if(ref)
+ ref = lv2_atom_forge_int(forge, 0); // we don't expect a reply
+ if(ref)
+ ref = lv2_atom_forge_key(forge, moony->uris.patch.remove);
+ if(ref)
+ ref = lv2_atom_forge_object(forge, &rem_frame, 0, 0);
+ if(ref)
+ ref = lv2_atom_forge_key(forge, moony->uris.patch.writable);
+ if(ref)
+ ref = lv2_atom_forge_urid(forge, moony->uris.patch.wildcard);
+ if(ref)
+ ref = lv2_atom_forge_key(forge, moony->uris.patch.readable);
+ if(ref)
+ ref = lv2_atom_forge_urid(forge, moony->uris.patch.wildcard);
+ if(ref)
+ lv2_atom_forge_pop(forge, &rem_frame);
+ if(ref)
+ ref = lv2_atom_forge_key(forge, moony->uris.patch.add);
+ if(ref)
+ lv2_atom_forge_object(forge, &add_frame, 0, 0);
+ if(ref)
+ lv2_atom_forge_pop(forge, &add_frame);
+ if(ref)
+ lv2_atom_forge_pop(forge, &obj_frame);
+
+ return ref;
+}
+
+__realtime LV2_Worker_Status
+moony_wake_worker(const LV2_Worker_Schedule *work_sched)
+{
+ int32_t dummy;
+ return work_sched->schedule_work(work_sched->handle, sizeof(int32_t), &dummy);
+}
+
+__realtime bool
+moony_in(moony_t *moony, const LV2_Atom_Sequence *control, LV2_Atom_Sequence *notify)
+{
+ LV2_Atom_Forge *forge = &moony->notify_forge;
+ LV2_Atom_Forge_Ref ref = moony->notify_ref;
+
+ char *chunk_new = (char *)atomic_exchange_explicit(&moony->chunk_new, 0, memory_order_relaxed);
+ if(chunk_new)
+ {
+ snprintf(moony->chunk, MOONY_MAX_CHUNK_LEN, "%s", chunk_new);
+ if(ref)
+ ref = _moony_chunk_out(moony, 0, forge);
+
+ moony_job_t *req;
+ if((req = varchunk_write_request(moony->from_dsp, sizeof(moony_job_t))))
+ {
+ req->type = MOONY_JOB_PTR_FREE;
+ req->ptr = chunk_new;
+
+ varchunk_write_advance(moony->from_dsp, sizeof(moony_job_t));
+ if(moony_wake_worker(moony->sched) != LV2_WORKER_SUCCESS)
+ moony_trace(moony, "waking worker failed");
+ }
+ }
+
+ char *err_new = (char *)atomic_exchange_explicit(&moony->err_new, 0, memory_order_relaxed);
+ if(err_new)
+ {
+ // always overwrite previous error message
+ snprintf(moony->error, MOONY_MAX_ERROR_LEN, "%s", err_new);
+ moony->error_out = true;
+
+ moony_job_t *req;
+ if((req = varchunk_write_request(moony->from_dsp, sizeof(moony_job_t))))
+ {
+ req->type = MOONY_JOB_PTR_FREE;
+ req->ptr = err_new;
+
+ varchunk_write_advance(moony->from_dsp, sizeof(moony_job_t));
+ if(moony_wake_worker(moony->sched) != LV2_WORKER_SUCCESS)
+ moony_trace(moony, "waking worker failed");
+ }
+ }
+
+ LV2_Atom *state_atom_new = (LV2_Atom *)atomic_exchange_explicit(&moony->state_atom_new, 0, memory_order_relaxed);
+ if(state_atom_new)
+ {
+ LV2_Atom *state_atom_old = moony->state_atom;
+ moony->state_atom = state_atom_new;
+
+ if(state_atom_old)
+ {
+ moony_job_t *req;
+ if((req = varchunk_write_request(moony->from_dsp, sizeof(moony_job_t))))
+ {
+ req->type = MOONY_JOB_PTR_FREE;
+ req->ptr = state_atom_old;
+
+ varchunk_write_advance(moony->from_dsp, sizeof(moony_job_t));
+ if(moony_wake_worker(moony->sched) != LV2_WORKER_SUCCESS)
+ moony_trace(moony, "waking worker failed");
+ }
+ }
+ }
+
+ moony_vm_t *vm_new = (moony_vm_t *)atomic_exchange_explicit(&moony->vm_new, 0, memory_order_relaxed);
+ if(vm_new)
+ {
+ lua_State *L = moony_current(moony);
+
+ moony->error[0] = 0x0; // clear error message
+ moony->error_out = true;
+
+ // stash
+ lua_rawgetp(L, LUA_REGISTRYINDEX, _stash);
+ if(lua_pcall(L, 0, 0, 0))
+ moony_error(moony);
+#ifdef USE_MANUAL_GC
+ lua_gc(L, LUA_GCSTEP, 0);
+#endif
+
+ // switch VM states
+ moony_vm_t *vm_old = moony->vm;
+ moony->vm = vm_new;
+ L = moony_current(moony);
+
+ if(moony->state_atom)
+ {
+ // restore Lua defined properties
+ lua_rawgetp(L, LUA_REGISTRYINDEX, _restore);
+ if(lua_pcall(L, 0, 0, 0))
+ moony_error(moony);
+#ifdef USE_MANUAL_GC
+ lua_gc(L, LUA_GCSTEP, 0);
+#endif
+ }
+
+ // apply stash
+ if(moony->stash_atom) // something has been stashed previously
+ {
+ lua_rawgetp(L, LUA_REGISTRYINDEX, _apply);
+ if(lua_pcall(L, 0, 0, 0))
+ moony_error(moony);
+#ifdef USE_MANUAL_GC
+ lua_gc(L, LUA_GCSTEP, 0);
+#endif
+
+ moony_rt_free(vm_old, moony->stash_atom, moony->stash_size);
+ moony->stash_atom = NULL;
+ moony->stash_size = 0;
+ }
+
+ {
+ moony_job_t *req;
+ if((req = varchunk_write_request(moony->from_dsp, sizeof(moony_job_t))))
+ {
+ req->type = MOONY_JOB_VM_FREE;
+ req->vm = vm_old;
+
+ varchunk_write_advance(moony->from_dsp, sizeof(moony_job_t));
+ if(moony_wake_worker(moony->sched) != LV2_WORKER_SUCCESS)
+ moony_trace(moony, "waking worker failed");
+ }
+ }
+
+ moony->once = true;
+
+ if(ref)
+ ref = _moony_props_out(moony, 0, forge);
+ }
+
+ lua_State *L = moony_current(moony);
+
+ // read control sequence
+ LV2_ATOM_SEQUENCE_FOREACH(control, ev)
+ {
+ const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
+
+ if(!lv2_atom_forge_is_object_type(&moony->forge, obj->atom.type))
+ continue;
+
+ if(obj->body.otype == moony->uris.patch.get)
+ {
+ const LV2_Atom_URID *subject = NULL;
+ const LV2_Atom_URID *property = NULL;
+ const LV2_Atom_Int *sequence= NULL;
+
+ lv2_atom_object_get(obj,
+ moony->uris.patch.subject, &subject,
+ moony->uris.patch.property, &property,
+ moony->uris.patch.sequence, &sequence,
+ 0);
+
+ int32_t sequence_num = 0;
+ if(sequence && (sequence->atom.type == moony->forge.Int))
+ sequence_num = sequence->body;
+ (void)sequence_num; //FIXME use
+
+ if( subject
+ && (subject->atom.type == moony->forge.URID)
+ && (subject->body != moony->uris.patch.self) )
+ continue; // subject does not match
+
+ if(property && (property->atom.type == moony->forge.URID) )
+ {
+ if(property->body == moony->uris.moony_code)
+ {
+ if(ref)
+ ref = _moony_chunk_out(moony, 0, forge);
+ }
+ else if(property->body == moony->uris.moony_error)
+ {
+ if(moony->error[0] != 0x0)
+ moony->error_out = true;
+ }
+ else if(property->body == moony->uris.moony_editorHidden)
+ {
+ const int32_t i32 = atomic_load_explicit(&moony->editor_hidden, memory_order_acquire);
+ if(ref)
+ ref = lv2_atom_forge_frame_time(forge, 0); //FIXME
+ if(ref)
+ ref = _patch_set(&moony->uris.patch, forge, property->body, sizeof(int32_t), forge->Bool, &i32);
+ }
+ else if(property->body == moony->uris.moony_logHidden)
+ {
+ const int32_t i32 = atomic_load_explicit(&moony->log_hidden, memory_order_acquire);
+ if(ref)
+ ref = lv2_atom_forge_frame_time(forge, 0); //FIXME
+ if(ref)
+ ref = _patch_set(&moony->uris.patch, forge, property->body, sizeof(int32_t), forge->Bool, &i32);
+ }
+ else if(property->body == moony->uris.moony_logFollow)
+ {
+ const int32_t i32 = atomic_load_explicit(&moony->log_follow, memory_order_acquire);
+ if(ref)
+ ref = lv2_atom_forge_frame_time(forge, 0); //FIXME
+ if(ref)
+ ref = _patch_set(&moony->uris.patch, forge, property->body, sizeof(int32_t), forge->Bool, &i32);
+ }
+ else if(property->body == moony->uris.moony_logReset)
+ {
+ const int32_t i32 = atomic_load_explicit(&moony->log_reset, memory_order_acquire);
+ if(ref)
+ ref = lv2_atom_forge_frame_time(forge, 0); //FIXME
+ if(ref)
+ ref = _patch_set(&moony->uris.patch, forge, property->body, sizeof(int32_t), forge->Bool, &i32);
+ }
+ else if(property->body == moony->uris.moony_paramHidden)
+ {
+ const int32_t i32 = atomic_load_explicit(&moony->param_hidden, memory_order_acquire);
+ if(ref)
+ ref = lv2_atom_forge_frame_time(forge, 0); //FIXME
+ if(ref)
+ ref = _patch_set(&moony->uris.patch, forge, property->body, sizeof(int32_t), forge->Bool, &i32);
+ }
+ else if(property->body == moony->uris.moony_paramCols)
+ {
+ const int32_t i32 = atomic_load_explicit(&moony->param_cols, memory_order_acquire);
+ if(ref)
+ ref = lv2_atom_forge_frame_time(forge, 0); //FIXME
+ if(ref)
+ ref = _patch_set(&moony->uris.patch, forge, property->body, sizeof(int32_t), forge->Int, &i32);
+ }
+ else if(property->body == moony->uris.moony_paramRows)
+ {
+ const int32_t i32 = atomic_load_explicit(&moony->param_rows, memory_order_acquire);
+ if(ref)
+ ref = lv2_atom_forge_frame_time(forge, 0); //FIXME
+ if(ref)
+ ref = _patch_set(&moony->uris.patch, forge, property->body, sizeof(int32_t), forge->Int, &i32);
+ }
+ }
+ else // !property
+ {
+ if(ref)
+ ref = _moony_props_out(moony, 0, forge);
+ }
+ }
+ else if(obj->body.otype == moony->uris.patch.set)
+ {
+ const LV2_Atom_URID *subject = NULL;
+ const LV2_Atom_URID *property = NULL;
+ const LV2_Atom_Int *sequence = NULL;
+ const LV2_Atom *value = NULL;
+
+ lv2_atom_object_get(obj,
+ moony->uris.patch.subject, &subject,
+ moony->uris.patch.property, &property,
+ moony->uris.patch.sequence, &sequence,
+ moony->uris.patch.value, &value,
+ 0);
+
+ int32_t sequence_num = 0;
+ if(sequence && (sequence->atom.type == moony->forge.Int))
+ sequence_num = sequence->body;
+ (void)sequence_num; //FIXME use
+
+ if( subject
+ && (subject->atom.type == moony->forge.URID)
+ && (subject->body != moony->uris.patch.self) )
+ continue; // subject does not match
+
+ if( property && value
+ && (property->atom.type == moony->forge.URID) )
+ {
+ if( (property->body == moony->uris.moony_code) && (value->type == forge->String) )
+ {
+ // send code to worker thread
+ const size_t sz = sizeof(moony_job_t) + value->size;
+ moony_job_t *req;
+ if((req = varchunk_write_request(moony->from_dsp, sz)))
+ {
+ req->type = MOONY_JOB_VM_ALLOC;
+ memcpy(req->chunk, LV2_ATOM_BODY_CONST(value), value->size);
+
+ varchunk_write_advance(moony->from_dsp, sz);
+ if(moony_wake_worker(moony->sched) != LV2_WORKER_SUCCESS)
+ moony_trace(moony, "waking worker failed");
+ }
+ }
+ else if( (property->body == moony->uris.moony_editorHidden) && (value->type == forge->Bool) )
+ {
+ atomic_store_explicit(&moony->editor_hidden, ((const LV2_Atom_Bool *)value)->body, memory_order_release);
+ }
+ else if( (property->body == moony->uris.moony_logHidden) && (value->type == forge->Bool) )
+ {
+ atomic_store_explicit(&moony->log_hidden, ((const LV2_Atom_Bool *)value)->body, memory_order_release);
+ }
+ else if( (property->body == moony->uris.moony_logFollow) && (value->type == forge->Bool) )
+ {
+ atomic_store_explicit(&moony->log_follow, ((const LV2_Atom_Bool *)value)->body, memory_order_release);
+ }
+ else if( (property->body == moony->uris.moony_logReset) && (value->type == forge->Bool) )
+ {
+ atomic_store_explicit(&moony->log_reset, ((const LV2_Atom_Bool *)value)->body, memory_order_release);
+ }
+ else if( (property->body == moony->uris.moony_paramHidden) && (value->type == forge->Bool) )
+ {
+ atomic_store_explicit(&moony->param_hidden, ((const LV2_Atom_Bool *)value)->body, memory_order_release);
+ }
+ else if( (property->body == moony->uris.moony_paramCols) && (value->type == forge->Int) )
+ {
+ atomic_store_explicit(&moony->param_cols, ((const LV2_Atom_Int *)value)->body, memory_order_release);
+ }
+ else if( (property->body == moony->uris.moony_paramRows) && (value->type == forge->Int) )
+ {
+ atomic_store_explicit(&moony->param_rows, ((const LV2_Atom_Int *)value)->body, memory_order_release);
+ }
+ else if( (property->body == moony->uris.moony_panic) && (value->type == forge->Bool) )
+ {
+ const LV2_Atom_Bool *i32 = (const LV2_Atom_Bool *)value;
+ if(i32->body)
+ moony_err(moony, "user called panic");
+ }
+ }
+ }
+ }
+
+ if(moony->error_out)
+ {
+ const uint32_t len = strlen(moony->error);
+ if(ref)
+ ref = lv2_atom_forge_frame_time(forge, 0);
+ if(ref)
+ ref = _moony_patch(&moony->uris.patch, forge, moony->uris.moony_error, moony->error, len);
+
+ moony->error_out = false; // reset flag
+ }
+
+ moony->notify_ref = ref;
+ moony->notify_snapshot = *forge; // make snapshot if script should error before moony_out
+
+ return moony->once;
+}
+
+__realtime void
+moony_out(moony_t *moony, LV2_Atom_Sequence *notify, uint32_t frames)
+{
+ LV2_Atom_Forge *forge = &moony->notify_forge;
+ LV2_Atom_Forge_Ref ref = moony->notify_ref;
+ moony_vm_t *vm = moony->vm;
+
+ if(moony_bypass(moony)) // discard any written atoms on notify port since moony_in
+ {
+ *forge = moony->notify_snapshot;
+ notify->atom.size = forge->offset - sizeof(LV2_Atom);
+ }
+
+ if(vm->trace_out)
+ {
+ for(const char *from = vm->trace, *to = strchr(from, '\n');
+ from && to;
+ from = to + 1, to = strchr(from, '\n'))
+ {
+ if(ref)
+ ref = lv2_atom_forge_frame_time(forge, frames);
+ if(ref)
+ ref = _moony_patch(&moony->uris.patch, forge, moony->uris.moony_trace, from, to - from);
+ }
+
+ vm->trace[0] = '\0';
+ vm->trace_out = false; // reset flag
+ }
+
+ if(vm->trace_overflow)
+ {
+ if(moony->log)
+ lv2_log_trace(&moony->logger, "trace buffer overflow\n");
+ vm->trace_overflow = false; // reset flag
+ }
+
+ if(ref)
+ lv2_atom_forge_pop(forge, &moony->notify_frame);
+ else
+ lv2_atom_sequence_clear(notify);
+
+ moony->once = false;
+}
diff --git a/api/api_atom.c b/api/api_atom.c
new file mode 100644
index 0000000..04708ae
--- /dev/null
+++ b/api/api_atom.c
@@ -0,0 +1,1214 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <api_atom.h>
+#include <api_stash.h>
+
+#include <inttypes.h>
+#include <math.h>
+
+#define LV2_ATOM_VECTOR_BODY_ITEM_CONST(body, i) \
+ (LV2_ATOM_CONTENTS_CONST(LV2_Atom_Vector_Body, (body)) + (i)*(body)->child_size)
+
+const lua_CFunction upclosures [MOONY_UPCLOSURE_COUNT] = {
+ [MOONY_UPCLOSURE_TUPLE_FOREACH] = _latom_tuple_foreach_itr,
+ [MOONY_UPCLOSURE_VECTOR_FOREACH] = _latom_vec_foreach_itr,
+ [MOONY_UPCLOSURE_OBJECT_FOREACH] = _latom_obj_foreach_itr,
+ [MOONY_UPCLOSURE_SEQUENCE_FOREACH] = _latom_seq_foreach_itr,
+ [MOONY_UPCLOSURE_SEQUENCE_MULTIPLEX] = _latom_seq_multiplex_itr
+};
+
+__realtime static inline void
+_pushupclosure(lua_State *L, moony_t *moony, moony_upclosure_t type, bool cache)
+{
+ int *upc = &moony->upc[type];
+
+ lua_rawgetp(L, LUA_REGISTRYINDEX, &upclosures[type]); // ref
+ if(lua_rawgeti(L, -1, *upc) == LUA_TNIL) // no cached udata, create one!
+ {
+#if 0
+ if(moony->log)
+ lv2_log_trace(&moony->logger, "_pushupclosure:\n");
+#endif
+ lua_pop(L, 1); // nil
+
+ lua_pushlightuserdata(L, moony);
+ _latom_new(L, NULL, false); // place-holder
+ lua_pushcclosure(L, upclosures[type], 2);
+
+ lua_pushvalue(L, -1);
+ lua_rawseti(L, -3, *upc); // store in cache
+ }
+ lua_remove(L, -2); // ref
+ *upc += 1;
+}
+
+__realtime int
+_latom_clone(lua_State *L)
+{
+ latom_t *latom = lua_touserdata(L, 1);
+
+ latom_t *litem = lua_newuserdata(L, sizeof(latom_t) + lv2_atom_total_size(latom->atom));
+ litem->lheader.type = MOONY_UDATA_ATOM;
+ litem->lheader.cache = false;
+ litem->atom = (const LV2_Atom *)litem->payload;
+ litem->body.raw = LV2_ATOM_BODY_CONST(litem->atom);
+
+ litem->payload->size = latom->atom->size;
+ litem->payload->type = latom->atom->type;
+ memcpy(LV2_ATOM_BODY(litem->payload), latom->body.raw, latom->atom->size);
+
+ luaL_getmetatable(L, "latom");
+ lua_setmetatable(L, -2);
+
+ return 1;
+}
+
+__realtime static int
+_latom__gc(lua_State *L)
+{
+ latom_t *latom = lua_touserdata(L, 1);
+
+ if(latom->lheader.type == MOONY_UDATA_STASH)
+ return _lstash__gc(L);
+
+ return 0;
+}
+
+// Nil driver
+__realtime static int
+_latom_nil__len(lua_State *L, latom_t *latom)
+{
+ lua_pushinteger(L, latom->atom->size);
+ return 1;
+}
+
+__realtime static int
+_latom_nil__tostring(lua_State *L, latom_t *latom)
+{
+ lua_pushfstring(L, "(nil: %p)", latom);
+ return 1;
+}
+
+__realtime static inline int
+_latom_nil_value(lua_State *L, latom_t *latom)
+{
+ lua_pushnil(L);
+ return 1;
+}
+
+const latom_driver_t latom_nil_driver = {
+ .__len = _latom_nil__len,
+ .__tostring = _latom_nil__tostring,
+ .value = _latom_nil_value
+};
+
+// Int driver
+__realtime static int
+_latom_int__len(lua_State *L, latom_t *latom)
+{
+ lua_pushinteger(L, latom->atom->size);
+ return 1;
+}
+
+__realtime static int
+_latom_int__tostring(lua_State *L, latom_t *latom)
+{
+ lua_pushfstring(L, "(int: %p, %d)", latom, *latom->body.i32);
+ return 1;
+}
+
+__realtime static inline int
+_latom_int_value(lua_State *L, latom_t *latom)
+{
+ lua_pushinteger(L, *latom->body.i32);
+ return 1;
+}
+
+const latom_driver_t latom_int_driver = {
+ .__len = _latom_int__len,
+ .__tostring = _latom_int__tostring,
+ .value = _latom_int_value
+};
+
+// Long driver
+__realtime static int
+_latom_long__len(lua_State *L, latom_t *latom)
+{
+ lua_pushinteger(L, latom->atom->size);
+ return 1;
+}
+
+static int
+__realtime _latom_long__tostring(lua_State *L, latom_t *latom)
+{
+ lua_pushfstring(L, "(long: %p, %d)", latom, *latom->body.i64);
+ return 1;
+}
+
+__realtime static inline int
+_latom_long_value(lua_State *L, latom_t *latom)
+{
+ lua_pushinteger(L, *latom->body.i64);
+ return 1;
+}
+
+const latom_driver_t latom_long_driver = {
+ .__len = _latom_long__len,
+ .__tostring = _latom_long__tostring,
+ .value = _latom_long_value
+};
+
+// Float driver
+__realtime static int
+_latom_float__len(lua_State *L, latom_t *latom)
+{
+ lua_pushinteger(L, latom->atom->size);
+ return 1;
+}
+
+__realtime static int
+_latom_float__tostring(lua_State *L, latom_t *latom)
+{
+ lua_pushfstring(L, "(float: %p, %f)", latom, *latom->body.f32);
+ return 1;
+}
+
+__realtime static inline int
+_latom_float_value(lua_State *L, latom_t *latom)
+{
+ lua_pushnumber(L, *latom->body.f32);
+ return 1;
+}
+
+const latom_driver_t latom_float_driver = {
+ .__len = _latom_float__len,
+ .__tostring = _latom_float__tostring,
+ .value = _latom_float_value
+};
+
+// Double driver
+__realtime static int
+_latom_double__len(lua_State *L, latom_t *latom)
+{
+ lua_pushinteger(L, latom->atom->size);
+ return 1;
+}
+
+__realtime static int
+_latom_double__tostring(lua_State *L, latom_t *latom)
+{
+ lua_pushfstring(L, "(double: %p, %f)", latom, *latom->body.f64);
+ return 1;
+}
+
+__realtime static inline int
+_latom_double_value(lua_State *L, latom_t *latom)
+{
+ lua_pushnumber(L, *latom->body.f64);
+ return 1;
+}
+
+const latom_driver_t latom_double_driver = {
+ .__len = _latom_double__len,
+ .__tostring = _latom_double__tostring,
+ .value = _latom_double_value
+};
+
+// Bool driver
+__realtime static int
+_latom_bool__len(lua_State *L, latom_t *latom)
+{
+ lua_pushinteger(L, latom->atom->size);
+ return 1;
+}
+
+__realtime static int
+_latom_bool__tostring(lua_State *L, latom_t *latom)
+{
+ if(*latom->body.i32 == 0)
+ lua_pushfstring(L, "(bool: %p, false)", latom);
+ else
+ lua_pushfstring(L, "(bool: %p, true)", latom);
+ return 1;
+}
+
+__realtime static inline int
+_latom_bool_value(lua_State *L, latom_t *latom)
+{
+ lua_pushboolean(L, *latom->body.i32);
+ return 1;
+}
+
+const latom_driver_t latom_bool_driver = {
+ .__len = _latom_bool__len,
+ .__tostring = _latom_bool__tostring,
+ .value = _latom_bool_value
+};
+
+// URID driver
+__realtime static int
+_latom_urid__len(lua_State *L, latom_t *latom)
+{
+ lua_pushinteger(L, latom->atom->size);
+ return 1;
+}
+
+__realtime static int
+_latom_urid__tostring(lua_State *L, latom_t *latom)
+{
+ lua_pushfstring(L, "(URID: %p, %d)", latom, *latom->body.u32);
+ return 1;
+}
+
+__realtime static inline int
+_latom_urid_value(lua_State *L, latom_t *latom)
+{
+ lua_pushinteger(L, *latom->body.u32);
+ return 1;
+}
+
+const latom_driver_t latom_urid_driver = {
+ .__len = _latom_urid__len,
+ .__tostring = _latom_urid__tostring,
+ .value = _latom_urid_value
+};
+
+// String driver
+__realtime static int
+_latom_string__len(lua_State *L, latom_t *latom)
+{
+ lua_pushinteger(L, latom->atom->size);
+ return 1;
+}
+
+__realtime static inline int
+_latom_string_value(lua_State *L, latom_t *latom)
+{
+ lua_pushlstring(L, latom->body.str, latom->atom->size - 1);
+ return 1;
+}
+
+__realtime static int
+_latom_string__tostring(lua_State *L, latom_t *latom)
+{
+ //FIXME URI, Path
+ lua_pushfstring(L, "(string: %p, %s)", latom, latom->body.str);
+ return 1;
+}
+
+const latom_driver_t latom_string_driver = {
+ .__len = _latom_string__len,
+ .__tostring = _latom_string__tostring,
+ .value = _latom_string_value
+};
+
+// Literal driver
+__realtime static int
+_latom_literal__indexk(lua_State *L, latom_t *latom, const char *key)
+{
+ if(!strcmp(key, "datatype"))
+ lua_pushinteger(L, latom->body.lit->datatype);
+ else if(!strcmp(key, "lang"))
+ lua_pushinteger(L, latom->body.lit->lang);
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
+__realtime static int
+_latom_literal_value(lua_State *L, latom_t *latom)
+{
+ lua_pushlstring(L,
+ LV2_ATOM_CONTENTS_CONST(LV2_Atom_Literal_Body, latom->body.lit),
+ latom->atom->size - 1 - sizeof(LV2_Atom_Literal_Body));
+ return 1;
+}
+
+__realtime static int
+_latom_literal__tostring(lua_State *L, latom_t *latom)
+{
+ lua_pushfstring(L, "(literal: %p, %s)", latom,
+ LV2_ATOM_CONTENTS_CONST(LV2_Atom_Literal_Body, latom->body.lit));
+ return 1;
+}
+
+__realtime int
+_latom_literal_unpack(lua_State *L)
+{
+ latom_t *latom = lua_touserdata(L, 1);
+ lua_pushlstring(L,
+ LV2_ATOM_CONTENTS_CONST(LV2_Atom_Literal_Body, latom->body.lit),
+ latom->atom->size - 1 - sizeof(LV2_Atom_Literal_Body));
+ lua_pushinteger(L, latom->body.lit->datatype);
+ lua_pushinteger(L, latom->body.lit->lang);
+ return 3;
+}
+
+const latom_driver_t latom_literal_driver = {
+ .__indexk = _latom_literal__indexk,
+ .__len = _latom_string__len,
+ .__tostring = _latom_literal__tostring,
+ .value = _latom_literal_value,
+ .unpack = _latom_literal_unpack
+};
+
+__realtime static int
+_latom_tuple__indexi(lua_State *L, latom_t *latom)
+{
+ const int idx = lua_tointeger(L, 2);
+
+ int count = 0;
+ LV2_ATOM_TUPLE_BODY_FOREACH(latom->body.tuple, latom->atom->size, atom)
+ {
+ if(++count == idx)
+ {
+ _latom_new(L, atom, latom->lheader.cache);
+ return 1;
+ }
+ }
+
+ lua_pushnil(L);
+ return 1;
+}
+
+__realtime static int
+_latom_tuple__len(lua_State *L, latom_t *latom)
+{
+ int count = 0;
+ LV2_ATOM_TUPLE_BODY_FOREACH(latom->body.tuple, latom->atom->size, atom)
+ ++count;
+
+ lua_pushinteger(L, count);
+ return 1;
+}
+
+__realtime static int
+_latom_tuple__tostring(lua_State *L, latom_t *latom)
+{
+ lua_pushfstring(L, "(tuple: %p)", latom);
+ return 1;
+}
+
+__realtime int
+_latom_tuple_unpack(lua_State *L)
+{
+ latom_t *latom = lua_touserdata(L, 1);
+
+ int n = lua_gettop(L);
+ int min = n > 1
+ ? luaL_checkinteger(L, 2)
+ : 1;
+ int max = n > 2
+ ? luaL_checkinteger(L, 3)
+ : INT_MAX;
+
+ int pos = 1;
+ int count = 0;
+ LV2_ATOM_TUPLE_BODY_FOREACH(latom->body.tuple, latom->atom->size, atom)
+ {
+ if(pos >= min)
+ {
+ if(pos <= max)
+ {
+ _latom_new(L, atom, latom->lheader.cache);
+ count += 1;
+ }
+ else
+ break;
+ }
+
+ pos += 1;
+ }
+
+ return count;
+}
+
+static const LV2_Atom nil_atom = {
+ .size = 0,
+ .type = 0
+};
+
+__realtime static void
+_latom_clear(latom_t *litem)
+{
+ litem->atom = &nil_atom;
+ litem->body.raw = NULL;
+}
+
+__realtime int
+_latom_tuple_foreach_itr(lua_State *L)
+{
+ latom_t *latom = lua_touserdata(L, 1);
+ latom_t *litem = lua_touserdata(L, lua_upvalueindex(2));
+
+ if(!lv2_atom_tuple_is_end(latom->body.tuple, latom->atom->size, latom->iter.tuple.item))
+ {
+ // push index
+ lua_pushinteger(L, latom->iter.tuple.pos);
+ // push atom
+ lua_pushvalue(L, lua_upvalueindex(2));
+ litem->atom = latom->iter.tuple.item;
+ litem->body.raw = LV2_ATOM_BODY_CONST(litem->atom);
+
+ // advance iterator
+ latom->iter.tuple.pos += 1;
+ latom->iter.tuple.item = lv2_atom_tuple_next(latom->iter.tuple.item);
+
+ return 2;
+ }
+
+ // end of tuple reached
+ _latom_clear(litem);
+ lua_pushnil(L);
+ return 1;
+}
+
+__realtime int
+_latom_tuple_foreach(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ latom_t *latom = lua_touserdata(L, 1);
+
+ // reset iterator to beginning of tuple
+ latom->iter.tuple.pos = 1;
+ latom->iter.tuple.item = latom->body.tuple;
+
+ _pushupclosure(L, moony, MOONY_UPCLOSURE_TUPLE_FOREACH, latom->lheader.cache);
+ lua_pushvalue(L, 1);
+
+ return 2;
+}
+
+const latom_driver_t latom_tuple_driver = {
+ .__indexi = _latom_tuple__indexi,
+ .__len = _latom_tuple__len,
+ .__tostring = _latom_tuple__tostring,
+ .unpack = _latom_tuple_unpack,
+ .foreach = _latom_tuple_foreach
+};
+
+__realtime static int
+_latom_obj__indexi(lua_State *L, latom_t *latom)
+{
+ const LV2_URID urid = lua_tointeger(L, 2);
+
+ const LV2_Atom *atom = NULL;
+ lv2_atom_object_body_get(latom->atom->size, latom->body.obj, urid, &atom, 0);
+
+ if(atom) // query returned a matching atom
+ _latom_new(L, atom, latom->lheader.cache);
+ else // query returned no matching atom
+ lua_pushnil(L);
+
+ return 1;
+}
+
+__realtime static int
+_latom_obj__indexk(lua_State *L, latom_t *latom, const char *key)
+{
+ if(!strcmp(key, "id"))
+ lua_pushinteger(L, latom->body.obj->id);
+ else if(!strcmp(key, "otype"))
+ lua_pushinteger(L, latom->body.obj->otype);
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
+__realtime static int
+_latom_obj__len(lua_State *L, latom_t *latom)
+{
+ int count = 0;
+ LV2_ATOM_OBJECT_BODY_FOREACH(latom->body.obj, latom->atom->size, prop)
+ ++count;
+
+ lua_pushinteger(L, count);
+ return 1;
+}
+
+__realtime static int
+_latom_obj__tostring(lua_State *L, latom_t *latom)
+{
+ lua_pushfstring(L, "(object: %p)", latom);
+ return 1;
+}
+
+__realtime int
+_latom_obj_foreach_itr(lua_State *L)
+{
+ latom_t *latom = lua_touserdata(L, 1);
+ latom_t *litem = lua_touserdata(L, lua_upvalueindex(2));
+
+ if(!lv2_atom_object_is_end(latom->body.obj, latom->atom->size, latom->iter.obj.prop))
+ {
+ // push key
+ lua_pushinteger(L, latom->iter.obj.prop->key);
+ // push atom
+
+ lua_pushvalue(L, lua_upvalueindex(2));
+ litem->atom = &latom->iter.obj.prop->value;
+ litem->body.raw = LV2_ATOM_BODY_CONST(litem->atom);
+
+ // push context
+ lua_pushinteger(L, latom->iter.obj.prop->context);
+
+ // advance iterator
+ latom->iter.obj.prop = lv2_atom_object_next(latom->iter.obj.prop);
+
+ return 3;
+ }
+
+ // end of object reached
+ _latom_clear(litem);
+ lua_pushnil(L);
+ return 1;
+}
+
+__realtime int
+_latom_obj_foreach(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ latom_t *latom = lua_touserdata(L, 1);
+
+ // reset iterator to beginning of object
+ latom->iter.obj.prop = lv2_atom_object_begin(latom->body.obj);
+
+ _pushupclosure(L, moony, MOONY_UPCLOSURE_OBJECT_FOREACH, latom->lheader.cache);
+ lua_pushvalue(L, 1);
+
+ return 2;
+}
+
+const latom_driver_t latom_object_driver = {
+ .__indexi = _latom_obj__indexi,
+ .__indexk = _latom_obj__indexk,
+ .__len = _latom_obj__len,
+ .__tostring = _latom_obj__tostring,
+ .foreach = _latom_obj_foreach
+};
+
+__realtime static int
+_latom_seq__indexk(lua_State *L, latom_t *latom, const char *key)
+{
+ if(!strcmp(key, "unit"))
+ lua_pushinteger(L, latom->body.seq->unit);
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
+__realtime static int
+_latom_seq__indexi(lua_State *L, latom_t *latom)
+{
+ int index = lua_tointeger(L, 2); // indexing start from 1
+
+ int count = 0;
+ LV2_ATOM_SEQUENCE_BODY_FOREACH(latom->body.seq, latom->atom->size, ev)
+ {
+ if(++count == index)
+ {
+ _latom_new(L, &ev->body, latom->lheader.cache);
+ return 1;
+ }
+ }
+
+ lua_pushnil(L);
+ return 1;
+}
+
+__realtime static int
+_latom_seq__len(lua_State *L, latom_t *latom)
+{
+ int count = 0;
+ LV2_ATOM_SEQUENCE_BODY_FOREACH(latom->body.seq, latom->atom->size, ev)
+ ++count;
+
+ lua_pushinteger(L, count);
+ return 1;
+}
+
+__realtime static int
+_latom_seq__tostring(lua_State *L, latom_t *latom)
+{
+ lua_pushfstring(L, "(sequence: %p)", latom);
+ return 1;
+}
+
+__realtime int
+_latom_seq_multiplex_itr(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ const unsigned n = lua_rawlen(L, 1);
+ latom_t *latom [n];
+ latom_t *litem = lua_touserdata(L, lua_upvalueindex(2));
+
+ // fill latom* array
+ for(unsigned i=0; i<n; i++)
+ {
+ lua_rawgeti(L, 1, 1+i);
+ latom[i] = lua_touserdata(L, -1);
+ }
+ lua_pop(L, n);
+
+ double huge = HUGE_VAL;
+ int nxt = -1;
+ for(unsigned i=0; i<n; i++)
+ {
+ if(lv2_atom_sequence_is_end(latom[i]->body.seq, latom[i]->atom->size, latom[i]->iter.seq.ev))
+ continue;
+
+ if(latom[i]->body.seq->unit == moony->uris.atom_beat_time)
+ {
+ if(latom[i]->iter.seq.ev->time.beats < huge)
+ {
+ huge = latom[i]->iter.seq.ev->time.beats;
+ nxt = i;
+ }
+ }
+ else
+ {
+ if(latom[i]->iter.seq.ev->time.frames < huge)
+ {
+ huge = latom[i]->iter.seq.ev->time.frames;
+ nxt = i;
+ }
+ }
+ }
+
+ if(nxt >= 0) // is there a valid next event?
+ {
+ if(latom[nxt]->body.seq->unit == moony->uris.atom_beat_time)
+ lua_pushnumber(L, latom[nxt]->iter.seq.ev->time.beats);
+ else
+ lua_pushinteger(L, latom[nxt]->iter.seq.ev->time.frames);
+
+ // push atom
+ lua_pushvalue(L, lua_upvalueindex(2));
+ litem->atom = &latom[nxt]->iter.seq.ev->body;
+ litem->body.raw = LV2_ATOM_BODY_CONST(litem->atom);
+ lua_rawgeti(L, 1, 1+nxt);
+
+ // advance iterator
+ latom[nxt]->iter.seq.ev = lv2_atom_sequence_next(latom[nxt]->iter.seq.ev);
+
+ return 3;
+ }
+
+ // end of sequence reached
+ _latom_clear(litem);
+ lua_pushnil(L);
+ return 1;
+}
+
+__realtime static int
+_latom_seq_multiplex(lua_State *L, unsigned n)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ latom_t *latom = lua_touserdata(L, 1);
+
+ for(unsigned i=1; i<=n; i++)
+ {
+ latom_t *lmux = lua_touserdata(L, i);
+
+ // reset iterator to beginning of sequence
+ lmux->iter.seq.ev = lv2_atom_sequence_begin(lmux->body.seq);
+ }
+
+ _pushupclosure(L, moony, MOONY_UPCLOSURE_SEQUENCE_MULTIPLEX, latom->lheader.cache);
+
+ lua_createtable(L, n, 0); //FIXME cache this?
+ for(unsigned i=1; i<=n; i++)
+ {
+ lua_pushvalue(L, i);
+ lua_rawseti(L, -2, i);
+ }
+
+ return 2;
+}
+
+__realtime int
+_latom_seq_foreach_itr(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ latom_t *latom = lua_touserdata(L, 1);
+ latom_t *litem = lua_touserdata(L, lua_upvalueindex(2));
+
+ if(!lv2_atom_sequence_is_end(latom->body.seq, latom->atom->size, latom->iter.seq.ev))
+ {
+ if(latom->body.seq->unit == moony->uris.atom_beat_time)
+ lua_pushnumber(L, latom->iter.seq.ev->time.beats);
+ else
+ lua_pushinteger(L, latom->iter.seq.ev->time.frames);
+
+ // push atom
+ lua_pushvalue(L, lua_upvalueindex(2));
+ litem->atom = &latom->iter.seq.ev->body;
+ litem->body.raw = LV2_ATOM_BODY_CONST(litem->atom);
+
+ // advance iterator
+ latom->iter.seq.ev = lv2_atom_sequence_next(latom->iter.seq.ev);
+
+ return 2;
+ }
+
+ // end of sequence reached
+ _latom_clear(litem);
+ lua_pushnil(L);
+ return 1;
+}
+
+__realtime int
+_latom_seq_foreach(lua_State *L)
+{
+ const unsigned n = lua_gettop(L);
+ if(n > 1) // multiplex if given any function arguments
+ return _latom_seq_multiplex(L, n);
+
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ latom_t *latom = lua_touserdata(L, 1);
+
+ // reset iterator to beginning of sequence
+ latom->iter.seq.ev = lv2_atom_sequence_begin(latom->body.seq);
+
+ _pushupclosure(L, moony, MOONY_UPCLOSURE_SEQUENCE_FOREACH, latom->lheader.cache);
+ lua_pushvalue(L, 1);
+
+ return 2;
+}
+
+const latom_driver_t latom_sequence_driver = {
+ .__indexk = _latom_seq__indexk,
+ .__indexi = _latom_seq__indexi,
+ .__len = _latom_seq__len,
+ .__tostring = _latom_seq__tostring,
+ .foreach = _latom_seq_foreach
+};
+
+__realtime static int
+_latom_vec__indexi(lua_State *L, latom_t *latom)
+{
+ int index = lua_tointeger(L, 2); // indexing start from 1
+
+ const int count = (latom->atom->size - sizeof(LV2_Atom_Vector_Body))
+ / latom->body.vec->child_size;
+
+ if( (index > 0) && (index <= count) )
+ {
+ latom_t *litem = _latom_new(L, NULL, latom->lheader.cache);
+ litem->atom = (const LV2_Atom *)latom->body.vec;
+ litem->body.raw = LV2_ATOM_VECTOR_BODY_ITEM_CONST(latom->body.vec, index - 1);
+ }
+ else // index is out of bounds
+ lua_pushnil(L);
+
+ return 1;
+}
+
+__realtime static int
+_latom_vec__indexk(lua_State *L, latom_t *latom, const char *key)
+{
+ if(!strcmp(key, "childType"))
+ lua_pushinteger(L, latom->body.vec->child_type);
+ else if(!strcmp(key, "childSize"))
+ lua_pushinteger(L, latom->body.vec->child_size);
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
+__realtime static int
+_latom_vec__len(lua_State *L, latom_t *latom)
+{
+ const int count = (latom->atom->size - sizeof(LV2_Atom_Vector_Body))
+ / latom->body.vec->child_size;
+
+ lua_pushinteger(L, count);
+ return 1;
+}
+
+__realtime static int
+_latom_vec__tostring(lua_State *L, latom_t *latom)
+{
+ lua_pushfstring(L, "(vector: %p)", latom);
+ return 1;
+}
+
+__realtime static int
+_latom_vec_value(lua_State *L, latom_t *latom)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ const int n = (latom->atom->size - sizeof(LV2_Atom_Vector_Body))
+ / latom->body.vec->child_size;
+ const void *ptr = LV2_ATOM_VECTOR_BODY_ITEM_CONST(latom->body.vec, 0);
+
+ lua_createtable(L, n, 0);
+
+ if(latom->body.vec->child_type == moony->forge.Bool)
+ {
+ const int32_t *i32 = ptr;
+ for(int i=0; i<n; i++)
+ {
+ lua_pushboolean(L, i32[i]);
+ lua_rawseti(L, -2, i+1);
+ }
+ }
+ else if(latom->body.vec->child_type == moony->forge.Int)
+ {
+ const int32_t *i32 = ptr;
+ for(int i=0; i<n; i++)
+ {
+ lua_pushinteger(L, i32[i]);
+ lua_rawseti(L, -2, i+1);
+ }
+ }
+ else if(latom->body.vec->child_type == moony->forge.URID)
+ {
+ const uint32_t *u32 = ptr;
+ for(int i=0; i<n; i++)
+ {
+ lua_pushinteger(L, u32[i]);
+ lua_rawseti(L, -2, i+1);
+ }
+ }
+ else if(latom->body.vec->child_type == moony->forge.Long)
+ {
+ const int64_t *i64 = ptr;
+ for(int i=0; i<n; i++)
+ {
+ lua_pushinteger(L, i64[i]);
+ lua_rawseti(L, -2, i+1);
+ }
+ }
+ else if(latom->body.vec->child_type == moony->forge.Float)
+ {
+ const float *f32 = ptr;
+ for(int i=0; i<n; i++)
+ {
+ lua_pushnumber(L, f32[i]);
+ lua_rawseti(L, -2, i+1);
+ }
+ }
+ else if(latom->body.vec->child_type == moony->forge.Double)
+ {
+ const double *f64 = ptr;
+ for(int i=0; i<n; i++)
+ {
+ lua_pushnumber(L, f64[i]);
+ lua_rawseti(L, -2, i+1);
+ }
+ }
+
+ return 1;
+}
+
+__realtime int
+_latom_vec_unpack(lua_State *L)
+{
+ latom_t *latom = lua_touserdata(L, 1);
+
+ const int count = (latom->atom->size - sizeof(LV2_Atom_Vector_Body))
+ / latom->body.vec->child_size;
+
+ const int n = lua_gettop(L);
+ int min = 1;
+ int max = count;
+
+ if(n > 1) // check provided index ranges
+ {
+ min = luaL_checkinteger(L, 2);
+ min = min < 1
+ ? 1
+ : (min > count
+ ? count
+ : min);
+
+ if(n > 2)
+ {
+ max = luaL_checkinteger(L, 3);
+ max = max < 1
+ ? 1
+ : (max > count
+ ? count
+ : max);
+ }
+ }
+
+ for(int i=min; i<=max; i++)
+ {
+ latom_t *litem = _latom_new(L, NULL, latom->lheader.cache);
+ litem->atom = (const LV2_Atom *)latom->body.vec;
+ litem->body.raw = LV2_ATOM_VECTOR_BODY_ITEM_CONST(latom->body.vec, i - 1);
+ }
+
+ return max - min + 1;
+}
+
+__realtime int
+_latom_vec_foreach_itr(lua_State *L)
+{
+ latom_t *latom = lua_touserdata(L, 1);
+ latom_t *litem = lua_touserdata(L, lua_upvalueindex(2));
+
+ if(latom->iter.vec.pos < latom->iter.vec.count)
+ {
+ // push index
+ lua_pushinteger(L, latom->iter.vec.pos + 1);
+ // push atom
+ lua_pushvalue(L, lua_upvalueindex(2));
+ litem->atom = (const LV2_Atom *)latom->body.vec;
+ litem->body.raw = LV2_ATOM_VECTOR_BODY_ITEM_CONST(latom->body.vec, latom->iter.vec.pos);
+
+ // advance iterator
+ latom->iter.vec.pos += 1;
+
+ return 2;
+ }
+
+ // end of vector reached
+ _latom_clear(litem);
+ lua_pushnil(L);
+ return 1;
+}
+
+__realtime int
+_latom_vec_foreach(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ latom_t *latom = lua_touserdata(L, 1);
+
+ // reset iterator to beginning of vector
+ latom->iter.vec.count = (latom->atom->size - sizeof(LV2_Atom_Vector_Body))
+ / latom->body.vec->child_size;
+ latom->iter.vec.pos = 0;
+
+ _pushupclosure(L, moony, MOONY_UPCLOSURE_VECTOR_FOREACH, latom->lheader.cache);
+ lua_pushvalue(L, 1);
+
+ return 2;
+}
+
+const latom_driver_t latom_vector_driver = {
+ .__indexi = _latom_vec__indexi,
+ .__indexk = _latom_vec__indexk,
+ .__len = _latom_vec__len,
+ .__tostring = _latom_vec__tostring,
+ .value = _latom_vec_value,
+ .unpack = _latom_vec_unpack,
+ .foreach = _latom_vec_foreach
+};
+
+__realtime static int
+_latom_chunk__indexi(lua_State *L, latom_t *latom)
+{
+ const uint8_t *payload = latom->body.raw;
+ int index = lua_tointeger(L, 2); // indexing start from 1
+
+ if( (index > 0) && (index <= (int)latom->atom->size) )
+ lua_pushinteger(L, payload[index-1]);
+ else // index is out of bounds
+ lua_pushnil(L);
+
+ return 1;
+}
+
+__realtime static int
+_latom_chunk__len(lua_State *L, latom_t *latom)
+{
+ lua_pushinteger(L, latom->atom->size);
+ return 1;
+}
+
+__realtime static int
+_latom_chunk__tostring(lua_State *L, latom_t *latom)
+{
+ lua_pushfstring(L, "(chunk: %p)", latom);
+ return 1;
+}
+
+__realtime static int
+_latom_chunk_value(lua_State *L, latom_t *latom)
+{
+ lua_pushlstring(L, latom->body.raw, latom->atom->size);
+
+ return 1;
+}
+
+__realtime int
+_latom_chunk_unpack(lua_State *L)
+{
+ latom_t *latom = lua_touserdata(L, 1);
+ const uint8_t *payload = latom->body.raw;
+
+ int n = lua_gettop(L);
+ int min = 1;
+ int max = latom->atom->size;
+
+ if(n > 1) // check provided index ranges
+ {
+ min = luaL_checkinteger(L, 2);
+ min = min < 1
+ ? 1
+ : (min > (int)latom->atom->size
+ ? (int)latom->atom->size
+ : min);
+
+ if(n > 2)
+ {
+ max = luaL_checkinteger(L, 3);
+ max = max < 1
+ ? 1
+ : (max > (int)latom->atom->size
+ ? (int)latom->atom->size
+ : max);
+ }
+ }
+
+ for(int i=min; i<=max; i++)
+ lua_pushinteger(L, payload[i-1]);
+
+ return max - min + 1;
+}
+
+const latom_driver_t latom_chunk_driver = {
+ .__indexi = _latom_chunk__indexi,
+ .__len = _latom_chunk__len,
+ .__tostring = _latom_chunk__tostring,
+ .value = _latom_chunk_value,
+ .unpack = _latom_chunk_unpack
+};
+
+__realtime static int
+_latom__index(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ latom_t *latom = lua_touserdata(L, 1);
+ const latom_driver_t *driver = _latom_driver(moony, latom->atom->type);
+
+ if(driver)
+ {
+ const int type = lua_type(L, 2);
+ if(type == LUA_TSTRING)
+ {
+ const char *key = lua_tostring(L, 2);
+ if(!strcmp(key, "type"))
+ {
+ lua_pushinteger(L, latom->atom->type);
+ return 1;
+ }
+ else if(driver->value && !strcmp(key, "body"))
+ {
+ return driver->value(L, latom);
+ }
+ else if(driver->foreach && !strcmp(key, "foreach"))
+ {
+ lua_rawgetp(L, LUA_REGISTRYINDEX, driver->foreach);
+ return 1;
+ }
+ else if(driver->unpack && !strcmp(key, "unpack"))
+ {
+ lua_rawgetp(L, LUA_REGISTRYINDEX, driver->unpack);
+ return 1;
+ }
+ else if(!strcmp(key, "clone"))
+ {
+ lua_rawgetp(L, LUA_REGISTRYINDEX, _latom_clone);
+ return 1;
+ }
+ else if(!strcmp(key, "raw"))
+ {
+ lua_pushlstring(L, latom->body.raw, latom->atom->size);
+ return 1;
+ }
+ else if( (latom->lheader.type == MOONY_UDATA_STASH) && !strcmp(key, "write") )
+ {
+ lua_rawgetp(L, LUA_REGISTRYINDEX, _lstash_write);
+ return 1;
+ }
+ else if( (latom->lheader.type == MOONY_UDATA_STASH) && !strcmp(key, "read") )
+ {
+ lua_rawgetp(L, LUA_REGISTRYINDEX, _lstash_read);
+ return 1;
+ }
+ else if(driver->__indexk)
+ {
+ return driver->__indexk(L, latom, key);
+ }
+ }
+ else if(driver->__indexi && (type == LUA_TNUMBER) )
+ {
+ return driver->__indexi(L, latom);
+ }
+ }
+
+ lua_pushnil(L);
+ return 1;
+}
+
+__realtime static int
+_latom__len(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ latom_t *latom = lua_touserdata(L, 1);
+ const latom_driver_t *driver = _latom_driver(moony, latom->atom->type);
+
+ if(driver && driver->__len)
+ return driver->__len(L, latom);
+
+ lua_pushinteger(L, latom->atom->size);
+ return 1;
+}
+
+__realtime static int
+_latom__tostring(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ latom_t *latom = lua_touserdata(L, 1);
+ const latom_driver_t *driver = _latom_driver(moony, latom->atom->type);
+
+ if(driver && driver->__tostring)
+ return driver->__tostring(L, latom);
+
+ lua_pushnil(L);
+ return 1;
+}
+
+__realtime static int
+_latom__eq(lua_State *L)
+{
+ //moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ latom_t *latom1 = lua_touserdata(L, 1);
+ latom_t *latom2 = luaL_checkudata(L, 2, "latom");
+
+ lua_pushboolean(L,
+ (latom1->atom->type == latom2->atom->type)
+ && (latom1->atom->size == latom2->atom->size)
+ && (memcmp(latom1->body.raw, latom2->body.raw, latom1->atom->size) == 0) );
+
+ return 1;
+}
+
+const luaL_Reg latom_mt [] = {
+ {"__index", _latom__index},
+ {"__len", _latom__len},
+ {"__tostring", _latom__tostring},
+ {"__eq", _latom__eq},
+ {"__gc", _latom__gc},
+
+ {NULL, NULL}
+};
diff --git a/api/api_atom.h b/api/api_atom.h
new file mode 100644
index 0000000..7b6790d
--- /dev/null
+++ b/api/api_atom.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _MOONY_API_ATOM_H
+#define _MOONY_API_ATOM_H
+
+#include <moony.h>
+
+typedef struct _lseq_t lseq_t;
+typedef struct _latom_t latom_t;
+typedef struct _lobj_t lobj_t;
+typedef struct _ltuple_t ltuple_t;
+typedef struct _lvec_t lvec_t;
+
+typedef int (*latom_driver_function_t)(lua_State *L, latom_t *latom);
+typedef int (*latom_driver_function_indexk_t)(lua_State *L, latom_t *latom, const char *key);
+
+struct _latom_driver_t {
+ latom_driver_function_t __indexi;
+ latom_driver_function_indexk_t __indexk;
+ latom_driver_function_t __len;
+ latom_driver_function_t __tostring;
+ latom_driver_function_t __call;
+
+ latom_driver_function_t value;
+ lua_CFunction unpack;
+ lua_CFunction foreach;
+};
+
+struct _latom_t {
+ lheader_t lheader;
+
+ const LV2_Atom *atom;
+
+ union {
+ const void *raw;
+
+ const int32_t *i32;
+ const int64_t *i64;
+ const float *f32;
+ const double *f64;
+ const uint32_t *u32;
+ const char *str;
+ const LV2_Atom_Literal_Body *lit;
+
+ const LV2_Atom_Sequence_Body *seq;
+ const LV2_Atom_Object_Body *obj;
+ const LV2_Atom *tuple;
+ const LV2_Atom_Vector_Body *vec;
+ } body;
+
+ union {
+ struct _lseq_t {
+ const LV2_Atom_Event *ev;
+ } seq;
+
+ struct _lobj_t {
+ const LV2_Atom_Property_Body *prop;
+ } obj;
+
+ struct _ltuple_t {
+ int pos;
+ const LV2_Atom *item;
+ } tuple;
+
+ struct _lvec_t {
+ int count;
+ int pos;
+ } vec;
+ } iter;
+
+ LV2_Atom payload [0];
+};
+
+// in api_atom.c
+extern const latom_driver_t latom_nil_driver;
+extern const latom_driver_t latom_bool_driver;
+extern const latom_driver_t latom_int_driver;
+extern const latom_driver_t latom_long_driver;
+extern const latom_driver_t latom_float_driver;
+extern const latom_driver_t latom_double_driver;
+extern const latom_driver_t latom_urid_driver;
+extern const latom_driver_t latom_string_driver;
+extern const latom_driver_t latom_literal_driver;
+extern const latom_driver_t latom_tuple_driver;
+extern const latom_driver_t latom_object_driver;
+extern const latom_driver_t latom_vector_driver;
+extern const latom_driver_t latom_sequence_driver;
+extern const latom_driver_t latom_chunk_driver;
+
+extern const luaL_Reg latom_mt [];
+
+extern const lua_CFunction upclosures [];
+
+int
+_latom_clone(lua_State *L);
+
+int
+_latom_literal_unpack(lua_State *L);
+int
+_latom_tuple_unpack(lua_State *L);
+int
+_latom_vec_unpack(lua_State *L);
+int
+_latom_chunk_unpack(lua_State *L);
+
+int
+_latom_tuple_foreach(lua_State *L);
+int
+_latom_obj_foreach(lua_State *L);
+int
+_latom_seq_foreach(lua_State *L);
+int
+_latom_vec_foreach(lua_State *L);
+
+int
+_latom_tuple_foreach_itr(lua_State *L);
+int
+_latom_obj_foreach_itr(lua_State *L);
+int
+_latom_seq_foreach_itr(lua_State *L);
+int
+_latom_vec_foreach_itr(lua_State *L);
+
+int
+_latom_seq_multiplex_itr(lua_State *L);
+
+__realtime static inline latom_t *
+_latom_body_new(lua_State *L, const LV2_Atom *atom, const void *body, bool cache)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ latom_t *latom = moony_newuserdata(L, moony, MOONY_UDATA_ATOM, cache);
+ if(atom)
+ {
+ latom->atom = atom;
+ latom->body.raw = body;
+ }
+
+ return latom;
+}
+
+__realtime static inline latom_t *
+_latom_new(lua_State *L, const LV2_Atom *atom, bool cache)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ latom_t *latom = moony_newuserdata(L, moony, MOONY_UDATA_ATOM, cache);
+ if(atom)
+ {
+ latom->atom = atom;
+ latom->body.raw = LV2_ATOM_BODY_CONST(atom);
+ }
+
+ return latom;
+}
+
+__realtime static inline const latom_driver_t *
+_latom_driver(moony_t *moony, LV2_URID type)
+{
+ const latom_driver_hash_t *base = moony->atom_driver_hash;
+
+ for(unsigned N = DRIVER_HASH_MAX, half; N > 1; N -= half)
+ {
+ half = N/2;
+ const latom_driver_hash_t *dst = &base[half];
+ base = (dst->type > type) ? base : dst;
+ }
+
+ return (base->type == type) ? base->driver : &latom_chunk_driver;
+}
+
+__realtime static void
+_latom_value(lua_State *L, const LV2_Atom *atom)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ const latom_driver_t *driver = _latom_driver(moony, atom->type);
+
+ // dummy wrapping
+ latom_t latom = {
+ .atom = atom,
+ .body.raw = LV2_ATOM_BODY_CONST(atom),
+ };
+
+ if(driver && driver->value)
+ driver->value(L, &latom);
+ else
+ lua_pushnil(L); // unknown type
+}
+
+#endif
diff --git a/api/api_forge.c b/api/api_forge.c
new file mode 100644
index 0000000..173004e
--- /dev/null
+++ b/api/api_forge.c
@@ -0,0 +1,2152 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <math.h>
+
+#include <api_atom.h>
+#include <api_forge.h>
+#include <api_stash.h>
+
+#include <osc.lv2/forge.h>
+
+__realtime static void
+_lforge_pop_inlined(lua_State *L, lforge_t *lforge)
+{
+ for(int i=lforge->depth; i>0; i--)
+ {
+ if(&lforge->frame[i-1] == lforge->forge->stack) // intercept assert
+ lv2_atom_forge_pop(lforge->forge, &lforge->frame[i-1]);
+ else
+ luaL_error(L, "forge frame mismatch");
+ }
+ lforge->depth = 0; // reset depth
+}
+
+__realtime static inline int
+_lforge_frame_time_inlined(lua_State *L, lforge_t *lforge, int64_t frames)
+{
+ if(frames >= lforge->last.frames)
+ {
+ if(!lv2_atom_forge_frame_time(lforge->forge, frames))
+ luaL_error(L, forge_buffer_overflow);
+ lforge->last.frames = frames;
+
+ lua_settop(L, 1);
+ return 1;
+ }
+
+ return luaL_error(L, "invalid frame time, must not decrease");
+}
+
+__realtime static int
+_lforge_frame_time(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+ int64_t frames = luaL_checkinteger(L, 2);
+
+ return _lforge_frame_time_inlined(L, lforge, frames);
+}
+
+__realtime static inline int
+_lforge_beat_time_inlined(lua_State *L, lforge_t *lforge, double beats)
+{
+ if(beats >= lforge->last.beats)
+ {
+ if(!lv2_atom_forge_beat_time(lforge->forge, beats))
+ luaL_error(L, forge_buffer_overflow);
+ lforge->last.beats = beats;
+
+ lua_settop(L, 1);
+ return 1;
+ }
+
+ return luaL_error(L, "invalid beat time, must not decrease");
+}
+
+__realtime static int
+_lforge_beat_time(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+ double beats = luaL_checknumber(L, 2);
+
+ return _lforge_beat_time_inlined(L, lforge, beats);
+}
+
+__realtime static int
+_lforge_time(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(lua_isinteger(L, 2))
+ {
+ int64_t frames = lua_tointeger(L, 2);
+
+ return _lforge_frame_time_inlined(L, lforge, frames);
+ }
+ else if(lua_isnumber(L, 2))
+ {
+ double beats = lua_tonumber(L, 2);
+
+ return _lforge_beat_time_inlined(L, lforge, beats);
+ }
+
+ return luaL_error(L, "integer or number expected");
+}
+
+__realtime static int
+_lforge_atom(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+ latom_t *latom = luaL_checkudata(L, 2, "latom");
+ const uint32_t size = latom->atom->size;
+
+ if(!lv2_atom_forge_atom(lforge->forge, size, latom->atom->type))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_raw(lforge->forge, latom->body.raw, size))
+ luaL_error(L, forge_buffer_overflow);
+ lv2_atom_forge_pad(lforge->forge, size);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static inline LV2_Atom_Forge_Ref
+_lforge_basic_int(lua_State *L, int pos, LV2_Atom_Forge *forge)
+{
+ int32_t val = luaL_checkinteger(L, pos);
+ return lv2_atom_forge_int(forge, val);
+}
+
+__realtime static inline LV2_Atom_Forge_Ref
+_lforge_basic_long(lua_State *L, int pos, LV2_Atom_Forge *forge)
+{
+ int64_t val = luaL_checkinteger(L, pos);
+ return lv2_atom_forge_long(forge, val);
+}
+
+__realtime static inline LV2_Atom_Forge_Ref
+_lforge_basic_float(lua_State *L, int pos, LV2_Atom_Forge *forge)
+{
+ float val = luaL_checknumber(L, pos);
+ return lv2_atom_forge_float(forge, val);
+}
+
+__realtime static inline LV2_Atom_Forge_Ref
+_lforge_basic_double(lua_State *L, int pos, LV2_Atom_Forge *forge)
+{
+ double val = luaL_checknumber(L, pos);
+ return lv2_atom_forge_double(forge, val);
+}
+
+__realtime static inline LV2_Atom_Forge_Ref
+_lforge_basic_bool(lua_State *L, int pos, LV2_Atom_Forge *forge)
+{
+ int32_t val = lua_toboolean(L, pos);
+ return lv2_atom_forge_bool(forge, val);
+}
+
+__realtime static inline LV2_Atom_Forge_Ref
+_lforge_basic_urid(lua_State *L, int pos, LV2_Atom_Forge *forge)
+{
+ LV2_URID val = luaL_checkinteger(L, pos);
+ return lv2_atom_forge_urid(forge, val);
+}
+
+__realtime static inline LV2_Atom_Forge_Ref
+_lforge_basic_string(lua_State *L, int pos, LV2_Atom_Forge *forge)
+{
+ size_t len;
+ const char *val = luaL_checklstring(L, pos, &len);
+ return lv2_atom_forge_string(forge, val, len);
+}
+
+__realtime static inline LV2_Atom_Forge_Ref
+_lforge_basic_uri(lua_State *L, int pos, LV2_Atom_Forge *forge)
+{
+ size_t len;
+ const char *val = luaL_checklstring(L, pos, &len);
+ return lv2_atom_forge_uri(forge, val, len);
+}
+
+__realtime static inline LV2_Atom_Forge_Ref
+_lforge_basic_path(lua_State *L, int pos, LV2_Atom_Forge *forge)
+{
+ size_t len;
+ const char *val = luaL_checklstring(L, pos, &len);
+ return lv2_atom_forge_path(forge, val, len);
+}
+
+__realtime static inline LV2_Atom_Forge_Ref
+_lforge_basic_literal(lua_State *L, int pos, LV2_Atom_Forge *forge)
+{
+ size_t len;
+ const char *val = luaL_checklstring(L, pos, &len);
+ return lv2_atom_forge_literal(forge, val, len, 0, 0); //TODO context, lang
+}
+
+__realtime static int
+_lforge_basic_bytes(lua_State *L, int pos, LV2_Atom_Forge *forge, LV2_URID type)
+{
+ int ltype = lua_type(L, pos);
+
+ if(ltype == LUA_TSTRING)
+ {
+ size_t size;
+ const char *str = lua_tolstring(L, pos, &size);
+ if(!lv2_atom_forge_atom(forge, size, type))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_raw(forge, str, size))
+ luaL_error(L, forge_buffer_overflow);
+ lv2_atom_forge_pad(forge, size);
+ }
+ else if(ltype == LUA_TTABLE)
+ {
+ int size = lua_rawlen(L, pos);
+ if(!lv2_atom_forge_atom(forge, size, type))
+ luaL_error(L, forge_buffer_overflow);
+ for(int i=1; i<=size; i++)
+ {
+ lua_rawgeti(L, pos, i);
+ uint8_t byte = luaL_checkinteger(L, -1);
+ lua_pop(L, 1);
+ if(!lv2_atom_forge_raw(forge, &byte, 1))
+ luaL_error(L, forge_buffer_overflow);
+ }
+ lv2_atom_forge_pad(forge, size);
+ }
+ else if(luaL_testudata(L, pos, "latom")) //to convert between chunk <-> midi
+ {
+ latom_t *latom = lua_touserdata(L, pos);
+ const uint32_t size = latom->atom->size;
+ if(!lv2_atom_forge_atom(forge, size, type))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_raw(forge, latom->body.raw, size))
+ luaL_error(L, forge_buffer_overflow);
+ lv2_atom_forge_pad(forge, size);
+ }
+ else // bytes as individual function arguments
+ {
+ const uint32_t size = lua_gettop(L) - (pos - 1);
+ if(!lv2_atom_forge_atom(forge, size, type))
+ luaL_error(L, forge_buffer_overflow);
+ for(unsigned i=0; i<size; i++)
+ {
+ const uint8_t byte = luaL_checkinteger(L, pos + i);
+ if(!lv2_atom_forge_raw(forge, &byte, 1))
+ luaL_error(L, forge_buffer_overflow);
+ }
+ lv2_atom_forge_pad(forge, size);
+ }
+
+ return 1;
+}
+
+__realtime static inline LV2_Atom_Forge_Ref
+_lforge_basic_chunk(lua_State *L, int pos, LV2_Atom_Forge *forge)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ return _lforge_basic_bytes(L, pos, forge, moony->forge.Chunk);
+}
+
+__realtime static inline LV2_Atom_Forge_Ref
+_lforge_basic_midi(lua_State *L, int pos, LV2_Atom_Forge *forge)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ return _lforge_basic_bytes(L, pos, forge, moony->uris.midi_event);
+}
+
+__realtime static int
+_lforge_basic_atom(lua_State *L, int pos, LV2_Atom_Forge *forge, LV2_URID range)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ latom_t *latom = luaL_checkudata(L, pos, "latom");
+
+ if(latom->atom->type != range)
+ {
+ luaL_error(L, "%s: atom type mismatch", __func__);
+ }
+
+ if( !lv2_atom_forge_atom(forge, latom->atom->size, latom->atom->type)
+ || !lv2_atom_forge_write(forge, latom->body.raw, latom->atom->size) )
+ {
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ return 1;
+}
+
+__realtime static inline uint32_t
+_lforge_basic_vector_child_size(LV2_Atom_Forge *forge, LV2_URID child_type)
+{
+ if( (child_type == forge->Bool)
+ || (child_type == forge->Int)
+ || (child_type == forge->Float)
+ || (child_type == forge->URID) )
+ {
+ return 4;
+ }
+ else if( (child_type == forge->Long)
+ || (child_type == forge->Double) )
+ {
+ return 8;
+ }
+
+ return 0; // failed
+}
+
+__realtime static inline LV2_Atom_Forge_Ref
+_lforge_basic_vector_item(lua_State *L, int pos, LV2_Atom_Forge *forge,
+ LV2_URID child_type)
+{
+ if(child_type == forge->Bool)
+ {
+ return lv2_atom_forge_bool(forge, lua_toboolean(L, pos));
+ }
+ else if(child_type == forge->Int)
+ {
+ return lv2_atom_forge_int(forge, luaL_checkinteger(L, pos));
+ }
+ else if(child_type == forge->Float)
+ {
+ return lv2_atom_forge_float(forge, luaL_checknumber(L, pos));
+ }
+ else if(child_type == forge->URID)
+ {
+ return lv2_atom_forge_urid(forge, luaL_checkinteger(L, pos));
+ }
+ else if(child_type == forge->Long)
+ {
+ return lv2_atom_forge_long(forge, luaL_checkinteger(L, pos));
+ }
+ else if(child_type == forge->Double)
+ {
+ return lv2_atom_forge_double(forge, luaL_checknumber(L, pos));
+ }
+
+ return 0; // failed
+}
+
+__realtime static inline LV2_Atom_Forge_Ref
+_lforge_basic_vector(lua_State *L, int pos, LV2_Atom_Forge *forge,
+ uint32_t child_size, LV2_URID child_type)
+{
+ luaL_checktype(L, pos, LUA_TTABLE);
+
+ LV2_Atom_Forge_Frame frame;
+ LV2_Atom_Forge_Ref ref = lv2_atom_forge_vector_head(forge, &frame,
+ child_size, child_type);
+
+ if(pos < 0)
+ pos = pos - 1;
+
+ lua_pushnil(L);
+ while(lua_next(L, pos) && ref)
+ {
+ ref = _lforge_basic_vector_item(L, -1, forge, child_type);
+
+ lua_pop(L, 1); // value
+ }
+
+ if(ref)
+ lv2_atom_forge_pop(forge, &frame);
+
+ return ref;
+}
+
+__realtime LV2_Atom_Forge_Ref
+_lforge_basic(lua_State *L, int pos, LV2_Atom_Forge *forge,
+ LV2_URID range, LV2_URID child_type)
+{
+ uint32_t child_size;
+
+ //FIXME binary lookup?
+ if(luaL_testudata(L, pos, "latom"))
+ return _lforge_basic_atom(L, pos, forge, range);
+ else if(range == forge->Int)
+ return _lforge_basic_int(L, pos, forge);
+ else if(range == forge->Long)
+ return _lforge_basic_long(L, pos, forge);
+ else if(range == forge->Float)
+ return _lforge_basic_float(L, pos, forge);
+ else if(range == forge->Double)
+ return _lforge_basic_double(L, pos, forge);
+ else if(range == forge->Bool)
+ return _lforge_basic_bool(L, pos, forge);
+ else if(range == forge->URID)
+ return _lforge_basic_urid(L, pos, forge);
+ else if(range == forge->String)
+ return _lforge_basic_string(L, pos, forge);
+ else if(range == forge->URI)
+ return _lforge_basic_uri(L, pos, forge);
+ else if(range == forge->Path)
+ return _lforge_basic_path(L, pos, forge);
+ else if(range == forge->Literal)
+ return _lforge_basic_literal(L, pos, forge);
+ else if(range == forge->Chunk)
+ return _lforge_basic_chunk(L, pos, forge);
+ else if( (range == forge->Vector) && (child_size = _lforge_basic_vector_child_size(forge, child_type) ))
+ return _lforge_basic_vector(L, pos, forge, child_size, child_type);
+
+ return lv2_atom_forge_atom(forge, 0, 0); // fall-back
+}
+
+__realtime static int
+_lforge_int(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!_lforge_basic_int(L, 2, lforge->forge))
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_long(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!_lforge_basic_long(L, 2, lforge->forge))
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_float(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!_lforge_basic_float(L, 2, lforge->forge))
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_double(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!_lforge_basic_double(L, 2, lforge->forge))
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_bool(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!_lforge_basic_bool(L, 2, lforge->forge))
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_urid(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!_lforge_basic_urid(L, 2, lforge->forge))
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_string(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!_lforge_basic_string(L, 2, lforge->forge))
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_uri(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!_lforge_basic_uri(L, 2, lforge->forge))
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_path(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!_lforge_basic_path(L, 2, lforge->forge))
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_literal(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+ size_t size;
+ const char *val = luaL_checklstring(L, 2, &size);
+ LV2_URID datatype = luaL_optinteger(L, 3, 0);
+ LV2_URID lang = luaL_optinteger(L, 4, 0);
+
+ if(!lv2_atom_forge_literal(lforge->forge, val, size, datatype, lang))
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_chunk(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!_lforge_basic_chunk(L, 2, lforge->forge))
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_midi(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!_lforge_basic_midi(L, 2, lforge->forge))
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_raw(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+ const LV2_URID type = luaL_checkinteger(L, 2);
+
+ if(!_lforge_basic_bytes(L, 3, lforge->forge, type))
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static uint64_t
+_lforge_to_timetag(lua_State *L, moony_t *moony, lforge_t *lforge, int pos)
+{
+ uint64_t timetag= 1ULL; // immediate timetag
+ if(lua_isinteger(L, pos))
+ {
+ // absolute timetag
+ timetag = lua_tointeger(L, pos);
+ }
+ else if(lua_isnumber(L, pos) && moony->osc_sched)
+ {
+ // timetag of current frame
+ timetag = moony->osc_sched->frames2osc(moony->osc_sched->handle,
+ lforge->last.frames);
+ volatile uint64_t sec = timetag >> 32;
+ volatile uint64_t frac = timetag & 0xffffffff;
+
+ // relative offset from current frame (in seconds)
+ double offset_d = lua_tonumber(L, pos);
+ double secs_d;
+ double frac_d = modf(offset_d, &secs_d);
+
+ // add relative offset to timetag
+ sec += secs_d;
+ frac += frac_d * 0x1p32;
+ if(frac >= 0x100000000ULL) // overflow
+ {
+ sec += 1;
+ frac -= 0x100000000ULL;
+ }
+ timetag = (sec << 32) | frac;
+
+ /*
+ // debug
+ uint64_t t0 = moony->osc_sched->frames2osc(moony->osc_sched->handle, lforge->last.frames);
+ uint64_t dt = timetag - t0;
+ double dd = dt * 0x1p-32;
+ printf("%"PRIu64" %lf\n", dt, dd);
+ */
+ }
+
+ return timetag ;
+}
+
+__realtime static int
+_lforge_osc_bundle(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ LV2_OSC_URID *osc_urid = &moony->osc_urid;
+ LV2_Atom_Forge *forge = lforge->forge;
+
+ const uint64_t timetag = _lforge_to_timetag(L, moony, lforge, 2);
+
+ lforge_t *lframe = moony_newuserdata(L, moony, MOONY_UDATA_FORGE, lforge->lheader.cache);
+ lframe->depth = 2;
+ lframe->last.frames = lforge->last.frames;
+ lframe->forge = lforge->forge;
+
+ lua_pushvalue(L, 1); // lforge
+ lua_setuservalue(L, -2); // store parent as uservalue
+
+ if(!lv2_osc_forge_bundle_head(forge, osc_urid, lframe->frame, LV2_OSC_TIMETAG_CREATE(timetag)))
+ luaL_error(L, forge_buffer_overflow);
+
+ return 1; // derived forge
+}
+
+__realtime static int
+_lforge_osc_message(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ LV2_OSC_URID *osc_urid = &moony->osc_urid;
+ LV2_Atom_Forge *forge = lforge->forge;
+
+ const char *path = luaL_checkstring(L, 2);
+ const char *fmt = luaL_optstring(L, 3, "");
+
+ LV2_Atom_Forge_Frame frame [2];
+
+ if(!lv2_osc_forge_message_head(forge, osc_urid, frame, path))
+ luaL_error(L, forge_buffer_overflow);
+
+ int pos = 4;
+ for(const char *type = fmt; *type; type++)
+ {
+ switch( (LV2_OSC_Type)*type)
+ {
+ case LV2_OSC_INT32:
+ {
+ const int32_t i = luaL_checkinteger(L, pos++);
+ if(!lv2_osc_forge_int(forge, osc_urid, i))
+ luaL_error(L, forge_buffer_overflow);
+ break;
+ }
+ case LV2_OSC_FLOAT:
+ {
+ const float f = luaL_checknumber(L, pos++);
+ if(!lv2_osc_forge_float(forge, osc_urid, f))
+ luaL_error(L, forge_buffer_overflow);
+ break;
+ }
+ case LV2_OSC_STRING:
+ {
+ size_t n;
+ const char *s = luaL_checklstring(L, pos++, &n);
+ if(!lv2_osc_forge_string(forge, osc_urid, s, n))
+ luaL_error(L, forge_buffer_overflow);
+ break;
+ }
+ case LV2_OSC_BLOB:
+ {
+ size_t n;
+ const char *b = luaL_checklstring(L, pos++, &n);
+ if(!lv2_atom_forge_atom(forge, n, forge->Chunk))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_raw(forge, b, n))
+ luaL_error(L, forge_buffer_overflow);
+ lv2_atom_forge_pad(forge, n);
+ break;
+ }
+
+ case LV2_OSC_TRUE:
+ {
+ if(!lv2_osc_forge_true(forge, osc_urid))
+ luaL_error(L, forge_buffer_overflow);
+ break;
+ }
+ case LV2_OSC_FALSE:
+ {
+ if(!lv2_osc_forge_false(forge, osc_urid))
+ luaL_error(L, forge_buffer_overflow);
+ break;
+ }
+ case LV2_OSC_NIL:
+ {
+ if(!lv2_osc_forge_nil(forge, osc_urid))
+ luaL_error(L, forge_buffer_overflow);
+ break;
+ }
+ case LV2_OSC_IMPULSE:
+ {
+ if(!lv2_osc_forge_impulse(forge, osc_urid))
+ luaL_error(L, forge_buffer_overflow);
+ break;
+ }
+
+ case LV2_OSC_INT64:
+ {
+ const int64_t h = luaL_checkinteger(L, pos++);
+ if(!lv2_osc_forge_long(forge, osc_urid, h))
+ luaL_error(L, forge_buffer_overflow);
+ break;
+ }
+ case LV2_OSC_DOUBLE:
+ {
+ const double d = luaL_checknumber(L, pos++);
+ if(!lv2_osc_forge_double(forge, osc_urid, d))
+ luaL_error(L, forge_buffer_overflow);
+ break;
+ }
+ case LV2_OSC_TIMETAG:
+ {
+ const uint64_t t = _lforge_to_timetag(L, moony, lforge, pos++);
+ if(!lv2_osc_forge_timetag(forge, osc_urid, LV2_OSC_TIMETAG_CREATE(t)))
+ luaL_error(L, forge_buffer_overflow);
+ break;
+ }
+
+ case LV2_OSC_SYMBOL:
+ {
+ const LV2_URID S = luaL_checkinteger(L, pos++);
+ if(!lv2_osc_forge_symbol(forge, osc_urid, S))
+ luaL_error(L, forge_buffer_overflow);
+ break;
+ break;
+ }
+ case LV2_OSC_MIDI:
+ {
+ size_t n;
+ const char *m = luaL_checklstring(L, pos++, &n);
+ if(!lv2_atom_forge_atom(forge, n, moony->osc_urid.MIDI_MidiEvent))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_raw(forge, m, n))
+ luaL_error(L, forge_buffer_overflow);
+ lv2_atom_forge_pad(forge, n);
+ break;
+ }
+ case LV2_OSC_CHAR:
+ {
+ const char c = luaL_checkinteger(L, pos++);
+ if(!lv2_osc_forge_char(forge, osc_urid, c))
+ luaL_error(L, forge_buffer_overflow);
+ break;
+ }
+ case LV2_OSC_RGBA:
+ {
+ const uint32_t rgba = luaL_checkinteger(L, pos++);
+ if(!lv2_osc_forge_rgba(forge, osc_urid,
+ (rgba >> 24) & 0xff,
+ (rgba >> 16) & 0xff,
+ (rgba >> 8) & 0xff,
+ rgba & 0xff))
+ luaL_error(L, forge_buffer_overflow);
+ break;
+ }
+ }
+ }
+
+ lv2_osc_forge_pop(forge, frame);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_osc_impulse(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ LV2_OSC_URID *osc_urid = &moony->osc_urid;
+
+ if(!lv2_osc_forge_impulse(lforge->forge, osc_urid))
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_osc_char(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ LV2_OSC_URID *osc_urid = &moony->osc_urid;
+
+ const char ch = luaL_checkinteger(L, 2);
+
+ if(!lv2_osc_forge_char(lforge->forge, osc_urid, ch))
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_osc_rgba(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ LV2_OSC_URID *osc_urid = &moony->osc_urid;
+
+ const uint32_t col = luaL_checkinteger(L, 2);
+ const uint8_t r = (col >> 24) & 0xff;
+ const uint8_t g = (col >> 16) & 0xff;
+ const uint8_t b = (col >> 8) & 0xff;
+ const uint8_t a = (col >> 0) & 0xff;
+
+ if(!lv2_osc_forge_rgba(lforge->forge, osc_urid, r, g, b, a))
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_osc_timetag(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ LV2_OSC_URID *osc_urid = &moony->osc_urid;
+
+ const uint64_t tt = _lforge_to_timetag(L, moony, lforge, 2);
+
+ if(!lv2_osc_forge_timetag(lforge->forge, osc_urid, LV2_OSC_TIMETAG_CREATE(tt)))
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_tuple(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ lforge_t *lframe = moony_newuserdata(L, moony, MOONY_UDATA_FORGE, lforge->lheader.cache);
+ lframe->depth = 1;
+ lframe->last.frames = lforge->last.frames;
+ lframe->forge = lforge->forge;
+
+ lua_pushvalue(L, 1); // lforge
+ lua_setuservalue(L, -2); // store parent as uservalue
+
+ if(!lv2_atom_forge_tuple(lforge->forge, &lframe->frame[0]))
+ luaL_error(L, forge_buffer_overflow);
+
+ return 1; // derived forge
+}
+
+__realtime static int
+_lforge_object(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ LV2_URID otype = luaL_optinteger(L, 2, 0);
+ LV2_URID id = luaL_optinteger(L, 3, 0);
+ lforge_t *lframe = moony_newuserdata(L, moony, MOONY_UDATA_FORGE, lforge->lheader.cache);
+ lframe->depth = 1;
+ lframe->last.frames = lforge->last.frames;
+ lframe->forge = lforge->forge;
+
+ lua_pushvalue(L, 1); // lforge
+ lua_setuservalue(L, -2); // store parent as uservalue
+
+ if(!lv2_atom_forge_object(lforge->forge, &lframe->frame[0], id, otype))
+ luaL_error(L, forge_buffer_overflow);
+
+ return 1; // derived forge
+}
+__realtime static int
+_lforge_key(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+ LV2_URID key = luaL_checkinteger(L, 2);
+ LV2_URID context = luaL_optinteger(L, 3, 0);
+
+ if(!lv2_atom_forge_property_head(lforge->forge, key, context))
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_vector_derive(moony_t *moony, lforge_t *lforge, lua_State *L,
+ uint32_t child_size, uint32_t child_type)
+{
+ lforge_t *lframe = moony_newuserdata(L, moony, MOONY_UDATA_FORGE, lforge->lheader.cache);
+ lframe->depth = 1;
+ lframe->last.frames = lforge->last.frames;
+ lframe->forge = lforge->forge;
+
+ lua_pushvalue(L, 1); // lforge
+ lua_setuservalue(L, -2); // store parent as uservalue
+
+ if(!lv2_atom_forge_vector_head(lforge->forge, &lframe->frame[0], child_size, child_type))
+ luaL_error(L, forge_buffer_overflow);
+
+ return 1; // derived forge
+}
+
+__realtime static int
+_lforge_vector_table(int idx, lforge_t *lforge, lua_State *L,
+ uint32_t child_size, uint32_t child_type)
+{
+ if(!_lforge_basic_vector(L, idx, lforge->forge, child_size, child_type))
+ luaL_error(L, forge_buffer_overflow);
+
+ return 1; // self forge
+}
+
+__realtime static int
+_lforge_vector_args(int from, int to, lforge_t *lforge, lua_State *L,
+ uint32_t child_size, uint32_t child_type)
+{
+ LV2_Atom_Forge_Frame vec_frame;
+
+ if(!lv2_atom_forge_vector_head(lforge->forge, &vec_frame, child_size, child_type))
+ luaL_error(L, forge_buffer_overflow);
+
+ for(int i = from; i <= to; i++)
+ {
+ if(!_lforge_basic_vector_item(L, i, lforge->forge, child_type))
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ lv2_atom_forge_pop(lforge->forge, &vec_frame);
+
+ return 1; // self forge
+}
+
+__realtime static int
+_lforge_vector(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ const LV2_URID child_type = luaL_checkinteger(L, 2);
+ uint32_t child_size;
+
+ if( (child_type == lforge->forge->Bool)
+ || (child_type == lforge->forge->Int)
+ || (child_type == lforge->forge->Float)
+ || (child_type == lforge->forge->URID) )
+ {
+ child_size = 4;
+ }
+ else if( (child_type == lforge->forge->Long)
+ || (child_type == lforge->forge->Double) )
+ {
+ child_size = 8;
+ }
+ else
+ {
+ luaL_error(L, "Atom vector only supports atom:Bool/Int/URID/Float/Long/Double");
+ }
+
+ const int top = lua_gettop(L);
+
+ if(top >= 3)
+ {
+ if(lua_istable(L, 3))
+ return _lforge_vector_table(3, lforge, L, child_size, child_type);
+ else
+ return _lforge_vector_args(3, top, lforge, L, child_size, child_type);
+ }
+
+ return _lforge_vector_derive(moony, lforge, L, child_size, child_type);
+}
+
+__realtime static int
+_lforge_sequence(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ LV2_URID unit = luaL_optinteger(L, 2, 0);
+ lforge_t *lframe = moony_newuserdata(L, moony, MOONY_UDATA_FORGE, lforge->lheader.cache);
+ lframe->depth = 1;
+ lframe->last.frames = 0;
+ lframe->forge = lforge->forge;
+
+ lua_pushvalue(L, 1); // lforge
+ lua_setuservalue(L, -2); // store parent as uservalue
+
+ if(!lv2_atom_forge_sequence_head(lforge->forge, &lframe->frame[0], unit))
+ luaL_error(L, forge_buffer_overflow);
+
+ return 1; // derived forge
+}
+
+__realtime static int
+_lforge_typed(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ LV2_URID urid = luaL_checkinteger(L, 2);
+ lua_remove(L, 2); // urid
+
+ lua_CFunction hook = NULL;
+
+ //TODO keep this list updated
+ //TODO use binary tree sorted by URID
+ if(urid == lforge->forge->Int)
+ hook = _lforge_int;
+ else if(urid == lforge->forge->Long)
+ hook = _lforge_long;
+ else if(urid == lforge->forge->Float)
+ hook = _lforge_float;
+ else if(urid == lforge->forge->Double)
+ hook = _lforge_double;
+ else if(urid == lforge->forge->Bool)
+ hook = _lforge_bool;
+ else if(urid == lforge->forge->URID)
+ hook = _lforge_urid;
+ else if(urid == lforge->forge->String)
+ hook = _lforge_string;
+ else if(urid == lforge->forge->Literal)
+ hook = _lforge_literal;
+ else if(urid == lforge->forge->URI)
+ hook = _lforge_uri;
+ else if(urid == lforge->forge->Path)
+ hook = _lforge_path;
+
+ else if(urid == lforge->forge->Chunk)
+ hook = _lforge_chunk;
+ else if(urid == moony->uris.midi_event)
+ hook = _lforge_midi;
+ else if(urid == moony->osc_urid.OSC_Bundle)
+ hook = _lforge_osc_bundle;
+ else if(urid == moony->osc_urid.OSC_Message)
+ hook = _lforge_osc_message;
+ else if(urid == lforge->forge->Tuple)
+ hook = _lforge_tuple;
+ else if(urid == lforge->forge->Object)
+ hook = _lforge_object;
+ else if(urid == lforge->forge->Property)
+ hook = _lforge_key;
+ else if(urid == lforge->forge->Vector)
+ hook = _lforge_vector;
+ else if(urid == lforge->forge->Sequence)
+ hook = _lforge_sequence;
+
+ if(!hook)
+ return luaL_error(L, "unknown atom type");
+
+ return hook(L);
+}
+
+__realtime static int
+_lforge_get(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ const LV2_URID property = luaL_optinteger(L, 2, 0);
+ const LV2_URID subject = luaL_optinteger(L, 3, 0);
+ const int32_t sequence_num = luaL_optinteger(L, 4, 0);
+
+ LV2_Atom_Forge_Frame frame;
+
+ if(!lv2_atom_forge_object(lforge->forge, &frame, 0, moony->uris.patch.get))
+ luaL_error(L, forge_buffer_overflow);
+
+ if(subject) // is optional
+ {
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.subject))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_urid(lforge->forge, subject))
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ if(property)
+ {
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.property))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_urid(lforge->forge, property))
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.sequence))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_int(lforge->forge, sequence_num))
+ luaL_error(L, forge_buffer_overflow);
+
+ lv2_atom_forge_pop(lforge->forge, &frame);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_set(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ const LV2_URID property = luaL_checkinteger(L, 2);
+ const LV2_URID subject = luaL_optinteger(L, 3, 0);
+ const int32_t sequence_num = luaL_optinteger(L, 4, 0);
+ lforge_t *lframe = moony_newuserdata(L, moony, MOONY_UDATA_FORGE, lforge->lheader.cache);
+ lframe->depth = 1;
+ lframe->last.frames = lforge->last.frames;
+ lframe->forge = lforge->forge;
+
+ lua_pushvalue(L, 1); // lforge
+ lua_setuservalue(L, -2); // store parent as uservalue
+
+ if(!lv2_atom_forge_object(lforge->forge, lframe->frame, 0, moony->uris.patch.set))
+ luaL_error(L, forge_buffer_overflow);
+
+ if(subject) // is optional
+ {
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.subject))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_urid(lforge->forge, subject))
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.sequence))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_int(lforge->forge, sequence_num))
+ luaL_error(L, forge_buffer_overflow);
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.property))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_urid(lforge->forge, property))
+ luaL_error(L, forge_buffer_overflow);
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.value))
+ luaL_error(L, forge_buffer_overflow);
+
+ return 1; // derived forge
+}
+
+__realtime static int
+_lforge_put(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ const LV2_URID subject = luaL_optinteger(L, 2, 0);
+ const LV2_URID sequence_num = luaL_optinteger(L, 3, 0);
+ lforge_t *lframe = moony_newuserdata(L, moony, MOONY_UDATA_FORGE, lforge->lheader.cache);
+ lframe->depth = 2;
+ lframe->last.frames = lforge->last.frames;
+ lframe->forge = lforge->forge;
+
+ lua_pushvalue(L, 1); // lforge
+ lua_setuservalue(L, -2); // store parent as uservalue
+
+ if(!lv2_atom_forge_object(lforge->forge, &lframe->frame[0], 0, moony->uris.patch.put))
+ luaL_error(L, forge_buffer_overflow);
+
+ if(subject) // is optional
+ {
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.subject))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_urid(lforge->forge, subject))
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.sequence))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_int(lforge->forge, sequence_num))
+ luaL_error(L, forge_buffer_overflow);
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.body))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_object(lforge->forge, &lframe->frame[1], 0, 0))
+ luaL_error(L, forge_buffer_overflow);
+
+ return 1; // derived forge
+}
+
+__realtime static int
+_lforge_patch(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ const LV2_URID subject = luaL_optinteger(L, 2, 0);
+ const int32_t sequence_num = luaL_optinteger(L, 3, 0);
+ lforge_t *lframe = moony_newuserdata(L, moony, MOONY_UDATA_FORGE, lforge->lheader.cache);
+ lframe->depth = 1;
+ lframe->last.frames = lforge->last.frames;
+ lframe->forge = lforge->forge;
+
+ lua_pushvalue(L, 1); // lforge
+ lua_setuservalue(L, -2); // store parent as uservalue
+
+ if(!lv2_atom_forge_object(lforge->forge, lframe->frame, 0, moony->uris.patch.patch))
+ luaL_error(L, forge_buffer_overflow);
+
+ if(subject) // is optional
+ {
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.subject))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_urid(lforge->forge, subject))
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.sequence))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_int(lforge->forge, sequence_num))
+ luaL_error(L, forge_buffer_overflow);
+
+ return 1; // derived forge
+}
+
+__realtime static int
+_lforge_remove(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ lforge_t *lframe = moony_newuserdata(L, moony, MOONY_UDATA_FORGE, lforge->lheader.cache);
+ lframe->depth = 1;
+ lframe->last.frames = lforge->last.frames;
+ lframe->forge = lforge->forge;
+
+ lua_pushvalue(L, 1); // lforge
+ lua_setuservalue(L, -2); // store parent as uservalue
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.remove))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_object(lforge->forge, lframe->frame, 0, 0))
+ luaL_error(L, forge_buffer_overflow);
+
+ return 1; // derived forge
+}
+
+__realtime static int
+_lforge_add(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ lforge_t *lframe = moony_newuserdata(L, moony, MOONY_UDATA_FORGE, lforge->lheader.cache);
+ lframe->depth = 1;
+ lframe->last.frames = lforge->last.frames;
+ lframe->forge = lforge->forge;
+
+ lua_pushvalue(L, 1); // lforge
+ lua_setuservalue(L, -2); // store parent as uservalue
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.add))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_object(lforge->forge, lframe->frame, 0, 0))
+ luaL_error(L, forge_buffer_overflow);
+
+ return 1; // derived forge
+}
+
+__realtime static int
+_lforge_ack(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ const LV2_URID subject = luaL_optinteger(L, 2, 0);
+ const int32_t sequence_num = luaL_optinteger(L, 3, 0);
+
+ LV2_Atom_Forge_Frame frame;
+
+ if(!lv2_atom_forge_object(lforge->forge, &frame, 0, moony->uris.patch.ack))
+ luaL_error(L, forge_buffer_overflow);
+
+ if(subject) // is optional
+ {
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.subject))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_urid(lforge->forge, subject))
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.sequence))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_int(lforge->forge, sequence_num))
+ luaL_error(L, forge_buffer_overflow);
+
+ lv2_atom_forge_pop(lforge->forge, &frame);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_error(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ const LV2_URID subject = luaL_optinteger(L, 2, 0);
+ const int32_t sequence_num = luaL_optinteger(L, 3, 0);
+
+ LV2_Atom_Forge_Frame frame;
+
+ if(!lv2_atom_forge_object(lforge->forge, &frame, 0, moony->uris.patch.error))
+ luaL_error(L, forge_buffer_overflow);
+
+ if(subject) // is optional
+ {
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.subject))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_urid(lforge->forge, subject))
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.sequence))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_int(lforge->forge, sequence_num))
+ luaL_error(L, forge_buffer_overflow);
+
+ lv2_atom_forge_pop(lforge->forge, &frame);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_delete(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ const LV2_URID subject = luaL_checkinteger(L, 2);
+ const int32_t sequence_num = luaL_optinteger(L, 3, 0);
+
+ LV2_Atom_Forge_Frame frame;
+
+ if(!lv2_atom_forge_object(lforge->forge, &frame, 0, moony->uris.patch.delete))
+ luaL_error(L, forge_buffer_overflow);
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.subject))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_urid(lforge->forge, subject))
+ luaL_error(L, forge_buffer_overflow);
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.sequence))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_int(lforge->forge, sequence_num))
+ luaL_error(L, forge_buffer_overflow);
+
+ lv2_atom_forge_pop(lforge->forge, &frame);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_copy(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ const LV2_URID subject = luaL_checkinteger(L, 2);
+ const LV2_URID destination = luaL_checkinteger(L, 3);
+ const int32_t sequence_num = luaL_optinteger(L, 4, 0);
+
+ LV2_Atom_Forge_Frame frame;
+
+ if(!lv2_atom_forge_object(lforge->forge, &frame, 0, moony->uris.patch.copy))
+ luaL_error(L, forge_buffer_overflow);
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.subject))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_urid(lforge->forge, subject))
+ luaL_error(L, forge_buffer_overflow);
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.destination))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_urid(lforge->forge, destination))
+ luaL_error(L, forge_buffer_overflow);
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.sequence))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_int(lforge->forge, sequence_num))
+ luaL_error(L, forge_buffer_overflow);
+
+ lv2_atom_forge_pop(lforge->forge, &frame);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_move(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ const LV2_URID subject = luaL_checkinteger(L, 2);
+ const LV2_URID destination = luaL_checkinteger(L, 3);
+ const int32_t sequence_num = luaL_optinteger(L, 4, 0);
+
+ LV2_Atom_Forge_Frame frame;
+
+ if(!lv2_atom_forge_object(lforge->forge, &frame, 0, moony->uris.patch.move))
+ luaL_error(L, forge_buffer_overflow);
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.subject))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_urid(lforge->forge, subject))
+ luaL_error(L, forge_buffer_overflow);
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.destination))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_urid(lforge->forge, destination))
+ luaL_error(L, forge_buffer_overflow);
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.sequence))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_int(lforge->forge, sequence_num))
+ luaL_error(L, forge_buffer_overflow);
+
+ lv2_atom_forge_pop(lforge->forge, &frame);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_insert(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+ const LV2_URID subject = luaL_optinteger(L, 2, 0);
+ const LV2_URID sequence_num = luaL_optinteger(L, 3, 0);
+ lforge_t *lframe = moony_newuserdata(L, moony, MOONY_UDATA_FORGE, lforge->lheader.cache);
+ lframe->depth = 2;
+ lframe->last.frames = lforge->last.frames;
+ lframe->forge = lforge->forge;
+
+ lua_pushvalue(L, 1); // lforge
+ lua_setuservalue(L, -2); // store parent as uservalue
+
+ if(!lv2_atom_forge_object(lforge->forge, &lframe->frame[0], 0, moony->uris.patch.insert))
+ luaL_error(L, forge_buffer_overflow);
+
+ if(subject) // is optional
+ {
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.subject))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_urid(lforge->forge, subject))
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.sequence))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_int(lforge->forge, sequence_num))
+ luaL_error(L, forge_buffer_overflow);
+
+ if(!lv2_atom_forge_key(lforge->forge, moony->uris.patch.body))
+ luaL_error(L, forge_buffer_overflow);
+ if(!lv2_atom_forge_object(lforge->forge, &lframe->frame[1], 0, 0))
+ luaL_error(L, forge_buffer_overflow);
+
+ return 1; // derived forge
+}
+
+__realtime static int
+_lforge_canvas_begin_path(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_beginPath(lforge->forge, &moony->canvas_urid) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_close_path(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_closePath(lforge->forge, &moony->canvas_urid) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_arc(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_arc(lforge->forge, &moony->canvas_urid,
+ luaL_checknumber(L, 2),
+ luaL_checknumber(L, 3),
+ luaL_checknumber(L, 4),
+ luaL_optnumber(L, 5, 0.f),
+ luaL_optnumber(L, 6, M_PI*2)) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_curve_to(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_curveTo(lforge->forge, &moony->canvas_urid,
+ luaL_checknumber(L, 2),
+ luaL_checknumber(L, 3),
+ luaL_checknumber(L, 4),
+ luaL_checknumber(L, 5),
+ luaL_checknumber(L, 6),
+ luaL_checknumber(L, 7)) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_line_to(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_lineTo(lforge->forge, &moony->canvas_urid,
+ luaL_checknumber(L, 2),
+ luaL_checknumber(L, 3)) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_move_to(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_moveTo(lforge->forge, &moony->canvas_urid,
+ luaL_checknumber(L, 2),
+ luaL_checknumber(L, 3)) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_rectangle(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_rectangle(lforge->forge, &moony->canvas_urid,
+ luaL_checknumber(L, 2),
+ luaL_checknumber(L, 3),
+ luaL_checknumber(L, 4),
+ luaL_checknumber(L, 5)) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_poly_line(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ const uint32_t nvec = lua_gettop(L) - 1;
+ float *vec = alloca(sizeof(float) * nvec); //FIXME add floats one at a time
+
+ //FIXME handle table as single argument
+ for(unsigned pos = 0; pos < nvec; pos++)
+ {
+ vec[pos] = luaL_checknumber(L, 2 + pos);
+ }
+
+ if(!lv2_canvas_forge_polyLine(lforge->forge, &moony->canvas_urid, nvec, vec) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_style(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_style(lforge->forge, &moony->canvas_urid,
+ luaL_checkinteger(L, 2)) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_line_width(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_lineWidth(lforge->forge, &moony->canvas_urid,
+ luaL_checknumber(L, 2)) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_line_dash(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_lineDash(lforge->forge, &moony->canvas_urid,
+ luaL_checknumber(L, 2),
+ luaL_checknumber(L, 3)) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_line_cap(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_lineCap(lforge->forge, &moony->canvas_urid,
+ luaL_checkinteger(L, 2)) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_line_join(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_lineJoin(lforge->forge, &moony->canvas_urid,
+ luaL_checkinteger(L, 2)) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_miter_limit(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_miterLimit(lforge->forge, &moony->canvas_urid,
+ luaL_checknumber(L, 2)) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_stroke(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_stroke(lforge->forge, &moony->canvas_urid) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_fill(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_fill(lforge->forge, &moony->canvas_urid) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_clip(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_clip(lforge->forge, &moony->canvas_urid) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_save(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_save(lforge->forge, &moony->canvas_urid) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_restore(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_restore(lforge->forge, &moony->canvas_urid) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_translate(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_translate(lforge->forge, &moony->canvas_urid,
+ luaL_checknumber(L, 2),
+ luaL_checknumber(L, 3)) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_scale(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_scale(lforge->forge, &moony->canvas_urid,
+ luaL_checknumber(L, 2),
+ luaL_checknumber(L, 3)) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_rotate(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_rotate(lforge->forge, &moony->canvas_urid,
+ luaL_checknumber(L, 2)) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_transform(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_transform(lforge->forge, &moony->canvas_urid,
+ luaL_checknumber(L, 2),
+ luaL_checknumber(L, 3),
+ luaL_checknumber(L, 4),
+ luaL_checknumber(L, 5),
+ luaL_checknumber(L, 6),
+ luaL_checknumber(L, 7)) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_reset(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_reset(lforge->forge, &moony->canvas_urid) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_font_size(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_fontSize(lforge->forge, &moony->canvas_urid,
+ luaL_checknumber(L, 2)) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_canvas_fill_text(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(!lv2_canvas_forge_fillText(lforge->forge, &moony->canvas_urid,
+ luaL_checkstring(L, 2)) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_xpress_token(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ const int64_t source = luaL_checkinteger(L, 2);
+ const int32_t zone = luaL_checkinteger(L, 3);
+ const int64_t uuid = luaL_checkinteger(L, 4);
+
+ const float x = luaL_optnumber(L, 5, 0.f);
+ const float y = luaL_optnumber(L, 6, 0.f);
+ const float z = luaL_optnumber(L, 7, 0.f);
+ const float dx = luaL_optnumber(L, 8, 0.f);
+ const float dy = luaL_optnumber(L, 9, 0.f);
+ const float dz = luaL_optnumber(L, 10, 0.f);
+
+ LV2_Atom_Forge_Frame frame;
+ if( !lv2_atom_forge_object(lforge->forge, &frame, 0, moony->uris.xpress_Token)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.xpress_source)
+ || !lv2_atom_forge_long(lforge->forge, source)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.xpress_zone)
+ || !lv2_atom_forge_int(lforge->forge, zone)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.xpress_uuid)
+ || !lv2_atom_forge_long(lforge->forge, uuid)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.xpress_pitch)
+ || !lv2_atom_forge_float(lforge->forge, x)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.xpress_pressure)
+ || !lv2_atom_forge_float(lforge->forge, y)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.xpress_timbre)
+ || !lv2_atom_forge_float(lforge->forge, z)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.xpress_dPitch)
+ || !lv2_atom_forge_float(lforge->forge, dx)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.xpress_dPressure)
+ || !lv2_atom_forge_float(lforge->forge, dy)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.xpress_dTimbre)
+ || !lv2_atom_forge_float(lforge->forge, dz) )
+ {
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ lv2_atom_forge_pop(lforge->forge, &frame);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_xpress_alive(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ const int64_t source = luaL_checkinteger(L, 2);
+
+ LV2_Atom_Forge_Frame frame [2];
+ if( !lv2_atom_forge_object(lforge->forge, &frame[0], 0, moony->uris.xpress_Alive)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.xpress_source)
+ || !lv2_atom_forge_long(lforge->forge, source)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.xpress_body)
+ || !lv2_atom_forge_tuple(lforge->forge, &frame[1]) )
+ {
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+
+ switch(lua_type(L, 3))
+ {
+ case LUA_TTABLE:
+ {
+ lua_pushnil(L);
+ while(lua_next(L, 3))
+ {
+ const int64_t uuid = luaL_checkinteger(L, -1);
+
+ if(!lv2_atom_forge_long(lforge->forge, uuid))
+ luaL_error(L, forge_buffer_overflow);
+
+ lua_pop(L, 1); // pop value, leave key from stack
+ }
+
+ break;
+ }
+ case LUA_TNUMBER:
+ {
+ const int top = lua_gettop(L);
+ for(int i=3; i<=top; i++)
+ {
+ const int64_t uuid = luaL_checkinteger(L, i);
+
+ if(!lv2_atom_forge_long(lforge->forge, uuid))
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ break;
+ }
+ }
+
+ lv2_atom_forge_pop(lforge->forge, &frame[1]);
+ lv2_atom_forge_pop(lforge->forge, &frame[0]);
+
+ lua_settop(L, 1);
+ return 1;
+}
+
+__realtime static int
+_lforge_pop(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ _lforge_pop_inlined(L, lforge);
+
+ lua_getuservalue(L, 1); // get parent lforge
+
+ return 1;
+}
+
+__realtime int
+_lforge_autopop_itr(lua_State *L)
+{
+ if(lua_isnil(L, 2)) // 1st invocation
+ {
+ lua_settop(L, 1); // return lforge
+ }
+ else // 2nd invocation
+ {
+ lforge_t *lforge = lua_touserdata(L, 1);
+ _lforge_pop_inlined(L, lforge);
+
+ lua_pushnil(L); // terminate iterator
+ }
+
+ return 1; // derived forge || nil
+}
+
+__realtime static int
+_lforge_autopop(lua_State *L)
+{
+ lua_rawgetp(L, LUA_REGISTRYINDEX, _lforge_autopop_itr);
+ lua_pushvalue(L, 1); // lforge
+
+ return 2;
+}
+
+__realtime static int
+_lforge_read(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(lforge->lheader.type != MOONY_UDATA_STASH)
+ luaL_error(L, "not a stash object");
+
+ return _lstash_read(L);
+}
+
+__realtime static int
+_lforge_write(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(lforge->lheader.type != MOONY_UDATA_STASH)
+ luaL_error(L, "not a stash object");
+
+ return _lstash_write(L);
+}
+
+__realtime static int
+_lforge__gc(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+
+ if(lforge->lheader.type == MOONY_UDATA_STASH)
+ return _lstash__gc(L);
+
+ return 0;
+}
+
+__realtime static int
+_lforge__tostring(lua_State *L)
+{
+ lforge_t *lforge = lua_touserdata(L, 1);
+ lua_pushfstring(L, "(forge: %p)", lforge);
+ return 1;
+}
+
+const luaL_Reg lforge_mt [] = {
+ {"int", _lforge_int},
+ {"long", _lforge_long},
+ {"float", _lforge_float},
+ {"double", _lforge_double},
+ {"bool", _lforge_bool},
+ {"urid", _lforge_urid},
+ {"string", _lforge_string},
+ {"literal", _lforge_literal},
+ {"uri", _lforge_uri},
+ {"path", _lforge_path},
+
+ {"chunk", _lforge_chunk},
+ {"midi", _lforge_midi},
+
+ {"raw", _lforge_raw},
+ {"typed", _lforge_typed},
+ {"atom", _lforge_atom},
+
+ {"tuple", _lforge_tuple},
+
+ {"object", _lforge_object},
+ {"key", _lforge_key},
+
+ {"vector", _lforge_vector},
+
+ {"sequence", _lforge_sequence},
+ {"frameTime", _lforge_frame_time},
+ {"beatTime", _lforge_beat_time},
+ {"time", _lforge_time},
+
+ // OSC
+ {"bundle", _lforge_osc_bundle},
+ {"message", _lforge_osc_message},
+ {"impulse", _lforge_osc_impulse},
+ {"char", _lforge_osc_char},
+ {"rgba", _lforge_osc_rgba},
+ {"timetag", _lforge_osc_timetag},
+
+ // patch
+ {"get", _lforge_get},
+ {"set", _lforge_set},
+ {"put", _lforge_put},
+ {"patch", _lforge_patch},
+ {"remove", _lforge_remove},
+ {"add", _lforge_add},
+ {"ack", _lforge_ack},
+ {"error", _lforge_error},
+ {"delete", _lforge_delete},
+ {"copy", _lforge_copy},
+ {"move", _lforge_move},
+ {"insert", _lforge_insert},
+
+ // canvas
+ {"beginPath", _lforge_canvas_begin_path},
+ {"closePath", _lforge_canvas_close_path},
+ {"arc", _lforge_canvas_arc},
+ {"curveTo", _lforge_canvas_curve_to},
+ {"lineTo", _lforge_canvas_line_to},
+ {"moveTo", _lforge_canvas_move_to},
+ {"rectangle", _lforge_canvas_rectangle},
+ {"polyLine", _lforge_canvas_poly_line},
+ {"style", _lforge_canvas_style},
+ {"lineWidth", _lforge_canvas_line_width},
+ {"lineDash", _lforge_canvas_line_dash},
+ {"lineCap", _lforge_canvas_line_cap},
+ {"lineJoin", _lforge_canvas_line_join},
+ {"miterLimit", _lforge_canvas_miter_limit},
+ {"stroke", _lforge_canvas_stroke},
+ {"fill", _lforge_canvas_fill},
+ {"clip", _lforge_canvas_clip},
+ {"save", _lforge_canvas_save},
+ {"restore", _lforge_canvas_restore},
+ {"translate", _lforge_canvas_translate},
+ {"scale", _lforge_canvas_scale},
+ {"rotate", _lforge_canvas_rotate},
+ {"transform", _lforge_canvas_transform},
+ {"reset", _lforge_canvas_reset},
+ {"fontSize", _lforge_canvas_font_size},
+ {"fillText", _lforge_canvas_fill_text},
+
+ // xpress
+ {"token", _lforge_xpress_token},
+ {"alive", _lforge_xpress_alive},
+
+ {"pop", _lforge_pop},
+ {"autopop", _lforge_autopop},
+
+ // stash
+ {"read", _lforge_read},
+ {"write", _lforge_write},
+
+ {"__tostring", _lforge__tostring},
+ {"__gc", _lforge__gc},
+
+ {NULL, NULL}
+};
+
+const char *forge_buffer_overflow = "forge buffer overflow";
diff --git a/api/api_forge.h b/api/api_forge.h
new file mode 100644
index 0000000..cc9f855
--- /dev/null
+++ b/api/api_forge.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _MOONY_API_FORGE_H
+#define _MOONY_API_FORGE_H
+
+#include <moony.h>
+
+typedef struct _lforge_t lforge_t;
+
+struct _lforge_t {
+ lheader_t lheader;
+
+ LV2_Atom_Forge *forge;
+ int depth;
+ union {
+ int64_t frames; // Time in audio frames
+ double beats; // Time in beats
+ } last;
+ union {
+ struct {
+ LV2_URID otype; // Object type
+ LV2_URID id; // Object ID
+ };
+ LV2_URID unit; // Sequence time unit
+ } pack;
+ LV2_Atom_Forge_Frame frame [2];
+};
+
+LV2_Atom_Forge_Ref
+_lforge_basic(lua_State *L, int pos, LV2_Atom_Forge *forge,
+ LV2_URID range, LV2_URID child_type);
+
+int
+_lforge_autopop_itr(lua_State *L);
+
+extern const char *forge_buffer_overflow;
+
+extern const luaL_Reg lforge_mt [];
+
+#endif
diff --git a/api/api_midi.c b/api/api_midi.c
new file mode 100644
index 0000000..d8b3aa4
--- /dev/null
+++ b/api/api_midi.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <api_midi.h>
+#include <api_atom.h>
+#include <api_forge.h>
+
+__realtime static int
+_lmidiresponder__call(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ const bool *through = lua_touserdata(L, 1);
+
+ lua_settop(L, 4); // discard superfluous arguments
+ // 1: self
+ // 2: frames
+ // 3: forge
+ // 4: atom
+
+ latom_t *latom = NULL;
+ if(luaL_testudata(L, 4, "latom"))
+ latom = lua_touserdata(L, 4);
+ lua_pop(L, 1); // atom
+
+ // check for valid atom and event type
+ if( !latom
+ || (latom->atom->type != moony->uris.midi_event) )
+ {
+ lua_pushboolean(L, 0); // not handled
+ return 1;
+ }
+
+ const uint8_t *midi = latom->body.raw;
+ const uint8_t status = midi[0];
+ const uint8_t command = status & 0xf0;
+ const bool is_system = command == 0xf0;
+
+ // replace self with its uservalue
+ lua_getuservalue(L, 1);
+ lua_replace(L, 1);
+
+ if(lua_geti(L, 1, is_system ? status : command) != LUA_TNIL)
+ {
+ lua_insert(L, 1);
+ if(is_system)
+ lua_pushnil(L); // system messages have no channel
+ else
+ lua_pushinteger(L, status & 0x0f); // 4: channel
+
+ for(unsigned i=1; i<latom->atom->size; i++)
+ lua_pushinteger(L, midi[i]);
+
+ lua_call(L, 4 + latom->atom->size - 1, 0);
+ }
+ else if(*through) // through
+ {
+ const int64_t frames = luaL_checkinteger(L, 2);
+ lforge_t *lforge = luaL_checkudata(L, 3, "lforge");
+
+ if(frames < lforge->last.frames)
+ luaL_error(L, "invalid frame time, must not decrease");
+ lforge->last.frames = frames;
+
+ if( !lv2_atom_forge_frame_time(lforge->forge, frames)
+ || !lv2_atom_forge_atom(lforge->forge, latom->atom->size, latom->atom->type)
+ || !lv2_atom_forge_write(lforge->forge, latom->body.raw, latom->atom->size) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ lua_pushboolean(L, 1); // handled
+ return 1;
+}
+
+__realtime int
+_lmidiresponder(lua_State *L)
+{
+ //moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ lua_settop(L, 2); // discard superfluous arguments
+
+ const bool _through = lua_toboolean(L, 2);
+ lua_pop(L, 1); // bool
+
+ // o = new
+ bool *through = lua_newuserdata(L, sizeof(bool));
+ *through= _through;
+
+ // o.uservalue = uservalue
+ lua_insert(L, 1);
+ lua_setuservalue(L, -2);
+
+ // setmetatable(o, self)
+ luaL_getmetatable(L, "lmidiresponder");
+ lua_setmetatable(L, -2);
+
+ // return o
+ return 1;
+}
+
+const luaL_Reg lmidiresponder_mt [] = {
+ {"__call", _lmidiresponder__call},
+ {NULL, NULL}
+};
+
+const midi_msg_t midi_msgs [] = {
+ { LV2_MIDI_MSG_NOTE_OFF , "NoteOff" },
+ { LV2_MIDI_MSG_NOTE_ON , "NoteOn" },
+ { LV2_MIDI_MSG_NOTE_PRESSURE , "NotePressure" },
+ { LV2_MIDI_MSG_CONTROLLER , "Controller" },
+ { LV2_MIDI_MSG_PGM_CHANGE , "ProgramChange" },
+ { LV2_MIDI_MSG_CHANNEL_PRESSURE , "ChannelPressure" },
+ { LV2_MIDI_MSG_BENDER , "Bender" },
+ { LV2_MIDI_MSG_SYSTEM_EXCLUSIVE , "SystemExclusive" },
+ { LV2_MIDI_MSG_MTC_QUARTER , "QuarterFrame" },
+ { LV2_MIDI_MSG_SONG_POS , "SongPosition" },
+ { LV2_MIDI_MSG_SONG_SELECT , "SongSelect" },
+ { LV2_MIDI_MSG_TUNE_REQUEST , "TuneRequest" },
+ { LV2_MIDI_MSG_CLOCK , "Clock" },
+ { LV2_MIDI_MSG_START , "Start" },
+ { LV2_MIDI_MSG_CONTINUE , "Continue" },
+ { LV2_MIDI_MSG_STOP , "Stop" },
+ { LV2_MIDI_MSG_ACTIVE_SENSE , "ActiveSense" },
+ { LV2_MIDI_MSG_RESET , "Reset" },
+ { 0xf7 , "EndOfExclusive" },
+
+ { 0, NULL} // sentinel
+};
+
+const midi_msg_t controllers [] = {
+ { LV2_MIDI_CTL_MSB_BANK , "BankSelection_MSB" },
+ { LV2_MIDI_CTL_MSB_MODWHEEL , "Modulation_MSB" },
+ { LV2_MIDI_CTL_MSB_BREATH , "Breath_MSB" },
+ { LV2_MIDI_CTL_MSB_FOOT , "Foot_MSB" },
+ { LV2_MIDI_CTL_MSB_PORTAMENTO_TIME , "PortamentoTime_MSB" },
+ { LV2_MIDI_CTL_MSB_DATA_ENTRY , "DataEntry_MSB" },
+ { LV2_MIDI_CTL_MSB_MAIN_VOLUME , "MainVolume_MSB" },
+ { LV2_MIDI_CTL_MSB_BALANCE , "Balance_MSB" },
+ { LV2_MIDI_CTL_MSB_PAN , "Panpot_MSB" },
+ { LV2_MIDI_CTL_MSB_EXPRESSION , "Expression_MSB" },
+ { LV2_MIDI_CTL_MSB_EFFECT1 , "Effect1_MSB" },
+ { LV2_MIDI_CTL_MSB_EFFECT2 , "Effect2_MSB" },
+ { LV2_MIDI_CTL_MSB_GENERAL_PURPOSE1 , "GeneralPurpose1_MSB" },
+ { LV2_MIDI_CTL_MSB_GENERAL_PURPOSE2 , "GeneralPurpose2_MSB" },
+ { LV2_MIDI_CTL_MSB_GENERAL_PURPOSE3 , "GeneralPurpose3_MSB" },
+ { LV2_MIDI_CTL_MSB_GENERAL_PURPOSE4 , "GeneralPurpose4_MSB" },
+
+ { LV2_MIDI_CTL_LSB_BANK , "BankSelection_LSB" },
+ { LV2_MIDI_CTL_LSB_MODWHEEL , "Modulation_LSB" },
+ { LV2_MIDI_CTL_LSB_BREATH , "Breath_LSB" },
+ { LV2_MIDI_CTL_LSB_FOOT , "Foot_LSB" },
+ { LV2_MIDI_CTL_LSB_PORTAMENTO_TIME , "PortamentoTime_LSB" },
+ { LV2_MIDI_CTL_LSB_DATA_ENTRY , "DataEntry_LSB" },
+ { LV2_MIDI_CTL_LSB_MAIN_VOLUME , "MainVolume_LSB" },
+ { LV2_MIDI_CTL_LSB_BALANCE , "Balance_LSB" },
+ { LV2_MIDI_CTL_LSB_PAN , "Panpot_LSB" },
+ { LV2_MIDI_CTL_LSB_EXPRESSION , "Expression_LSB" },
+ { LV2_MIDI_CTL_LSB_EFFECT1 , "Effect1_LSB" },
+ { LV2_MIDI_CTL_LSB_EFFECT2 , "Effect2_LSB" },
+ { LV2_MIDI_CTL_LSB_GENERAL_PURPOSE1 , "GeneralPurpose1_LSB" },
+ { LV2_MIDI_CTL_LSB_GENERAL_PURPOSE2 , "GeneralPurpose2_LSB" },
+ { LV2_MIDI_CTL_LSB_GENERAL_PURPOSE3 , "GeneralPurpose3_LSB" },
+ { LV2_MIDI_CTL_LSB_GENERAL_PURPOSE4 , "GeneralPurpose4_LSB" },
+
+ { LV2_MIDI_CTL_SUSTAIN , "SustainPedal" },
+ { LV2_MIDI_CTL_PORTAMENTO , "Portamento" },
+ { LV2_MIDI_CTL_SOSTENUTO , "Sostenuto" },
+ { LV2_MIDI_CTL_SOFT_PEDAL , "SoftPedal" },
+ { LV2_MIDI_CTL_LEGATO_FOOTSWITCH , "LegatoFootSwitch" },
+ { LV2_MIDI_CTL_HOLD2 , "Hold2" },
+
+ { LV2_MIDI_CTL_SC1_SOUND_VARIATION , "SoundVariation" },
+ { LV2_MIDI_CTL_SC3_RELEASE_TIME , "ReleaseTime" },
+ { LV2_MIDI_CTL_SC2_TIMBRE , "Timbre" },
+ { LV2_MIDI_CTL_SC4_ATTACK_TIME , "AttackTime" },
+ { LV2_MIDI_CTL_SC5_BRIGHTNESS , "Brightness" },
+ { LV2_MIDI_CTL_SC1_SOUND_VARIATION , "SC1" },
+ { LV2_MIDI_CTL_SC2_TIMBRE , "SC2" },
+ { LV2_MIDI_CTL_SC3_RELEASE_TIME , "SC3" },
+ { LV2_MIDI_CTL_SC4_ATTACK_TIME , "SC4" },
+ { LV2_MIDI_CTL_SC5_BRIGHTNESS , "SC5" },
+ { LV2_MIDI_CTL_SC6 , "SC6" },
+ { LV2_MIDI_CTL_SC7 , "SC7" },
+ { LV2_MIDI_CTL_SC8 , "SC8" },
+ { LV2_MIDI_CTL_SC9 , "SC9" },
+ { LV2_MIDI_CTL_SC10 , "SC10" },
+
+ { LV2_MIDI_CTL_GENERAL_PURPOSE5 , "GeneralPurpose5" },
+ { LV2_MIDI_CTL_GENERAL_PURPOSE6 , "GeneralPurpose6" },
+ { LV2_MIDI_CTL_GENERAL_PURPOSE7 , "GeneralPurpose7" },
+ { LV2_MIDI_CTL_GENERAL_PURPOSE8 , "GeneralPurpose8" },
+ { LV2_MIDI_CTL_PORTAMENTO_CONTROL , "PortamentoControl" },
+
+ { LV2_MIDI_CTL_E1_REVERB_DEPTH , "ReverbDepth" },
+ { LV2_MIDI_CTL_E2_TREMOLO_DEPTH , "TremoloDepth" },
+ { LV2_MIDI_CTL_E3_CHORUS_DEPTH , "ChorusDepth" },
+ { LV2_MIDI_CTL_E4_DETUNE_DEPTH , "DetuneDepth" },
+ { LV2_MIDI_CTL_E5_PHASER_DEPTH , "PhaserDepth" },
+ { LV2_MIDI_CTL_E1_REVERB_DEPTH , "E1" },
+ { LV2_MIDI_CTL_E2_TREMOLO_DEPTH , "E2" },
+ { LV2_MIDI_CTL_E3_CHORUS_DEPTH , "E3" },
+ { LV2_MIDI_CTL_E4_DETUNE_DEPTH , "E4" },
+ { LV2_MIDI_CTL_E5_PHASER_DEPTH , "E5" },
+
+ { LV2_MIDI_CTL_DATA_INCREMENT , "DataIncrement" },
+ { LV2_MIDI_CTL_DATA_DECREMENT , "DataDecrement" },
+
+ { LV2_MIDI_CTL_NRPN_LSB , "NRPN_LSB" },
+ { LV2_MIDI_CTL_NRPN_MSB , "NRPN_MSB" },
+
+ { LV2_MIDI_CTL_RPN_LSB , "RPN_LSB" },
+ { LV2_MIDI_CTL_RPN_MSB , "RPN_MSB" },
+
+ { LV2_MIDI_CTL_ALL_SOUNDS_OFF , "AllSoundsOff" },
+ { LV2_MIDI_CTL_RESET_CONTROLLERS , "ResetControllers" },
+ { LV2_MIDI_CTL_LOCAL_CONTROL_SWITCH , "LocalControlSwitch" },
+ { LV2_MIDI_CTL_ALL_NOTES_OFF , "AllNotesOff" },
+ { LV2_MIDI_CTL_OMNI_OFF , "OmniOff" },
+ { LV2_MIDI_CTL_OMNI_ON , "OmniOn" },
+ { LV2_MIDI_CTL_MONO1 , "Mono1" },
+ { LV2_MIDI_CTL_MONO2 , "Mono2" },
+
+ { 0, NULL} // sentinel
+};
diff --git a/api/api_midi.h b/api/api_midi.h
new file mode 100644
index 0000000..4e06da7
--- /dev/null
+++ b/api/api_midi.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _MOONY_API_MIDI_H
+#define _MOONY_API_MIDI_H
+
+#include <moony.h>
+
+typedef struct _midi_msg_t midi_msg_t;
+
+struct _midi_msg_t {
+ uint8_t type;
+ const char *key;
+};
+
+int
+_lmidiresponder(lua_State *L);
+
+extern const luaL_Reg lmidiresponder_mt [];
+
+extern const midi_msg_t midi_msgs [];
+extern const midi_msg_t controllers [];
+
+#endif
diff --git a/api/api_osc.c b/api/api_osc.c
new file mode 100644
index 0000000..8060bce
--- /dev/null
+++ b/api/api_osc.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <math.h>
+
+#include <api_osc.h>
+#include <api_atom.h>
+#include <api_forge.h>
+
+#include <osc.lv2/util.h>
+
+typedef struct _osc_responder_data_t osc_responder_data_t;
+
+struct _osc_responder_data_t {
+ moony_t *moony;
+ bool matched;
+};
+
+__realtime static inline bool
+_osc_path_has_wildcards(const char *path)
+{
+ if(strpbrk(path, "?*[{}]"))
+ return true;
+ return false;
+}
+
+//TODO can we rewrite this directly in C?
+const char *loscresponder_match =
+ "function __expand(v)\n"
+ " return coroutine.wrap(function()\n"
+ " local item = string.match(v, '%b{}')\n"
+ " if item then\n"
+ " for key in string.gmatch(item, '{?([^,}]*)') do\n"
+ " local vv = string.gsub(v, item, key)\n"
+ " for w in __expand(vv) do\n"
+ " coroutine.yield(w)\n"
+ " end\n"
+ " end\n"
+ " else\n"
+ " coroutine.yield(v)\n"
+ " end\n"
+ " end)\n"
+ "end\n"
+ "\n"
+ "function __match(v, o, ...)\n"
+ " local matched = false\n"
+ " v = string.gsub(v, '%?', '.')\n"
+ " v = string.gsub(v, '%*', '.*')\n"
+ " v = string.gsub(v, '%[%!', '[^')\n"
+ " v = '^' .. v .. '$'\n"
+ " for w in __expand(v) do\n"
+ " for k, x in pairs(o) do\n"
+ " if string.match(k, w) then\n"
+ " x(o, ...)\n"
+ " matched = matched or true\n"
+ " end\n"
+ " end\n"
+ " end\n"
+ " return matched\n"
+ "end";
+
+__realtime static inline void
+_loscresponder_method(const char *path, const LV2_Atom_Tuple *arguments, void *data)
+{
+ osc_responder_data_t *ord = data;
+ moony_t *moony = ord->moony;
+ lua_State *L = moony_current(moony);
+ //LV2_Atom_Forge *forge = &moony->forge;
+ LV2_OSC_URID *osc_urid = &moony->osc_urid;
+ //LV2_URID_Unmap *unmap = moony->unmap;
+
+ // 1: uservalue
+ // 2: frames
+ // 3: data
+
+ int has_wildcard = 0;
+ if(_osc_path_has_wildcards(path))
+ {
+ lua_getglobal(L, "__match"); // push pattern has_wildcard function
+ lua_pushstring(L, path); // path
+ has_wildcard = 1;
+ }
+ else if(lua_getfield(L, 1, path) == LUA_TNIL) // raw string match
+ {
+ lua_pop(L, 1); // nil
+ ord->matched = ord->matched || false;
+ return;
+ }
+
+ lua_pushvalue(L, 1); // self
+ lua_pushvalue(L, 2); // frames
+ lua_pushvalue(L, 3); // data
+
+ // push format string
+ luaL_Buffer B;
+ luaL_buffinit(L, &B);
+ LV2_ATOM_TUPLE_FOREACH(arguments, atom)
+ {
+ const LV2_OSC_Type type = lv2_osc_argument_type(osc_urid, atom);
+ if(type)
+ luaL_addchar(&B, type);
+ }
+ luaL_pushresult(&B);
+
+ int oldtop = lua_gettop(L);
+
+ LV2_ATOM_TUPLE_FOREACH(arguments, atom)
+ {
+ //const LV2_Atom_Object *obj= (const LV2_Atom_Object *)atom;
+
+ switch(lv2_osc_argument_type(osc_urid, atom))
+ {
+ case LV2_OSC_INT32:
+ {
+ int32_t i;
+ if(lv2_osc_int32_get(osc_urid, atom, &i))
+ lua_pushinteger(L, i);
+ break;
+ }
+ case LV2_OSC_FLOAT:
+ {
+ float f;
+ if(lv2_osc_float_get(osc_urid, atom, &f))
+ lua_pushnumber(L, f);
+ break;
+ }
+ case LV2_OSC_STRING:
+ {
+ const char *s;
+ if(lv2_osc_string_get(osc_urid, atom, &s))
+ lua_pushstring(L, s);
+ break;
+ }
+ case LV2_OSC_BLOB:
+ {
+ const uint8_t *b;
+ uint32_t len;
+ if(lv2_osc_blob_get(osc_urid, atom, &len, &b))
+ lua_pushlstring(L, (const char *)b, len);
+ break;
+ }
+
+ case LV2_OSC_INT64:
+ {
+ int64_t h;
+ if(lv2_osc_int64_get(osc_urid, atom, &h))
+ lua_pushinteger(L, h);
+ break;
+ }
+ case LV2_OSC_DOUBLE:
+ {
+ double d;
+ if(lv2_osc_double_get(osc_urid, atom, &d))
+ lua_pushnumber(L, d);
+ break;
+ }
+ case LV2_OSC_TIMETAG:
+ {
+ LV2_OSC_Timetag tt;
+ if(lv2_osc_timetag_get(osc_urid, atom, &tt))
+ lua_pushinteger(L, lv2_osc_timetag_parse(&tt));
+ break;
+ }
+
+ case LV2_OSC_TRUE:
+ {
+ lua_pushboolean(L, 1);
+ break;
+ }
+ case LV2_OSC_FALSE:
+ {
+ lua_pushboolean(L, 0);
+ break;
+ }
+ case LV2_OSC_NIL:
+ {
+ lua_pushnil(L);
+ break;
+ }
+ case LV2_OSC_IMPULSE:
+ {
+ lua_pushnumber(L, HUGE_VAL);
+ break;
+ }
+
+ case LV2_OSC_SYMBOL:
+ {
+ LV2_URID S;
+ if(lv2_osc_symbol_get(&moony->osc_urid, atom, &S))
+ lua_pushinteger(L, S);
+ break;
+ }
+ case LV2_OSC_MIDI:
+ {
+ const uint8_t *m;
+ uint32_t len;
+ if(lv2_osc_midi_get(&moony->osc_urid, atom, &len, &m))
+ lua_pushlstring(L, (const char *)m, len);
+ break;
+ }
+ case LV2_OSC_CHAR:
+ {
+ char c;
+ if(lv2_osc_char_get(&moony->osc_urid, atom, &c))
+ lua_pushinteger(L, c);
+ break;
+ }
+ case LV2_OSC_RGBA:
+ {
+ uint8_t r, g, b, a;
+ if(lv2_osc_rgba_get(&moony->osc_urid, atom, &r, &g, &b, &a))
+ lua_pushinteger(L, ((int64_t)r << 24) | (g << 16) | (b << 8) | a);
+ break;
+ }
+ }
+ }
+
+ lua_call(L, 4 + has_wildcard + lua_gettop(L) - oldtop, has_wildcard);
+
+ if(has_wildcard)
+ {
+ ord->matched = ord->matched || lua_toboolean(L, -1);
+ lua_pop(L, 1);
+ }
+ else // raw string
+ {
+ ord->matched = ord->matched || true;
+ }
+}
+
+__realtime static int
+_loscresponder__call(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ const bool *through = lua_touserdata(L, 1);
+
+ lua_settop(L, 4); // discard superfluous arguments
+ // 1: self
+ // 2: frames
+ // 3: data
+ // 4: atom
+
+ latom_t *latom = NULL;
+ if(luaL_testudata(L, 4, "latom"))
+ latom = lua_touserdata(L, 4);
+ lua_pop(L, 1); // atom
+
+ // check for valid atom and event type
+ const LV2_Atom_Object *obj = latom ? (const LV2_Atom_Object *)latom->atom : NULL;
+
+ if( !latom
+ || !lv2_atom_forge_is_object_type(&moony->forge, obj->atom.type)
+ || !(lv2_osc_is_message_or_bundle_type(&moony->osc_urid, obj->body.otype)))
+ {
+ lua_pushboolean(L, 0); // not handled
+ return 1;
+ }
+
+ // replace self with its uservalue
+ lua_getuservalue(L, 1);
+ lua_replace(L, 1);
+
+ osc_responder_data_t ord = {
+ .moony = moony,
+ .matched = false
+ };
+
+ lv2_osc_body_unroll(&moony->osc_urid, latom->atom->size, latom->body.obj,
+ _loscresponder_method, &ord);
+
+ if(!ord.matched && *through) // not handled and through mode
+ {
+ const int64_t frames = luaL_checkinteger(L, 2);
+ lforge_t *lforge = luaL_checkudata(L, 3, "lforge");
+
+ if(frames < lforge->last.frames)
+ luaL_error(L, "invalid frame time, must not decrease");
+ lforge->last.frames = frames;
+
+ if( !lv2_atom_forge_frame_time(lforge->forge, frames)
+ || !lv2_atom_forge_atom(lforge->forge, latom->atom->size, latom->atom->type)
+ || !lv2_atom_forge_write(lforge->forge, latom->body.raw, latom->atom->size) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ lua_pushboolean(L, 1); // handled
+ lua_pushboolean(L, ord.matched); // matched a registered path
+ return 2;
+}
+
+__realtime int
+_loscresponder(lua_State *L)
+{
+ //moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ lua_settop(L, 2); // discard superfluous arguments
+
+ const bool _through = lua_toboolean(L, 2);
+ lua_pop(L, 1); // bool
+
+ // o = new
+ bool *through = lua_newuserdata(L, sizeof(bool));
+ *through= _through;
+
+ // o.uservalue = uservalue
+ lua_insert(L, 1);
+ lua_setuservalue(L, -2);
+
+ // setmetatable(o, self)
+ luaL_getmetatable(L, "loscresponder");
+ lua_setmetatable(L, -2);
+
+ // return o
+ return 1;
+}
+
+const luaL_Reg loscresponder_mt [] = {
+ {"__call", _loscresponder__call},
+ {NULL, NULL}
+};
diff --git a/api/api_osc.h b/api/api_osc.h
new file mode 100644
index 0000000..409d290
--- /dev/null
+++ b/api/api_osc.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _MOONY_API_OSC_H
+#define _MOONY_API_OSC_H
+
+#include <moony.h>
+
+extern const char *loscresponder_match;
+
+int
+_loscresponder(lua_State *L);
+
+extern const luaL_Reg loscresponder_mt [];
+
+#endif
diff --git a/api/api_parameter.c b/api/api_parameter.c
new file mode 100644
index 0000000..702018e
--- /dev/null
+++ b/api/api_parameter.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <api_parameter.h>
+
+__realtime static int
+_lparameter__index(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ // 1: self
+ // 2: key
+
+ if(lua_isinteger(L, 2) && (lua_tointeger(L, 2) == moony->uris.rdf_value))
+ {
+ if(lua_geti(L, 1, moony->uris.patch.get) == LUA_TFUNCTION)
+ {
+ lua_pushvalue(L, 1); // self
+ lua_call(L, 1, 1);
+
+ return 1;
+ }
+ }
+
+ lua_pushnil(L);
+ return 1;
+}
+
+__realtime static int
+_lparameter__newindex(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ // 1: self
+ // 2: key
+ // 3: value
+
+ if(lua_isinteger(L, 2) && (lua_tointeger(L, 2) == moony->uris.rdf_value))
+ {
+ if(lua_geti(L, 1, moony->uris.patch.set) == LUA_TFUNCTION)
+ {
+ lua_pushvalue(L, 1); // self
+ lua_pushvalue(L, 3); // value
+ lua_call(L, 2, 0);
+ }
+ }
+
+ return 0;
+}
+
+__realtime static int
+_lparameter__call(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ lua_settop(L, 2); // discard superfluous arguments
+ // 1: self
+ // 2: value or nil
+
+ // push old value on stack
+ lua_geti(L, 1, moony->uris.rdf_value);
+
+ if(!lua_isnil(L, 2)) // has value to set
+ {
+ lua_pushvalue(L, 2);
+ lua_seti(L, 1, moony->uris.rdf_value); // param[RDF.value] = value
+ }
+
+ return 1;
+}
+
+__realtime int
+_lparameter(lua_State *L)
+{
+ //moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ lua_settop(L, 1); // discard superfluous arguments
+
+ // o = o or {}
+ if(lua_isnil(L, 1))
+ {
+ lua_pop(L, 1); // nil
+ lua_newtable(L);
+ }
+
+ // setmetatable(o, self)
+ luaL_getmetatable(L, "lparameter");
+ lua_setmetatable(L, -2);
+
+ // return o
+ return 1;
+}
+
+const luaL_Reg lparameter_mt [] = {
+ {"__index", _lparameter__index},
+ {"__newindex", _lparameter__newindex},
+ {"__call", _lparameter__call},
+ {NULL, NULL}
+};
diff --git a/api/api_parameter.h b/api/api_parameter.h
new file mode 100644
index 0000000..24a5ae4
--- /dev/null
+++ b/api/api_parameter.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _MOONY_API_PARAMETER_H
+#define _MOONY_API_PARAMETER_H
+
+#include <moony.h>
+
+int
+_lparameter(lua_State *L);
+
+extern const luaL_Reg lparameter_mt [];
+
+#endif
diff --git a/api/api_stash.c b/api/api_stash.c
new file mode 100644
index 0000000..fed7d7b
--- /dev/null
+++ b/api/api_stash.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <api_stash.h>
+
+__realtime int
+_lstash__gc(lua_State *L)
+{
+ lstash_t *lstash = lua_touserdata(L, 1);
+ atom_ser_t *ser = &lstash->ser;
+
+ if(ser->buf)
+ moony_rt_free(ser->data, ser->buf, ser->size);
+
+ return 0;
+}
+
+__realtime int
+_lstash_write(lua_State *L)
+{
+ lstash_t *lstash = lua_touserdata(L, 1);
+ atom_ser_t *ser = &lstash->ser;
+ lforge_t *lforge = &lstash->lforge;
+
+ lforge->depth = 0;
+ lforge->last.frames = 0;
+ lforge->forge = &lstash->forge;
+
+ ser->offset = 0; // reset stash pointer
+ lv2_atom_forge_set_sink(&lstash->forge, _sink_rt, _deref, ser);
+
+ LV2_Atom *atom = (LV2_Atom *)ser->buf;
+ atom->type = 0;
+ atom->size = 0;
+
+ luaL_getmetatable(L, "lforge");
+ lua_setmetatable(L, 1);
+
+ return 1;
+}
+
+__realtime int
+_lstash_read(lua_State *L)
+{
+ lstash_t *lstash = lua_touserdata(L, 1);
+ atom_ser_t *ser = &lstash->ser;
+ latom_t *latom = &lstash->latom;
+
+ latom->atom = (const LV2_Atom *)ser->buf;
+ latom->body.raw = LV2_ATOM_BODY_CONST(latom->atom);
+
+ luaL_getmetatable(L, "latom");
+ lua_setmetatable(L, 1);
+
+ return 1;
+}
+
+__realtime int
+_lstash(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+ moony_vm_t *vm = lua_touserdata(L, lua_upvalueindex(2));
+
+ lstash_t *lstash = moony_newuserdata(L, moony, MOONY_UDATA_STASH, false);
+
+ // initialize forge (URIDs)
+ memcpy(&lstash->forge, &moony->forge, sizeof(LV2_Atom_Forge));
+
+ // initialize memory pool
+ atom_ser_t *ser = &lstash->ser;
+ ser->data = vm;
+ ser->size = 1024;
+ ser->offset = 0; // reset stash pointer
+ ser->buf = moony_rt_alloc(vm, ser->size);
+
+ if(!ser->buf)
+ lua_pushnil(L); // memory allocation failed
+
+ // start off as forge
+ _lstash_write(L);
+
+ return 1;
+}
+
+const luaL_Reg lstash_mt [] = {
+ {NULL, NULL}
+};
diff --git a/api/api_stash.h b/api/api_stash.h
new file mode 100644
index 0000000..8242a64
--- /dev/null
+++ b/api/api_stash.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _MOONY_API_STASH_H
+#define _MOONY_API_STASH_H
+
+#include <moony.h>
+
+#include <api_atom.h>
+#include <api_forge.h>
+
+typedef struct _lstash_t lstash_t;
+
+struct _lstash_t {
+ union {
+ latom_t latom;
+ lforge_t lforge;
+ };
+
+ atom_ser_t ser;
+ LV2_Atom_Forge forge;
+};
+
+int
+_lstash(lua_State *L);
+int
+_lstash__gc(lua_State *L);
+int
+_lstash_write(lua_State *L);
+int
+_lstash_read(lua_State *L);
+
+extern const luaL_Reg lstash_mt [];
+
+#endif
diff --git a/api/api_state.c b/api/api_state.c
new file mode 100644
index 0000000..18f4bb3
--- /dev/null
+++ b/api/api_state.c
@@ -0,0 +1,847 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <api_state.h>
+#include <api_atom.h>
+#include <api_forge.h>
+
+__realtime static inline void
+_lstateresponder_register_access(lua_State *L, moony_t *moony, int64_t frames,
+ lforge_t *lforge, const LV2_Atom_URID *subject, LV2_URID access, int32_t sequence_num)
+{
+ LV2_Atom_Forge_Frame obj_frame;
+ LV2_Atom_Forge_Frame add_frame;
+ LV2_Atom_Forge_Frame rem_frame;
+
+ // iterate over properties
+ lua_pushnil(L); // first key
+ while(lua_next(L, -2))
+ {
+ // uses 'key' (at index -2) and 'value' (at index -1)
+ const LV2_URID key = luaL_checkinteger(L, -2);
+
+ const char *label = ""; // fallback
+ LV2_URID range = 0; // fallback
+ LV2_URID child_type = 0; // fallback
+
+ if(lua_geti(L, -1, moony->uris.rdfs_label) == LUA_TSTRING)
+ label = lua_tostring(L, -1);
+ lua_pop(L, 1); // label
+
+ if(lua_geti(L, -1, moony->uris.rdfs_range) == LUA_TNUMBER)
+ range = lua_tointeger(L, -1);
+ lua_pop(L, 1); // range
+
+ if(lua_geti(L, -1, moony->uris.atom_child_type) == LUA_TNUMBER)
+ child_type= lua_tointeger(L, -1);
+ lua_pop(L, 1); // child_type
+
+ if( !lv2_atom_forge_frame_time(lforge->forge, frames)
+ || !lv2_atom_forge_object(lforge->forge, &obj_frame, 0, moony->uris.patch.patch) )
+ luaL_error(L, forge_buffer_overflow);
+ {
+ if(subject)
+ {
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.subject)
+ || !lv2_atom_forge_urid(lforge->forge, subject->body) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ if(sequence_num)
+ {
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.sequence)
+ || !lv2_atom_forge_int(lforge->forge, sequence_num) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.remove)
+ || !lv2_atom_forge_object(lforge->forge, &rem_frame, 0, 0)
+
+ || !lv2_atom_forge_key(lforge->forge, access)
+ || !lv2_atom_forge_urid(lforge->forge, key) )
+ luaL_error(L, forge_buffer_overflow);
+ lv2_atom_forge_pop(lforge->forge, &rem_frame); // patch:remove
+
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.add)
+ || !lv2_atom_forge_object(lforge->forge, &add_frame, 0, 0)
+
+ || !lv2_atom_forge_key(lforge->forge, access)
+ || !lv2_atom_forge_urid(lforge->forge, key) )
+ luaL_error(L, forge_buffer_overflow);
+ lv2_atom_forge_pop(lforge->forge, &add_frame); // patch:add
+ }
+ lv2_atom_forge_pop(lforge->forge, &obj_frame); // patch:patch
+
+ if( !lv2_atom_forge_frame_time(lforge->forge, frames)
+ || !lv2_atom_forge_object(lforge->forge, &obj_frame, 0, moony->uris.patch.patch) )
+ luaL_error(L, forge_buffer_overflow);
+ {
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.subject)
+ || !lv2_atom_forge_urid(lforge->forge, key) )
+ luaL_error(L, forge_buffer_overflow);
+
+ if(sequence_num)
+ {
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.sequence)
+ || !lv2_atom_forge_int(lforge->forge, sequence_num) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.remove)
+ || !lv2_atom_forge_object(lforge->forge, &rem_frame, 0, 0) )
+ luaL_error(L, forge_buffer_overflow);
+ {
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.rdfs_label)
+ || !lv2_atom_forge_urid(lforge->forge, moony->uris.patch.wildcard)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.rdfs_range)
+ || !lv2_atom_forge_urid(lforge->forge, moony->uris.patch.wildcard)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.rdfs_comment)
+ || !lv2_atom_forge_urid(lforge->forge, moony->uris.patch.wildcard)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.lv2_minimum)
+ || !lv2_atom_forge_urid(lforge->forge, moony->uris.patch.wildcard)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.lv2_maximum)
+ || !lv2_atom_forge_urid(lforge->forge, moony->uris.patch.wildcard)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.units_unit)
+ || !lv2_atom_forge_urid(lforge->forge, moony->uris.patch.wildcard)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.units_symbol)
+ || !lv2_atom_forge_urid(lforge->forge, moony->uris.patch.wildcard)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.moony_color)
+ || !lv2_atom_forge_urid(lforge->forge, moony->uris.patch.wildcard)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.moony_syntax)
+ || !lv2_atom_forge_urid(lforge->forge, moony->uris.patch.wildcard)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.lv2_scale_point)
+ || !lv2_atom_forge_urid(lforge->forge, moony->uris.patch.wildcard) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+ lv2_atom_forge_pop(lforge->forge, &rem_frame); // patch:remove
+
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.add)
+ || !lv2_atom_forge_object(lforge->forge, &add_frame, 0, 0) )
+ luaL_error(L, forge_buffer_overflow);
+ {
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.rdfs_label)
+ || !lv2_atom_forge_string(lforge->forge, label, strlen(label))
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.rdfs_range)
+ || !lv2_atom_forge_urid(lforge->forge, range) )
+ luaL_error(L, forge_buffer_overflow);
+
+ if(lua_geti(L, -1, moony->uris.rdfs_comment) == LUA_TSTRING)
+ {
+ size_t comment_size;
+ const char *comment = lua_tolstring(L, -1, &comment_size);
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.rdfs_comment)
+ || !lv2_atom_forge_string(lforge->forge, comment, comment_size) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+ lua_pop(L, 1); // comment
+
+ const LV2_URID range2 = (range == lforge->forge->Vector)
+ ? child_type
+ : range;
+
+ if(lua_geti(L, -1, moony->uris.lv2_minimum) != LUA_TNIL)
+ {
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.lv2_minimum)
+ || !_lforge_basic(L, -1, lforge->forge, range2, 0) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+ lua_pop(L, 1); // minimum
+
+ if(lua_geti(L, -1, moony->uris.lv2_maximum) != LUA_TNIL)
+ {
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.lv2_maximum)
+ || !_lforge_basic(L, -1, lforge->forge, range2, 0) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+ lua_pop(L, 1); // maximum
+
+ if(lua_geti(L, -1, moony->uris.units_unit) == LUA_TNUMBER)
+ {
+ const LV2_URID unit = lua_tointeger(L, -1);
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.units_unit)
+ || !lv2_atom_forge_urid(lforge->forge, unit) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+ lua_pop(L, 1); // unit
+
+ if(lua_geti(L, -1, moony->uris.units_symbol) == LUA_TSTRING)
+ {
+ size_t len;
+ const char *symbol = lua_tolstring(L, -1, &len);
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.units_symbol)
+ || !lv2_atom_forge_string(lforge->forge, symbol, len) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+ lua_pop(L, 1); // symbol
+
+ if(lua_geti(L, -1, moony->uris.moony_color) == LUA_TNUMBER)
+ {
+ const uint32_t col = lua_tointeger(L, -1);
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.moony_color)
+ || !lv2_atom_forge_long(lforge->forge, col) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+ lua_pop(L, 1); // symbol
+
+ if(lua_geti(L, -1, moony->uris.moony_syntax) == LUA_TNUMBER)
+ {
+ const LV2_URID syntax = lua_tointeger(L, -1);
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.moony_syntax)
+ || !lv2_atom_forge_urid(lforge->forge, syntax) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+ lua_pop(L, 1); // symbol
+
+ if(lua_geti(L, -1, moony->uris.lv2_scale_point) != LUA_TNIL)
+ {
+ LV2_Atom_Forge_Frame tuple_frame;
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.lv2_scale_point)
+ || !lv2_atom_forge_tuple(lforge->forge, &tuple_frame) )
+ luaL_error(L, forge_buffer_overflow);
+
+ // iterate over properties
+ lua_pushnil(L); // first key
+ while(lua_next(L, -2))
+ {
+ // uses 'key' (at index -2) and 'value' (at index -1)
+ size_t point_size;
+ const char *point = luaL_checklstring(L, -2, &point_size);
+ LV2_Atom_Forge_Frame scale_point_frame;
+
+ if( !lv2_atom_forge_object(lforge->forge, &scale_point_frame, 0, 0)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.rdfs_label)
+ || !lv2_atom_forge_string(lforge->forge, point, point_size)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.rdf_value)
+ || !_lforge_basic(L, -1, lforge->forge, range, 0) )
+ luaL_error(L, forge_buffer_overflow);
+
+ lv2_atom_forge_pop(lforge->forge, &scale_point_frame); // core:scalePoint
+
+ // removes 'value'; keeps 'key' for next iteration
+ lua_pop(L, 1);
+ }
+
+ lv2_atom_forge_pop(lforge->forge, &tuple_frame);
+ }
+ lua_pop(L, 1); // scale_points
+ }
+ lv2_atom_forge_pop(lforge->forge, &add_frame); // patch:add
+ }
+ lv2_atom_forge_pop(lforge->forge, &obj_frame); // patch:patch
+
+ //FIXME also send patch:set ?
+
+ // removes 'value'; keeps 'key' for next iteration
+ lua_pop(L, 1);
+ }
+}
+
+__realtime static inline void
+_lstateresponder_reg(lua_State *L, moony_t *moony, int64_t frames,
+ lforge_t *lforge, const LV2_Atom_URID *subject, int32_t sequence_num)
+{
+ LV2_Atom_Forge_Frame obj_frame;
+ LV2_Atom_Forge_Frame add_frame;
+ LV2_Atom_Forge_Frame rem_frame;
+
+ // clear all properties
+ if( !lv2_atom_forge_frame_time(lforge->forge, frames)
+ || !lv2_atom_forge_object(lforge->forge, &obj_frame, 0, moony->uris.patch.patch) )
+ luaL_error(L, forge_buffer_overflow);
+ {
+ if(subject)
+ {
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.subject)
+ || !lv2_atom_forge_urid(lforge->forge, subject->body) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ if(sequence_num)
+ {
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.sequence)
+ || !lv2_atom_forge_int(lforge->forge, sequence_num) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.remove)
+ || !lv2_atom_forge_object(lforge->forge, &rem_frame, 0, 0)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.patch.writable)
+ || !lv2_atom_forge_urid(lforge->forge, moony->uris.patch.wildcard)
+
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.patch.readable)
+ || !lv2_atom_forge_urid(lforge->forge, moony->uris.patch.wildcard) )
+ luaL_error(L, forge_buffer_overflow);
+ lv2_atom_forge_pop(lforge->forge, &rem_frame); // patch:remove
+
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.add)
+ || !lv2_atom_forge_object(lforge->forge, &add_frame, 0, 0) )
+ luaL_error(L, forge_buffer_overflow);
+ lv2_atom_forge_pop(lforge->forge, &add_frame); // patch:add
+ }
+ lv2_atom_forge_pop(lforge->forge, &obj_frame); // patch:patch
+
+ if(lua_geti(L, 1, moony->uris.patch.writable) != LUA_TNIL)
+ {
+ _lstateresponder_register_access(L, moony, frames, lforge, subject,
+ moony->uris.patch.writable, sequence_num);
+ }
+ lua_pop(L, 1); // nil || table
+
+ if(lua_geti(L, 1, moony->uris.patch.readable) != LUA_TNIL)
+ {
+ _lstateresponder_register_access(L, moony, frames, lforge, subject,
+ moony->uris.patch.readable, sequence_num);
+ }
+ lua_pop(L, 1); // nil || table
+}
+
+__realtime static void
+_lstateresponder_error(lua_State *L, lforge_t *lforge, moony_t *moony,
+ int64_t frames, int32_t sequence_num)
+{
+ LV2_Atom_Forge_Frame obj_frame;
+ if( !lv2_atom_forge_frame_time(lforge->forge, frames)
+ || !lv2_atom_forge_object(lforge->forge, &obj_frame, 0, moony->uris.patch.error) )
+ luaL_error(L, forge_buffer_overflow);
+
+ if(sequence_num)
+ {
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.sequence)
+ || !lv2_atom_forge_int(lforge->forge, sequence_num) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ lv2_atom_forge_pop(lforge->forge, &obj_frame);
+}
+
+__realtime static void
+_lstateresponder_ack(lua_State *L, lforge_t *lforge, moony_t *moony,
+ int64_t frames, int32_t sequence_num)
+{
+ LV2_Atom_Forge_Frame obj_frame;
+ if( !lv2_atom_forge_frame_time(lforge->forge, frames)
+ || !lv2_atom_forge_object(lforge->forge, &obj_frame, 0, moony->uris.patch.ack) )
+ luaL_error(L, forge_buffer_overflow);
+
+ if(sequence_num)
+ {
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.sequence)
+ || !lv2_atom_forge_int(lforge->forge, sequence_num) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ lv2_atom_forge_pop(lforge->forge, &obj_frame);
+}
+
+__realtime static int
+_lstateresponder__call(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ lua_settop(L, 4); // discard superfluous arguments
+ // 1: self
+ // 2: frames
+ // 3: forge
+ // 4: atom
+
+ int64_t frames = luaL_checkinteger(L, 2);
+ lforge_t *lforge = luaL_checkudata(L, 3, "lforge");
+ latom_t *latom = luaL_checkudata(L, 4, "latom");
+ lua_pop(L, 1); // atom
+
+ // replace self with its uservalue
+ lua_getuservalue(L, 1);
+ lua_replace(L, 1);
+
+ if(lv2_atom_forge_is_object_type(lforge->forge, latom->atom->type))
+ {
+ if(latom->body.obj->otype == moony->uris.patch.get)
+ {
+ const LV2_Atom_URID *subject = NULL;
+ const LV2_Atom_URID *property = NULL;
+ const LV2_Atom_Int *sequence = NULL;
+
+ lv2_atom_object_body_get(latom->atom->size, latom->body.obj,
+ moony->uris.patch.subject, &subject,
+ moony->uris.patch.property, &property,
+ moony->uris.patch.sequence, &sequence,
+ 0);
+
+ int32_t sequence_num = 0;
+ if(sequence && (sequence->atom.type == moony->forge.Int) )
+ sequence_num = sequence->body;
+
+ if(!subject || ((subject->atom.type == moony->forge.URID) && (subject->body == moony->uris.patch.self)) )
+ {
+ if(!property)
+ {
+ // register state
+ _lstateresponder_reg(L, moony, frames, lforge, subject, sequence_num);
+
+ lua_pushboolean(L, 1); // handled
+ return 1;
+ }
+ else if(property->atom.type == moony->forge.URID)
+ {
+ bool found_it = false;
+
+ if(lua_geti(L, 1, moony->uris.patch.writable) != LUA_TNIL)
+ {
+ if(lua_geti(L, -1, property->body) != LUA_TNIL)
+ found_it = true;
+ else
+ lua_pop(L, 1); // nil
+ }
+ else
+ lua_pop(L, 1); // nil
+
+ if(!found_it)
+ {
+ if(lua_geti(L, 1, moony->uris.patch.readable) != LUA_TNIL)
+ {
+ if(lua_geti(L, -1, property->body) != LUA_TNIL)
+ found_it = true;
+ else
+ lua_pop(L, 1); // readable, property nil
+ }
+ else
+ lua_pop(L, 1); // nil
+ }
+
+ if(found_it)
+ {
+ LV2_URID range = 0; // fallback
+ LV2_URID child_type = 0; //fallback
+
+ // get atom type
+ if(lua_geti(L, -1, moony->uris.rdfs_range) == LUA_TNUMBER)
+ range = lua_tointeger(L, -1);
+ lua_pop(L, 1); // range
+
+ // get child type
+ if(lua_geti(L, -1, moony->uris.atom_child_type) == LUA_TNUMBER)
+ child_type = lua_tointeger(L, -1);
+ lua_pop(L, 1); // child_type
+
+ LV2_Atom_Forge_Frame obj_frame;
+
+ if( !lv2_atom_forge_frame_time(lforge->forge, frames)
+ || !lv2_atom_forge_object(lforge->forge, &obj_frame, 0, moony->uris.patch.set) )
+ luaL_error(L, forge_buffer_overflow);
+ {
+ if(subject)
+ {
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.subject)
+ || !lv2_atom_forge_urid(lforge->forge, subject->body) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ if(sequence_num)
+ {
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.sequence)
+ || !lv2_atom_forge_int(lforge->forge, sequence_num) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.property)
+ || !lv2_atom_forge_urid(lforge->forge, property->body) )
+ luaL_error(L, forge_buffer_overflow);
+
+ if(lua_geti(L, -1, moony->uris.rdf_value) != LUA_TNIL)
+ {
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.value)
+ || !_lforge_basic(L, -1, lforge->forge, range, child_type) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+ lua_pop(L, 1); // value
+ }
+ lv2_atom_forge_pop(lforge->forge, &obj_frame);
+
+ lua_pushboolean(L, 1); // handled
+ return 1;
+ }
+ }
+
+ if(sequence_num)
+ _lstateresponder_error(L, lforge, moony, frames, sequence_num);
+ }
+ }
+ else if(latom->body.obj->otype == moony->uris.patch.set)
+ {
+ const LV2_Atom_URID *subject = NULL;
+ const LV2_Atom_URID *property = NULL;
+ const LV2_Atom_Int *sequence = NULL;
+ const LV2_Atom *value = NULL;
+
+ lv2_atom_object_body_get(latom->atom->size, latom->body.obj,
+ moony->uris.patch.subject, &subject,
+ moony->uris.patch.property, &property,
+ moony->uris.patch.sequence, &sequence,
+ moony->uris.patch.value, &value,
+ 0);
+
+ int32_t sequence_num = 0;
+ if(sequence && (sequence->atom.type == moony->forge.Int) )
+ sequence_num = sequence->body;
+
+ if(!subject || ((subject->atom.type == moony->forge.URID) && (subject->body == moony->uris.patch.self)) )
+ {
+ if(property && (property->atom.type == moony->forge.URID) && value)
+ {
+ if( (lua_geti(L, 1, moony->uris.patch.writable) != LUA_TNIL)
+ && (lua_geti(L, -1, property->body) != LUA_TNIL) ) // self[property]
+ {
+ _latom_value(L, value);
+ lua_seti(L, -2, moony->uris.rdf_value); // self[property][RDF.value] = value
+
+ if(sequence_num)
+ _lstateresponder_ack(L, lforge, moony, frames, sequence_num);
+
+ lua_pushboolean(L, 1); // handled
+ return 1;
+ }
+ }
+
+ if(sequence_num)
+ _lstateresponder_error(L, lforge, moony, frames, sequence_num);
+ }
+ }
+ else if(latom->body.obj->otype == moony->uris.patch.put)
+ {
+ const LV2_Atom_Object *patch_body = NULL;
+ const LV2_Atom_Int *sequence = NULL;
+
+ lv2_atom_object_body_get(latom->atom->size, latom->body.obj,
+ moony->uris.patch.body, &patch_body,
+ moony->uris.patch.sequence, &sequence,
+ NULL);
+
+ int32_t sequence_num = 0;
+ if(sequence && (sequence->atom.type == moony->forge.Int) )
+ sequence_num = sequence->body;
+
+ if(patch_body && (patch_body->atom.type == moony->forge.Object) )
+ {
+ LV2_ATOM_OBJECT_FOREACH(patch_body, prop)
+ {
+ const LV2_URID property = prop->key;
+ const LV2_Atom *value = &prop->value;
+
+ if( (lua_geti(L, 1, moony->uris.patch.writable) != LUA_TNIL)
+ && (lua_geti(L, -1, property) != LUA_TNIL) ) // self[property]
+ {
+ _latom_value(L, value);
+ lua_seti(L, -2, moony->uris.rdf_value); // self[property][RDF.value] = value
+ }
+ }
+
+ if(sequence_num)
+ _lstateresponder_ack(L, lforge, moony, frames, sequence_num);
+
+ lua_pushboolean(L, 1); // handled
+ return 1;
+ }
+
+ if(sequence_num)
+ _lstateresponder_error(L, lforge, moony, frames, sequence_num);
+ }
+ }
+
+ lua_pushboolean(L, 0); // not handled
+ return 1;
+}
+
+__realtime static int
+_lstateresponder_register(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ lua_settop(L, 3); // discard superfluous arguments
+ // 1: self
+ // 2: frames
+ // 3: forge
+
+ // replace self with its uservalue
+ lua_getuservalue(L, 1);
+ lua_replace(L, 1);
+
+ int64_t frames = luaL_checkinteger(L, 2);
+ lforge_t *lforge = luaL_checkudata(L, 3, "lforge");
+
+ // fake subject
+ const LV2_Atom_URID subject = {
+ .atom = {
+ .size = sizeof(uint32_t),
+ .type = lforge->forge->URID
+ },
+ .body = moony->uris.patch.self
+ };
+
+ // register state
+ _lstateresponder_reg(L, moony, frames, lforge, &subject, 0); //TODO use patch:sequenceNumber
+
+ return 1; // forge
+}
+
+__realtime static int
+_lstateresponder_sync(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ lua_settop(L, 3); // discard superfluous arguments
+ // 1: self
+ // 2: frames
+ // 3: forge
+ //FIXME support syncing specific parameter only
+
+ // replace self with its uservalue
+ lua_getuservalue(L, 1);
+ lua_replace(L, 1);
+
+ int64_t frames = luaL_checkinteger(L, 2);
+ lforge_t *lforge = luaL_checkudata(L, 3, "lforge");
+
+ LV2_Atom_Forge_Frame obj_frame;
+
+ if( !lv2_atom_forge_frame_time(lforge->forge, frames)
+ || !lv2_atom_forge_object(lforge->forge, &obj_frame, 0, moony->uris.patch.put) )
+ luaL_error(L, forge_buffer_overflow);
+ {
+ LV2_Atom_Forge_Frame body_frame;
+
+ if( !lv2_atom_forge_key(lforge->forge, moony->uris.patch.subject)
+ || !lv2_atom_forge_urid(lforge->forge, moony->uris.patch.self)
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.patch.sequence)
+ || !lv2_atom_forge_int(lforge->forge, 0) //TODO
+ || !lv2_atom_forge_key(lforge->forge, moony->uris.patch.body)
+ || !lv2_atom_forge_object(lforge->forge, &body_frame, 0, 0) )
+ luaL_error(L, forge_buffer_overflow);
+ {
+ if(lua_geti(L, 1, moony->uris.patch.writable) != LUA_TNIL)
+ {
+ // iterate over writable properties
+ lua_pushnil(L);
+ while(lua_next(L, -2))
+ {
+ const LV2_URID key = lua_tointeger(L, -2); // key
+ LV2_URID range = 0; // fallback
+ LV2_URID child_type = 0; // fallback
+
+ if(lua_geti(L, -1, moony->uris.rdfs_range) == LUA_TNUMBER) // prop[RDFS.range]
+ range = lua_tointeger(L, -1);
+ lua_pop(L, 1); // range
+
+ if(lua_geti(L, -1, moony->uris.atom_child_type) == LUA_TNUMBER) // prop[Atom.childType]
+ child_type = lua_tointeger(L, -1);
+ lua_pop(L, 1); // child_type
+
+ if(lua_geti(L, -1, moony->uris.rdf_value) != LUA_TNIL)
+ {
+ if( !lv2_atom_forge_key(lforge->forge, key)
+ || !_lforge_basic(L, -1, lforge->forge, range, child_type) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+ lua_pop(L, 1); // nil || rdf_value
+
+ lua_pop(L, 1); // removes 'value', keeps 'key' for next iteration
+ }
+ }
+ lua_pop(L, 1); // nil || writable
+
+ if(lua_geti(L, 1, moony->uris.patch.readable) != LUA_TNIL)
+ {
+ // iterate over writable properties
+ lua_pushnil(L);
+ while(lua_next(L, -2))
+ {
+ const LV2_URID key = lua_tointeger(L, -2); // key
+ LV2_URID range = 0; // fallback
+ LV2_URID child_type = 0; // fallback
+
+ if(lua_geti(L, -1, moony->uris.rdfs_range) == LUA_TNUMBER) // prop[RDFS.range]
+ range = lua_tointeger(L, -1);
+ lua_pop(L, 1); // range
+
+ if(lua_geti(L, -1, moony->uris.atom_child_type) == LUA_TNUMBER) // prop[Atom.childType]
+ child_type = lua_tointeger(L, -1);
+ lua_pop(L, 1); // child_type
+
+ if(lua_geti(L, -1, moony->uris.rdf_value) != LUA_TNIL)
+ {
+ if( !lv2_atom_forge_key(lforge->forge, key)
+ || !_lforge_basic(L, -1, lforge->forge, range, child_type) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+ lua_pop(L, 1); // nil || rdf_value
+
+ lua_pop(L, 1); // removes 'value', keeps 'key' for next iteration
+ }
+ }
+ lua_pop(L, 1); // nil || readable
+ }
+ lv2_atom_forge_pop(lforge->forge, &body_frame);
+ }
+ lv2_atom_forge_pop(lforge->forge, &obj_frame);
+
+ return 0;
+}
+
+__realtime static int
+_lstateresponder_stash(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ lua_settop(L, 2); // discard superfluous arguments
+ // 1: self
+ // 2: forge
+
+ // replace self with its uservalue
+ lua_getuservalue(L, 1);
+ lua_replace(L, 1);
+
+ lforge_t *lforge = luaL_checkudata(L, 2, "lforge");
+
+ // ignore patch:readable's
+ if(lua_geti(L, 1, moony->uris.patch.writable) == LUA_TNIL)
+ {
+ lua_pop(L, 1); // nil
+ return 1; // forge
+ }
+
+ LV2_Atom_Forge_Frame frame;
+ if(!lv2_atom_forge_object(lforge->forge, &frame, 0, 0))
+ luaL_error(L, forge_buffer_overflow);
+
+ // iterate over writable properties
+ lua_pushnil(L); // first key
+ while(lua_next(L, -2))
+ {
+ // uses 'key' (at index -2) and 'value' (at index -1)
+ const LV2_URID key = luaL_checkinteger(L, -2);
+ LV2_URID range = 0; // fallback
+ LV2_URID child_type = 0; // fallback
+
+ if(lua_geti(L, -1, moony->uris.rdfs_range) == LUA_TNUMBER) // prop[RDFS.range]
+ range = lua_tointeger(L, -1);
+ lua_pop(L, 1); // range
+
+ if(lua_geti(L, -1, moony->uris.atom_child_type) == LUA_TNUMBER) // prop[Atom.child_type]
+ child_type = lua_tointeger(L, -1);
+ lua_pop(L, 1); // child_type
+
+ if(lua_geti(L, -1, moony->uris.rdf_value) != LUA_TNIL) // prop[RDF.value]
+ {
+ if( !lv2_atom_forge_key(lforge->forge, key)
+ || !_lforge_basic(L, -1, lforge->forge, range, child_type) )
+ luaL_error(L, forge_buffer_overflow);
+ }
+ lua_pop(L, 1); // value
+
+ // removes 'value'; keeps 'key' for next iteration
+ lua_pop(L, 1);
+ }
+
+ lv2_atom_forge_pop(lforge->forge, &frame);
+
+ lua_pop(L, 1); // patch:writable
+ return 1; // forge
+}
+
+__realtime static int
+_lstateresponder_apply(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ lua_settop(L, 2); // discard superfluous arguments
+ // 1: self
+ // 2: obj
+
+ // replace self with its uservalue
+ lua_getuservalue(L, 1);
+ lua_replace(L, 1);
+
+ latom_t *latom = luaL_checkudata(L, 2, "latom");
+
+ // check for atom object
+ if(!lv2_atom_forge_is_object_type(&moony->forge, latom->atom->type))
+ {
+ lua_pushboolean(L, false);
+ return 1;
+ }
+
+ // ignore patch:readable's
+ if(lua_geti(L, 1, moony->uris.patch.writable) != LUA_TNIL)
+ {
+ LV2_ATOM_OBJECT_BODY_FOREACH(latom->body.obj, latom->atom->size, prop)
+ {
+ if(lua_geti(L, -1, prop->key) != LUA_TNIL)
+ {
+ _latom_value(L, &prop->value);
+ lua_seti(L, -2, moony->uris.rdf_value); // set prop[RDF.value]
+ }
+ lua_pop(L, 1); // nil || prop
+ }
+ }
+
+ lua_pushboolean(L, true);
+ return 1;
+}
+
+__realtime int
+_lstateresponder(lua_State *L)
+{
+ //moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ lua_settop(L, 1); // discard superfluous arguments
+
+ // o = new
+ int32_t *dummy = lua_newuserdata(L, sizeof(int32_t));
+ (void)dummy;
+
+ // o.uservalue = uservalue
+ lua_insert(L, 1);
+ lua_setuservalue(L, -2);
+
+ // setmetatable(o, self)
+ luaL_getmetatable(L, "lstateresponder");
+ lua_setmetatable(L, -2);
+
+ // return o
+ return 1;
+}
+
+const luaL_Reg lstateresponder_mt [] = {
+ {"__call", _lstateresponder__call},
+ {"register", _lstateresponder_register},
+ {"sync", _lstateresponder_sync},
+ {"stash", _lstateresponder_stash},
+ {"apply", _lstateresponder_apply},
+ {NULL, NULL}
+};
diff --git a/api/api_state.h b/api/api_state.h
new file mode 100644
index 0000000..f6e5abc
--- /dev/null
+++ b/api/api_state.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _MOONY_API_STATE_H
+#define _MOONY_API_STATE_H
+
+#include <moony.h>
+
+int
+_lstateresponder(lua_State *L);
+
+extern const luaL_Reg lstateresponder_mt [];
+
+#endif
diff --git a/api/api_time.c b/api/api_time.c
new file mode 100644
index 0000000..179f0e0
--- /dev/null
+++ b/api/api_time.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <api_time.h>
+#include <api_atom.h>
+#include <api_forge.h>
+
+#include <timely.h>
+
+__realtime static void
+_ltimeresponder_cb(timely_t *timely, int64_t frames, LV2_URID type,
+ void *data)
+{
+ lua_State *L = data;
+
+ if(lua_geti(L, 5, type) != LUA_TNIL) // uservalue[type]
+ {
+ lua_pushvalue(L, 5); // uservalue
+ lua_pushinteger(L, frames); // frames
+ lua_pushvalue(L, 4); // data
+
+ if(type == TIMELY_URI_BAR_BEAT(timely))
+ {
+ lua_pushnumber(L, TIMELY_BAR_BEAT_RAW(timely));
+ }
+ else if(type == TIMELY_URI_BAR(timely))
+ {
+ lua_pushinteger(L, TIMELY_BAR(timely));
+ }
+ else if(type == TIMELY_URI_BEAT_UNIT(timely))
+ {
+ lua_pushinteger(L, TIMELY_BEAT_UNIT(timely));
+ }
+ else if(type == TIMELY_URI_BEATS_PER_BAR(timely))
+ {
+ lua_pushnumber(L, TIMELY_BEATS_PER_BAR(timely));
+ }
+ else if(type == TIMELY_URI_BEATS_PER_MINUTE(timely))
+ {
+ lua_pushnumber(L, TIMELY_BEATS_PER_MINUTE(timely));
+ }
+ else if(type == TIMELY_URI_FRAME(timely))
+ {
+ lua_pushinteger(L, TIMELY_FRAME(timely));
+ }
+ else if(type == TIMELY_URI_FRAMES_PER_SECOND(timely))
+ {
+ lua_pushnumber(L, TIMELY_FRAMES_PER_SECOND(timely));
+ }
+ else if(type == TIMELY_URI_SPEED(timely))
+ {
+ lua_pushnumber(L, TIMELY_SPEED(timely));
+ }
+ else
+ {
+ lua_pushnil(L);
+ }
+
+ lua_call(L, 4, 0);
+ }
+ else
+ lua_pop(L, 1); // nil
+}
+
+__realtime static int
+_ltimeresponder__call(lua_State *L)
+{
+ //moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ lua_settop(L, 5); // discard superfluous arguments
+ // 1: self
+ // 2: from
+ // 3: to
+ // 4: data aka forge
+ // 5: atom || nil
+
+ timely_t *timely = lua_touserdata(L, 1);
+ int64_t from = luaL_checkinteger(L, 2);
+ int64_t to = luaL_checkinteger(L, 3);
+ latom_t *latom = NULL;
+ if(luaL_testudata(L, 5, "latom"))
+ latom = lua_touserdata(L, 5);
+ lua_pop(L, 1); // atom
+
+ lua_getuservalue(L, 1); // 5: uservalue
+ const int handled = latom
+ ? timely_advance_body(timely, latom->atom->size, latom->atom->type, latom->body.obj, from, to)
+ : timely_advance_body(timely, 0, 0, NULL, from, to);
+
+ lua_pushboolean(L, handled); // handled vs not handled
+ return 1;
+}
+
+__realtime int
+_ltimeresponder_apply(lua_State *L)
+{
+ // 1: self
+ // 2: atom
+
+ lua_pushinteger(L, 0);
+ lua_insert(L, 2); // from
+
+ lua_pushinteger(L, 0);
+ lua_insert(L, 3); // to
+
+ lua_pushnil(L);
+ lua_insert(L, 4); // data aka forge
+
+ return _ltimeresponder__call(L);
+}
+
+__realtime int
+_ltimeresponder_stash(lua_State *L)
+{
+ //moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ lua_settop(L, 2); // discard superfluous arguments
+ // 1: self
+ // 2: lforge
+
+ timely_t *timely = lua_touserdata(L, 1);
+ lforge_t *lforge = luaL_checkudata(L, 2, "lforge");
+
+ const float multiplier_1 = 1.f / timely->multiplier;
+
+ // serialize full time state to stash
+ LV2_Atom_Forge_Frame frame;
+ if( !lv2_atom_forge_object(lforge->forge, &frame, 0, timely->urid.time_position)
+
+ || !lv2_atom_forge_key(lforge->forge, timely->urid.time_barBeat)
+ || !lv2_atom_forge_float(lforge->forge, TIMELY_BAR_BEAT(timely) * multiplier_1)
+
+ || !lv2_atom_forge_key(lforge->forge, timely->urid.time_bar)
+ || !lv2_atom_forge_long(lforge->forge, TIMELY_BAR(timely))
+
+ || !lv2_atom_forge_key(lforge->forge, timely->urid.time_beatUnit)
+ || !lv2_atom_forge_int(lforge->forge, TIMELY_BEAT_UNIT(timely) * multiplier_1)
+
+ || !lv2_atom_forge_key(lforge->forge, timely->urid.time_beatsPerBar)
+ || !lv2_atom_forge_float(lforge->forge, TIMELY_BEATS_PER_BAR(timely) * multiplier_1)
+
+ || !lv2_atom_forge_key(lforge->forge, timely->urid.time_beatsPerMinute)
+ || !lv2_atom_forge_float(lforge->forge, TIMELY_BEATS_PER_MINUTE(timely))
+
+ || !lv2_atom_forge_key(lforge->forge, timely->urid.time_frame)
+ || !lv2_atom_forge_long(lforge->forge, TIMELY_FRAME(timely))
+
+ || !lv2_atom_forge_key(lforge->forge, timely->urid.time_framesPerSecond)
+ || !lv2_atom_forge_float(lforge->forge, TIMELY_FRAMES_PER_SECOND(timely))
+
+ || !lv2_atom_forge_key(lforge->forge, timely->urid.time_speed)
+ || !lv2_atom_forge_float(lforge->forge, TIMELY_SPEED(timely)) )
+ luaL_error(L, forge_buffer_overflow);
+ lv2_atom_forge_pop(lforge->forge, &frame);
+
+ return 1; // forge
+}
+
+__realtime static int
+_ltimeresponder__index(lua_State *L)
+{
+ //moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ lua_settop(L, 2); // discard superfluous arguments
+ // 1: self
+ // 2: type
+
+ timely_t *timely = lua_touserdata(L, 1);
+
+ int ltype = lua_type(L, 2);
+ if(ltype != LUA_TNUMBER)
+ {
+ if(ltype == LUA_TSTRING)
+ {
+ const char *key = lua_tostring(L, 2);
+ if(!strcmp(key, "stash"))
+ lua_rawgetp(L, LUA_REGISTRYINDEX, _ltimeresponder_stash);
+ else if(!strcmp(key, "apply"))
+ lua_rawgetp(L, LUA_REGISTRYINDEX, _ltimeresponder_apply);
+ else if(!strcmp(key, "multiplier"))
+ lua_pushnumber(L, timely->multiplier);
+ else
+ lua_pushnil(L);
+ }
+ else
+ lua_pushnil(L);
+
+ return 1;
+ }
+
+ LV2_URID type = lua_tointeger(L, 2);
+
+ if(type == TIMELY_URI_BAR_BEAT(timely))
+ {
+ lua_pushnumber(L, TIMELY_BAR_BEAT(timely));
+ }
+ else if(type == TIMELY_URI_BAR(timely))
+ {
+ lua_pushinteger(L, TIMELY_BAR(timely));
+ }
+ else if(type == TIMELY_URI_BEAT_UNIT(timely))
+ {
+ lua_pushinteger(L, TIMELY_BEAT_UNIT(timely));
+ }
+ else if(type == TIMELY_URI_BEATS_PER_BAR(timely))
+ {
+ lua_pushnumber(L, TIMELY_BEATS_PER_BAR(timely));
+ }
+ else if(type == TIMELY_URI_BEATS_PER_MINUTE(timely))
+ {
+ lua_pushnumber(L, TIMELY_BEATS_PER_MINUTE(timely));
+ }
+ else if(type == TIMELY_URI_FRAME(timely))
+ {
+ lua_pushinteger(L, TIMELY_FRAME(timely));
+ }
+ else if(type == TIMELY_URI_FRAMES_PER_SECOND(timely))
+ {
+ lua_pushnumber(L, TIMELY_FRAMES_PER_SECOND(timely));
+ }
+ else if(type == TIMELY_URI_SPEED(timely))
+ {
+ lua_pushnumber(L, TIMELY_SPEED(timely));
+ }
+ else
+ {
+ lua_pushnil(L);
+ }
+
+ return 1;
+}
+
+__realtime static int
+_ltimeresponder__newindex(lua_State *L)
+{
+ //moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ lua_settop(L, 3); // discard superfluous arguments
+ // 1: self
+ // 2: key
+ // 3: value
+
+ timely_t *timely = lua_touserdata(L, 1);
+
+ if(lua_type(L, 2) == LUA_TSTRING)
+ {
+ if(!strcmp(lua_tostring(L, 2), "multiplier"))
+ {
+ const float multiplier = luaL_checknumber(L, 3);
+ if(multiplier <= 0.f)
+ luaL_error(L, "multiplier not > 0.0");
+ timely_set_multiplier(timely, multiplier);
+ }
+ }
+
+ return 0;
+}
+
+__realtime int
+_ltimeresponder(lua_State *L)
+{
+ moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
+
+ lua_settop(L, 2); // discard superfluous arguments
+
+ timely_mask_t mask = TIMELY_MASK_BAR_BEAT
+ | TIMELY_MASK_BAR
+ | TIMELY_MASK_BEAT_UNIT
+ | TIMELY_MASK_BEATS_PER_BAR
+ | TIMELY_MASK_BEATS_PER_MINUTE
+ | TIMELY_MASK_FRAME
+ | TIMELY_MASK_FRAMES_PER_SECOND
+ | TIMELY_MASK_SPEED
+ | TIMELY_MASK_BAR_BEAT_WHOLE
+ | TIMELY_MASK_BAR_WHOLE;
+
+ // o = o or {}
+ if(lua_isnil(L, 1))
+ {
+ lua_remove(L, 1);
+ lua_newtable(L);
+ lua_insert(L, 1);
+ }
+
+ const float multiplier = luaL_optnumber(L, 2, 1.f);
+ lua_pop(L, 1); // multiplier
+
+ if(multiplier <= 0.f)
+ luaL_error(L, "multiplier not > 0.0");
+
+ // TODO do we want to cache/reuse this, too?
+ timely_t *timely = lua_newuserdata(L, sizeof(timely_t)); // userdata
+ timely_init(timely, moony->map, moony->sample_rate.body, mask,
+ _ltimeresponder_cb, L);
+ timely_set_multiplier(timely, multiplier);
+
+ // userdata.uservalue = o
+ lua_insert(L, 1);
+ lua_setuservalue(L, -2);
+
+ // setmetatable(o, self)
+ luaL_getmetatable(L, "ltimeresponder");
+ lua_setmetatable(L, -2);
+
+ // return o
+ return 1;
+}
+
+const luaL_Reg ltimeresponder_mt [] = {
+ {"__index", _ltimeresponder__index},
+ {"__newindex", _ltimeresponder__newindex},
+ {"__call", _ltimeresponder__call},
+ {NULL, NULL}
+};
diff --git a/api/api_time.h b/api/api_time.h
new file mode 100644
index 0000000..1bcef47
--- /dev/null
+++ b/api/api_time.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _MOONY_API_TIME_H
+#define _MOONY_API_TIME_H
+
+#include <moony.h>
+
+int
+_ltimeresponder(lua_State *L);
+
+int
+_ltimeresponder_apply(lua_State *L);
+
+int
+_ltimeresponder_stash(lua_State *L);
+
+extern const luaL_Reg ltimeresponder_mt [];
+
+#endif
diff --git a/api/api_vm.c b/api/api_vm.c
new file mode 100644
index 0000000..ade9457
--- /dev/null
+++ b/api/api_vm.c
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#if !defined(_WIN32)
+# include <sys/mman.h>
+#endif
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <moony.h>
+#include <api_vm.h>
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <laes128.h>
+
+//FIXME put those into a header
+extern int luaopen_lpeg(lua_State *L);
+extern int luaopen_base64(lua_State *L);
+extern int luaopen_ascii85(lua_State *L);
+extern int luaopen_mathx(lua_State *L);
+extern int luaopen_complex(lua_State *L);
+extern int luaopen_random(lua_State *L);
+
+//#define MOONY_LOG_MEM
+#ifdef MOONY_LOG_MEM
+__realtime static inline void
+_log_mem(moony_vm_t *vm, void *ptr, size_t osize, size_t nsize)
+{
+ moony_t *moony = vm->data;
+
+ if(moony->log)
+ {
+ char suffix0 = ' ';
+ size_t space = vm->space;
+ if(space >= 1024)
+ {
+ suffix0 = 'K';
+ space >>= 10;
+ }
+ if(space >= 1024)
+ {
+ suffix0 = 'M';
+ space >>= 10;
+ }
+
+ char suffix1 = ' ';
+ size_t used = vm->used;
+ if(used >= 1024)
+ {
+ suffix1 = 'K';
+ used >>= 10;
+ }
+ if(used >= 1024)
+ {
+ suffix1 = 'M';
+ used >>= 10;
+ }
+
+ lv2_log_trace(&moony->logger, "space: %4zu%c, used: %4zu%c, old: %4zu, new: %4zu, data: %p\n",
+ space, suffix0, used, suffix1, osize, nsize, ptr);
+ }
+}
+#endif
+
+__realtime inline void *
+moony_rt_alloc(moony_vm_t *vm, size_t nsize)
+{
+ vm->used += nsize;
+ if(vm->used > (vm->space >> 1))
+ moony_vm_mem_extend(vm);
+
+#ifdef MOONY_LOG_MEM
+ _log_mem(vm, NULL, 0, nsize);
+#endif
+
+ return tlsf_malloc(vm->tlsf, nsize);
+}
+
+__realtime inline void *
+moony_rt_realloc(moony_vm_t *vm, void *buf, size_t osize, size_t nsize)
+{
+ vm->used -= osize;
+ vm->used += nsize;
+ if(vm->used > (vm->space >> 1))
+ moony_vm_mem_extend(vm);
+
+#ifdef MOONY_LOG_MEM
+ _log_mem(vm, buf, osize, nsize);
+#endif
+
+ return tlsf_realloc(vm->tlsf, buf, nsize);
+}
+
+__realtime inline void
+moony_rt_free(moony_vm_t *vm, void *buf, size_t osize)
+{
+ vm->used -= osize;
+ if(vm->used > (vm->space >> 1))
+ moony_vm_mem_extend(vm);
+
+#ifdef MOONY_LOG_MEM
+ _log_mem(vm, buf, osize, 0);
+#endif
+
+ tlsf_free(vm->tlsf, buf);
+}
+
+__realtime static void *
+lua_alloc(void *ud, void *ptr, size_t osize, size_t nsize)
+{
+ moony_vm_t *vm = ud;
+
+ if(nsize == 0)
+ {
+ if(ptr)
+ moony_rt_free(vm, ptr, osize);
+ return NULL;
+ }
+ else
+ {
+ if(ptr)
+ return moony_rt_realloc(vm, ptr, osize, nsize);
+ else
+ return moony_rt_alloc(vm, nsize);
+ }
+}
+
+__non_realtime moony_vm_t *
+moony_vm_new(size_t mem_size, bool testing, void *data)
+{
+ moony_vm_t *vm = calloc(1, sizeof(moony_vm_t));
+ if(!vm)
+ return NULL;
+
+ vm->data = data;
+
+ // initialize array of increasing pool sizes
+ vm->size[0] = mem_size;
+
+ // allocate first pool
+ vm->area[0] = moony_vm_mem_alloc(vm->size[0]);
+ if(!vm->area[0])
+ {
+ free(vm);
+ return NULL;
+ }
+
+ vm->tlsf = tlsf_create_with_pool(vm->area[0], vm->size[0]);
+ if(!vm->tlsf)
+ {
+ moony_vm_mem_free(vm->area[0], vm->size[0]);
+ free(vm);
+ return NULL;
+ }
+
+ vm->pool[0] = tlsf_get_pool(vm->tlsf);
+ vm->space += vm->size[0];
+
+ lua_State *L = lua_newstate(lua_alloc, vm);
+ if(!L)
+ {
+ free(vm);
+ return NULL;
+ }
+
+ vm->L = L;
+
+ const int n = lua_gettop(L);
+
+ luaL_requiref(L, "base", luaopen_base, 0);
+
+ luaL_requiref(L, "coroutine", luaopen_coroutine, 1);
+ luaL_requiref(L, "table", luaopen_table, 1);
+ luaL_requiref(L, "string", luaopen_string, 1);
+ luaL_requiref(L, "math", luaopen_math, 1);
+ luaL_requiref(L, "utf8", luaopen_utf8, 1);
+ luaL_requiref(L, "debug", luaopen_debug, 1);
+
+ luaL_requiref(L, "lpeg", luaopen_lpeg, 1);
+ luaL_requiref(L, "base64", luaopen_base64, 1);
+ luaL_requiref(L, "ascii85", luaopen_ascii85, 1);
+ luaL_requiref(L, "aes128", luaopen_aes128, 1);
+ luaL_requiref(L, "mathx", luaopen_mathx, 1);
+ luaL_requiref(L, "complex", luaopen_complex, 1);
+ luaL_requiref(L, "random", luaopen_random, 1);
+
+ if(testing)
+ {
+ luaL_requiref(L, "io", luaopen_io, 1);
+ luaL_requiref(L, "package", luaopen_package, 1);
+ //luaL_requiref(L, "os", luaopen_os, 1);
+ //luaL_requiref(L, "bit32", luaopen_bit32, 1);
+ }
+
+ lua_settop(L, n);
+
+ if(!testing)
+ {
+ // clear dofile
+ lua_pushnil(L);
+ lua_setglobal(L, "dofile");
+
+ // clear loadfile
+ lua_pushnil(L);
+ lua_setglobal(L, "loadfile");
+ }
+
+ // clear math.random[seed]
+ lua_getglobal(L, "math");
+
+ lua_pushnil(L);
+ lua_setfield(L, -2, "random");
+
+ lua_pushnil(L);
+ lua_setfield(L, -2, "randomseed");
+
+ lua_pop(L, 1); // math
+
+#ifdef USE_MANUAL_GC
+ // manual garbage collector
+ lua_gc(L, LUA_GCSTOP, 0); // disable automatic garbage collection
+ lua_gc(L, LUA_GCSETPAUSE, 0); // don't wait to start a new cycle
+ lua_gc(L, LUA_GCSETSTEPMUL, 100); // set step size to run 'as fast a memory allocation'
+#else
+ // automatic garbage collector
+ lua_gc(L, LUA_GCRESTART, 0); // enable automatic garbage collection
+ lua_gc(L, LUA_GCSETPAUSE, 105); // next step when memory increased by 5%
+ lua_gc(L, LUA_GCSETSTEPMUL, 105); // run 5% faster than memory allocation
+#endif
+
+ return vm;
+}
+
+__non_realtime void
+moony_vm_free(moony_vm_t *vm)
+{
+ if(vm->L)
+ lua_close(vm->L);
+
+ if(vm->ser.buf)
+ moony_rt_free(vm, vm->ser.buf, vm->ser.size);
+
+ vm->used = 0;
+
+ for(int i=(MOONY_POOL_NUM-1); i>=0; i--)
+ {
+ if(!vm->area[i])
+ continue; // this memory slot is unused, skip it
+
+ tlsf_remove_pool(vm->tlsf, vm->pool[i]);
+ moony_vm_mem_free(vm->area[i], vm->size[i]);
+ vm->space -= vm->size[i];
+
+ vm->area[i] = NULL;
+ vm->pool[i] = NULL;
+ vm->size[i] = 0;
+ }
+
+ assert(vm->space == 0);
+ tlsf_destroy(vm->tlsf);
+ vm->tlsf = NULL;
+
+ free(vm);
+}
+
+__non_realtime void *
+moony_vm_mem_alloc(size_t size)
+{
+ void *area = NULL;
+
+ //printf("moony_vm_mem_alloc: %zu\n", size);
+
+#if defined(_WIN32)
+ area = _aligned_malloc(size, 8);
+#else
+ posix_memalign(&area, 8, size);
+#endif
+ if(!area)
+ return NULL;
+
+ mlock(area, size);
+ memset(area, 0x0, size);
+ return area;
+}
+
+__non_realtime void
+moony_vm_mem_free(void *area, size_t size)
+{
+ if(!area)
+ return;
+
+ //printf("moony_vm_mem_free: %zu\n", size);
+
+ munlock(area, size);
+ free(area);
+}
+
+__realtime int
+moony_vm_mem_extend(moony_vm_t *vm)
+{
+ moony_t *moony = vm->data;
+
+ // request processing or fully extended?
+ if(vm->allocating || vm->fully_extended)
+ return -1;
+
+ for(int i=1; i<MOONY_POOL_NUM; i++)
+ {
+ if(vm->area[i]) // pool already allocated/in-use
+ continue;
+
+ if(vm->nrt)
+ {
+ vm->size[i] = vm->size[i-1] * 2;
+ vm->area[i] = moony_vm_mem_alloc(vm->size[i]);
+ if(vm->area[i])
+ {
+ vm->pool[i] = tlsf_add_pool(vm->tlsf,
+ vm->area[i], vm->size[i]); //FIXME stoat complains about printf
+
+ if(vm->pool[i])
+ {
+ vm->space += vm->size[i];
+ }
+ else
+ {
+ moony_vm_mem_free(vm->area[i], vm->size[i]);
+ vm->size[i] = 0;
+ vm->area[i] = NULL;
+ }
+ }
+ }
+ else
+ {
+ moony_job_t *req;
+ if((req = varchunk_write_request(moony->from_dsp, sizeof(moony_job_t))))
+ {
+ req->type = MOONY_JOB_MEM_ALLOC;
+ req->mem.size = vm->size[i-1] * 2;
+ req->mem.ptr = NULL;
+
+ varchunk_write_advance(moony->from_dsp, sizeof(moony_job_t));
+ if(moony_wake_worker(moony->sched) == LV2_WORKER_SUCCESS)
+ vm->allocating = true; // toggle working flag
+ }
+ }
+
+ return 0;
+ }
+
+ vm->fully_extended = true;
+
+ return -1;
+}
+
+void
+moony_vm_nrt_enter(moony_vm_t *vm)
+{
+ vm->nrt = true;
+}
+
+void
+moony_vm_nrt_leave(moony_vm_t *vm)
+{
+ vm->nrt = false;
+}
diff --git a/api/api_vm.h b/api/api_vm.h
new file mode 100644
index 0000000..ae4756c
--- /dev/null
+++ b/api/api_vm.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _MOONY_API_VM_H
+#define _MOONY_API_VM_H
+
+#include <tlsf.h>
+#include <lua.h>
+
+// from vm.c
+#define MOONY_POOL_NUM 8
+#define MOONY_MAX_TRACE_LEN 0x800 // 2KB
+
+typedef enum _moony_job_enum_t moony_job_enum_t;
+typedef struct _moony_vm_t moony_vm_t;
+typedef struct _moony_job_t moony_job_t;
+
+struct _moony_vm_t {
+ tlsf_t tlsf;
+
+ size_t size [MOONY_POOL_NUM];
+ void *area [MOONY_POOL_NUM];
+ pool_t pool [MOONY_POOL_NUM];
+
+ size_t space;
+ size_t used;
+
+ lua_State *L;
+ bool nrt;
+ void *data;
+
+ bool allocating;
+ bool fully_extended;
+
+ bool trace_out;
+ bool trace_overflow;
+ char trace [MOONY_MAX_TRACE_LEN];
+
+ atom_ser_t ser;
+};
+
+enum _moony_job_enum_t {
+ MOONY_JOB_MEM_ALLOC,
+ MOONY_JOB_MEM_FREE,
+ MOONY_JOB_VM_ALLOC,
+ MOONY_JOB_VM_FREE,
+ MOONY_JOB_PTR_FREE
+};
+
+struct _moony_job_t {
+ moony_job_enum_t type;
+
+ union {
+ struct {
+ size_t size;
+ void *ptr;
+ } mem;
+ moony_vm_t *vm;
+ void *ptr;
+ char chunk [0];
+ };
+};
+
+moony_vm_t *moony_vm_new(size_t mem_size, bool testing, void *data);
+void moony_vm_free(moony_vm_t *vm);
+
+void *moony_vm_mem_alloc(size_t size);
+void moony_vm_mem_free(void *area, size_t size);
+int moony_vm_mem_extend(moony_vm_t *vm);
+
+void moony_vm_nrt_enter(moony_vm_t *vm);
+void moony_vm_nrt_leave(moony_vm_t *vm);
+
+#endif
diff --git a/canvas.lv2/COPYING b/canvas.lv2/COPYING
new file mode 100644
index 0000000..ddb9a46
--- /dev/null
+++ b/canvas.lv2/COPYING
@@ -0,0 +1,201 @@
+ The Artistic License 2.0
+
+ Copyright (c) 2000-2006, The Perl Foundation.
+
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+Preamble
+
+This license establishes the terms under which a given free software
+Package may be copied, modified, distributed, and/or redistributed.
+The intent is that the Copyright Holder maintains some artistic
+control over the development of that Package while still keeping the
+Package available as open source and free software.
+
+You are always permitted to make arrangements wholly outside of this
+license directly with the Copyright Holder of a given Package. If the
+terms of this license do not permit the full use that you propose to
+make of the Package, you should contact the Copyright Holder and seek
+a different licensing arrangement.
+
+Definitions
+
+ "Copyright Holder" means the individual(s) or organization(s)
+ named in the copyright notice for the entire Package.
+
+ "Contributor" means any party that has contributed code or other
+ material to the Package, in accordance with the Copyright Holder's
+ procedures.
+
+ "You" and "your" means any person who would like to copy,
+ distribute, or modify the Package.
+
+ "Package" means the collection of files distributed by the
+ Copyright Holder, and derivatives of that collection and/or of
+ those files. A given Package may consist of either the Standard
+ Version, or a Modified Version.
+
+ "Distribute" means providing a copy of the Package or making it
+ accessible to anyone else, or in the case of a company or
+ organization, to others outside of your company or organization.
+
+ "Distributor Fee" means any fee that you charge for Distributing
+ this Package or providing support for this Package to another
+ party. It does not mean licensing fees.
+
+ "Standard Version" refers to the Package if it has not been
+ modified, or has been modified only in ways explicitly requested
+ by the Copyright Holder.
+
+ "Modified Version" means the Package, if it has been changed, and
+ such changes were not explicitly requested by the Copyright
+ Holder.
+
+ "Original License" means this Artistic License as Distributed with
+ the Standard Version of the Package, in its current version or as
+ it may be modified by The Perl Foundation in the future.
+
+ "Source" form means the source code, documentation source, and
+ configuration files for the Package.
+
+ "Compiled" form means the compiled bytecode, object code, binary,
+ or any other form resulting from mechanical transformation or
+ translation of the Source form.
+
+
+Permission for Use and Modification Without Distribution
+
+(1) You are permitted to use the Standard Version and create and use
+Modified Versions for any purpose without restriction, provided that
+you do not Distribute the Modified Version.
+
+
+Permissions for Redistribution of the Standard Version
+
+(2) You may Distribute verbatim copies of the Source form of the
+Standard Version of this Package in any medium without restriction,
+either gratis or for a Distributor Fee, provided that you duplicate
+all of the original copyright notices and associated disclaimers. At
+your discretion, such verbatim copies may or may not include a
+Compiled form of the Package.
+
+(3) You may apply any bug fixes, portability changes, and other
+modifications made available from the Copyright Holder. The resulting
+Package will still be considered the Standard Version, and as such
+will be subject to the Original License.
+
+
+Distribution of Modified Versions of the Package as Source
+
+(4) You may Distribute your Modified Version as Source (either gratis
+or for a Distributor Fee, and with or without a Compiled form of the
+Modified Version) provided that you clearly document how it differs
+from the Standard Version, including, but not limited to, documenting
+any non-standard features, executables, or modules, and provided that
+you do at least ONE of the following:
+
+ (a) make the Modified Version available to the Copyright Holder
+ of the Standard Version, under the Original License, so that the
+ Copyright Holder may include your modifications in the Standard
+ Version.
+
+ (b) ensure that installation of your Modified Version does not
+ prevent the user installing or running the Standard Version. In
+ addition, the Modified Version must bear a name that is different
+ from the name of the Standard Version.
+
+ (c) allow anyone who receives a copy of the Modified Version to
+ make the Source form of the Modified Version available to others
+ under
+
+ (i) the Original License or
+
+ (ii) a license that permits the licensee to freely copy,
+ modify and redistribute the Modified Version using the same
+ licensing terms that apply to the copy that the licensee
+ received, and requires that the Source form of the Modified
+ Version, and of any works derived from it, be made freely
+ available in that license fees are prohibited but Distributor
+ Fees are allowed.
+
+
+Distribution of Compiled Forms of the Standard Version
+or Modified Versions without the Source
+
+(5) You may Distribute Compiled forms of the Standard Version without
+the Source, provided that you include complete instructions on how to
+get the Source of the Standard Version. Such instructions must be
+valid at the time of your distribution. If these instructions, at any
+time while you are carrying out such distribution, become invalid, you
+must provide new instructions on demand or cease further distribution.
+If you provide valid instructions or cease distribution within thirty
+days after you become aware that the instructions are invalid, then
+you do not forfeit any of your rights under this license.
+
+(6) You may Distribute a Modified Version in Compiled form without
+the Source, provided that you comply with Section 4 with respect to
+the Source of the Modified Version.
+
+
+Aggregating or Linking the Package
+
+(7) You may aggregate the Package (either the Standard Version or
+Modified Version) with other packages and Distribute the resulting
+aggregation provided that you do not charge a licensing fee for the
+Package. Distributor Fees are permitted, and licensing fees for other
+components in the aggregation are permitted. The terms of this license
+apply to the use and Distribution of the Standard or Modified Versions
+as included in the aggregation.
+
+(8) You are permitted to link Modified and Standard Versions with
+other works, to embed the Package in a larger work of your own, or to
+build stand-alone binary or bytecode versions of applications that
+include the Package, and Distribute the result without restriction,
+provided the result does not expose a direct interface to the Package.
+
+
+Items That are Not Considered Part of a Modified Version
+
+(9) Works (including, but not limited to, modules and scripts) that
+merely extend or make use of the Package, do not, by themselves, cause
+the Package to be a Modified Version. In addition, such works are not
+considered parts of the Package itself, and are not subject to the
+terms of this license.
+
+
+General Provisions
+
+(10) Any use, modification, and distribution of the Standard or
+Modified Versions is governed by this Artistic License. By using,
+modifying or distributing the Package, you accept this license. Do not
+use, modify, or distribute the Package, if you do not accept this
+license.
+
+(11) If your Modified Version has been derived from a Modified
+Version made by someone other than you, you are nevertheless required
+to ensure that your Modified Version complies with the requirements of
+this license.
+
+(12) This license does not grant you the right to use any trademark,
+service mark, tradename, or logo of the Copyright Holder.
+
+(13) This license includes the non-exclusive, worldwide,
+free-of-charge patent license to make, have made, use, offer to sell,
+sell, import and otherwise transfer the Package with respect to any
+patent claims licensable by the Copyright Holder that are necessarily
+infringed by the Package. If you institute patent litigation
+(including a cross-claim or counterclaim) against any party alleging
+that the Package constitutes direct or contributory patent
+infringement, then this Artistic License to you shall terminate on the
+date that such litigation is filed.
+
+(14) Disclaimer of Warranty:
+THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
+LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/canvas.lv2/README.md b/canvas.lv2/README.md
new file mode 100644
index 0000000..06b87f2
--- /dev/null
+++ b/canvas.lv2/README.md
@@ -0,0 +1,18 @@
+# Canvas LV2 plugin extension
+
+### License
+
+Copyright (c) 2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+
+This is free software: you can redistribute it and/or modify
+it under the terms of the Artistic License 2.0 as published by
+The Perl Foundation.
+
+This source is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+Artistic License 2.0 for more details.
+
+You should have received a copy of the Artistic License 2.0
+along the source as a COPYING file. If not, obtain it from
+<http://www.perlfoundation.org/artistic_license_2_0>.
diff --git a/canvas.lv2/canvas.h b/canvas.lv2/canvas.lv2/canvas.h
index 7cca0fa..7cca0fa 100644
--- a/canvas.lv2/canvas.h
+++ b/canvas.lv2/canvas.lv2/canvas.h
diff --git a/canvas.lv2/forge.h b/canvas.lv2/canvas.lv2/forge.h
index c5ce5fe..c5ce5fe 100644
--- a/canvas.lv2/forge.h
+++ b/canvas.lv2/canvas.lv2/forge.h
diff --git a/canvas.lv2/idisp.h b/canvas.lv2/canvas.lv2/idisp.h
index 099ab2c..099ab2c 100644
--- a/canvas.lv2/idisp.h
+++ b/canvas.lv2/canvas.lv2/idisp.h
diff --git a/canvas.lv2/lv2_extensions.h b/canvas.lv2/canvas.lv2/lv2_extensions.h
index 64fc3bc..64fc3bc 100644
--- a/canvas.lv2/lv2_extensions.h
+++ b/canvas.lv2/canvas.lv2/lv2_extensions.h
diff --git a/canvas.lv2/render.h b/canvas.lv2/canvas.lv2/render.h
index b809804..b809804 100644
--- a/canvas.lv2/render.h
+++ b/canvas.lv2/canvas.lv2/render.h
diff --git a/canvas.lv2/render_cairo.h b/canvas.lv2/canvas.lv2/render_cairo.h
index a41c72c..a41c72c 100644
--- a/canvas.lv2/render_cairo.h
+++ b/canvas.lv2/canvas.lv2/render_cairo.h
diff --git a/canvas.lv2/render_nanovg.h b/canvas.lv2/canvas.lv2/render_nanovg.h
index a98bc95..a98bc95 100644
--- a/canvas.lv2/render_nanovg.h
+++ b/canvas.lv2/canvas.lv2/render_nanovg.h
diff --git a/cmake/arm-linux-gnueabihf.cmake b/cmake/arm-linux-gnueabihf.cmake
new file mode 100644
index 0000000..c0ae918
--- /dev/null
+++ b/cmake/arm-linux-gnueabihf.cmake
@@ -0,0 +1,20 @@
+# the name of the target operating system
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR "armv7h")
+set(TOOLCHAIN "arm-linux-gnueabihf")
+
+# which compilers to use for C and C++
+set(CMAKE_C_COMPILER "${TOOLCHAIN}-gcc")
+set(CMAKE_CXX_COMPILER "${TOOLCHAIN}-g++")
+
+# here is the target environment located
+set(CMAKE_FIND_ROOT_PATH "usr/${TOOLCHAIN}")
+
+# adjust the default behaviour of the FIND_XXX() commands:
+# search headers and libraries in the target environment, search
+# programs in the host environment
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+
+set(QEMU qemu-arm)
diff --git a/cmake/i686-linux-gnu.cmake b/cmake/i686-linux-gnu.cmake
new file mode 100644
index 0000000..7768048
--- /dev/null
+++ b/cmake/i686-linux-gnu.cmake
@@ -0,0 +1,7 @@
+# the name of the target operating system
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR "i686")
+set(TOOLCHAIN "i686-linux-gnu")
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32" CACHE STRING "c++ flags")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32" CACHE STRING "c flags")
diff --git a/cmake/i686-w64-mingw32.cmake b/cmake/i686-w64-mingw32.cmake
new file mode 100644
index 0000000..acb6a6a
--- /dev/null
+++ b/cmake/i686-w64-mingw32.cmake
@@ -0,0 +1,24 @@
+# the name of the target operating system
+set(CMAKE_SYSTEM_NAME Windows)
+set(CMAKE_SYSTEM_PROCESSOR "i686")
+set(TOOLCHAIN "i686-w64-mingw32")
+
+# which compilers to use for C and C++
+set(CMAKE_C_COMPILER "${TOOLCHAIN}-gcc")
+set(CMAKE_CXX_COMPILER "${TOOLCHAIN}-g++")
+set(CMAKE_RC_COMPILER "${TOOLCHAIN}-windres")
+
+# here is the target environment located
+set(CMAKE_FIND_ROOT_PATH "/usr/${TOOLCHAIN}")
+
+# adjust the default behaviour of the FIND_XXX() commands:
+# search headers and libraries in the target environment, search
+# programs in the host environment
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+
+set(LIBS ${LIBS} "-static-libgcc -lws2_32")
+set(LIBS_WEB ${LIBS_WEB} "-static-libgcc -lws2_32")
+
+set(WINE wine)
diff --git a/cmake/universal-apple-darwin.cmake b/cmake/universal-apple-darwin.cmake
new file mode 100644
index 0000000..17476af
--- /dev/null
+++ b/cmake/universal-apple-darwin.cmake
@@ -0,0 +1,20 @@
+# the name of the target operating system
+set(CMAKE_SYSTEM_NAME Darwin)
+set(CMAKE_SYSTEM_PROCESSOR "x86_64")
+set(TOOLCHAIN "universal-apple-darwin")
+
+set(CMAKE_OSX_ARCHITECTURES "x86_64;i386")
+
+# which compilers to use for C and C++
+set(CMAKE_C_COMPILER "/usr/${TOOLCHAIN}/bin/x86_64-apple-darwin15-clang")
+set(CMAKE_CXX_COMPILER "/usr/${TOOLCHAIN}/bin/x86_64-apple-darwin15-clang++")
+
+# here is the target environment located
+set(CMAKE_FIND_ROOT_PATH "/usr/${TOOLCHAIN}/SDK/MacOSX10.11.sdk")
+
+# adjust the default behaviour of the FIND_XXX() commands:
+# search headers and libraries in the target environment, search
+# programs in the host environment
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
diff --git a/cmake/x86_64-linux-gnu.cmake b/cmake/x86_64-linux-gnu.cmake
new file mode 100644
index 0000000..7ab5477
--- /dev/null
+++ b/cmake/x86_64-linux-gnu.cmake
@@ -0,0 +1,4 @@
+# the name of the target operating system
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR "x86_64")
+set(TOOLCHAIN "x86_64-linux-gnu")
diff --git a/cmake/x86_64-w64-mingw32.cmake b/cmake/x86_64-w64-mingw32.cmake
new file mode 100644
index 0000000..a70f001
--- /dev/null
+++ b/cmake/x86_64-w64-mingw32.cmake
@@ -0,0 +1,24 @@
+# the name of the target operating system
+set(CMAKE_SYSTEM_NAME Windows)
+set(CMAKE_SYSTEM_PROCESSOR "x86_64")
+set(TOOLCHAIN "x86_64-w64-mingw32")
+
+# which compilers to use for C and C++
+set(CMAKE_C_COMPILER "${TOOLCHAIN}-gcc")
+set(CMAKE_CXX_COMPILER "${TOOLCHAIN}-g++")
+set(CMAKE_RC_COMPILER "${TOOLCHAIN}-windres")
+
+# here is the target environment located
+set(CMAKE_FIND_ROOT_PATH "/usr/${TOOLCHAIN}")
+
+# adjust the default behaviour of the FIND_XXX() commands:
+# search headers and libraries in the target environment, search
+# programs in the host environment
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+
+set(LIBS ${LIBS} "-static-libgcc -lws2_32")
+set(LIBS_WEB ${LIBS_WEB} "-static-libgcc -lws2_32")
+
+set(WINE wine64)
diff --git a/coverage.sh b/coverage.sh
new file mode 100755
index 0000000..2e6ade8
--- /dev/null
+++ b/coverage.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+rm -f *.profraw *.profdata
+
+LLVM_PROFILE_FILE=moony_test.profraw ./moony_test ../test/moony_test.lua
+LLVM_PROFILE_FILE=moony_overflow.profraw ./moony_test ../test/moony_overflow.lua 0
+LLVM_PROFILE_FILE=moony_manual.profraw ./moony_test ../test/moony_manual.lua
+LLVM_PROFILE_FILE=moony_presets.profraw ./moony_test ../test/moony_presets.lua
+
+llvm-profdata merge -sparse *.profraw -o moony.profdata
+
+for file in ../api/api_*.c;
+do
+ llvm-cov $1 ./moony_test -instr-profile=moony.profdata $file;
+done
+
+llvm-cov $1 ./moony_test -instr-profile=moony.profdata
diff --git a/docker_build.sh b/docker_build.sh
new file mode 100755
index 0000000..05ff351
--- /dev/null
+++ b/docker_build.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+TARGET=$1
+
+CI_PROJECT_DIR=$(pwd)
+CI_BUILD_NAME=${TARGET}
+
+BASE_NAME="moony.lv2"
+PKG_CONFIG_PATH="/opt/lv2/lib/pkgconfig:/opt/${CI_BUILD_NAME}/lib/pkgconfig"
+TOOLCHAIN_FILE="${CI_PROJECT_DIR}/cmake/${CI_BUILD_NAME}.cmake"
+
+rm -rf ${TARGET}
+mkdir -p ${TARGET}
+pushd ${TARGET}
+ PKG_CONFIG_PATH=${PKG_CONFIG_PATH} cmake \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DBUILD_TESTING=0 \
+ -DCMAKE_INSTALL_PREFIX=${CI_PROJECT_DIR} \
+ -DPLUGIN_DEST="${BASE_NAME}-$(cat ../VERSION)/${CI_BUILD_NAME}/${BASE_NAME}" \
+ -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE} \
+ ..
+ make -j4
+ make install
+ #ARGS='-VV' make test
+popd
diff --git a/docker_run.sh b/docker_run.sh
new file mode 100755
index 0000000..9d4a4c8
--- /dev/null
+++ b/docker_run.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/bash
+
+TARGET=$1
+
+docker run --rm -it -v $(pwd):/workdir/moony.lv2 ventosus/${TARGET}
diff --git a/ext_ui.lv2/lv2_external_ui.h b/ext_ui.lv2/lv2_external_ui.h
new file mode 100644
index 0000000..2c9e6ee
--- /dev/null
+++ b/ext_ui.lv2/lv2_external_ui.h
@@ -0,0 +1,109 @@
+/*
+ LV2 External UI extension
+ This work is in public domain.
+
+ This file is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ If you have questions, contact Filipe Coelho (aka falkTX) <falktx@falktx.com>
+ or ask in #lad channel, FreeNode IRC network.
+*/
+
+/**
+ @file lv2_external_ui.h
+ C header for the LV2 External UI extension <http://kxstudio.sf.net/ns/lv2ext/external-ui>.
+*/
+
+#ifndef LV2_EXTERNAL_UI_H
+#define LV2_EXTERNAL_UI_H
+
+#include "lv2/lv2plug.in/ns/extensions/ui/ui.h"
+
+#define LV2_EXTERNAL_UI_URI "http://kxstudio.sf.net/ns/lv2ext/external-ui"
+#define LV2_EXTERNAL_UI_PREFIX LV2_EXTERNAL_UI_URI "#"
+
+#define LV2_EXTERNAL_UI__Host LV2_EXTERNAL_UI_PREFIX "Host"
+#define LV2_EXTERNAL_UI__Widget LV2_EXTERNAL_UI_PREFIX "Widget"
+
+/** This extension used to be defined by a lv2plug.in URI */
+#define LV2_EXTERNAL_UI_DEPRECATED_URI "http://lv2plug.in/ns/extensions/ui#external"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * When LV2_EXTERNAL_UI__Widget UI is instantiated, the returned
+ * LV2UI_Widget handle must be cast to pointer to LV2_External_UI_Widget.
+ * UI is created in invisible state.
+ */
+typedef struct _LV2_External_UI_Widget {
+ /**
+ * Host calls this function regulary. UI library implementing the
+ * callback may do IPC or redraw the UI.
+ *
+ * @param _this_ the UI context
+ */
+ void (*run)(struct _LV2_External_UI_Widget * _this_);
+
+ /**
+ * Host calls this function to make the plugin UI visible.
+ *
+ * @param _this_ the UI context
+ */
+ void (*show)(struct _LV2_External_UI_Widget * _this_);
+
+ /**
+ * Host calls this function to make the plugin UI invisible again.
+ *
+ * @param _this_ the UI context
+ */
+ void (*hide)(struct _LV2_External_UI_Widget * _this_);
+
+} LV2_External_UI_Widget;
+
+#define LV2_EXTERNAL_UI_RUN(ptr) (ptr)->run(ptr)
+#define LV2_EXTERNAL_UI_SHOW(ptr) (ptr)->show(ptr)
+#define LV2_EXTERNAL_UI_HIDE(ptr) (ptr)->hide(ptr)
+
+/**
+ * On UI instantiation, host must supply LV2_EXTERNAL_UI__Host feature.
+ * LV2_Feature::data must be pointer to LV2_External_UI_Host.
+ */
+typedef struct _LV2_External_UI_Host {
+ /**
+ * Callback that plugin UI will call when UI (GUI window) is closed by user.
+ * This callback will be called during execution of LV2_External_UI_Widget::run()
+ * (i.e. not from background thread).
+ *
+ * After this callback is called, UI is defunct. Host must call LV2UI_Descriptor::cleanup().
+ * If host wants to make the UI visible again, the UI must be reinstantiated.
+ *
+ * @note When using the depreated URI LV2_EXTERNAL_UI_DEPRECATED_URI,
+ * some hosts will not call LV2UI_Descriptor::cleanup() as they should,
+ * and may call show() again without re-initialization.
+ *
+ * @param controller Host context associated with plugin UI, as
+ * supplied to LV2UI_Descriptor::instantiate().
+ */
+ void (*ui_closed)(LV2UI_Controller controller);
+
+ /**
+ * Optional (may be NULL) "user friendly" identifier which the UI
+ * may display to allow a user to easily associate this particular
+ * UI instance with the correct plugin instance as it is represented
+ * by the host (e.g. "track 1" or "channel 4").
+ *
+ * If supplied by host, the string will be referenced only during
+ * LV2UI_Descriptor::instantiate()
+ */
+ const char * plugin_human_id;
+
+} LV2_External_UI_Host;
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LV2_EXTERNAL_UI_H */
diff --git a/include/moony.h b/include/moony.h
new file mode 100644
index 0000000..0ff8ebb
--- /dev/null
+++ b/include/moony.h
@@ -0,0 +1,474 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _MOONY_H
+#define _MOONY_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdatomic.h>
+
+#if !defined(_WIN32)
+# include <sys/mman.h>
+#else
+# define mlock(...)
+# define munlock(...)
+#endif
+
+#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
+#include <lv2/lv2plug.in/ns/ext/atom/util.h>
+#include <lv2/lv2plug.in/ns/ext/atom/forge.h>
+#include <lv2/lv2plug.in/ns/ext/midi/midi.h>
+#include <lv2/lv2plug.in/ns/ext/time/time.h>
+#include <lv2/lv2plug.in/ns/ext/urid/urid.h>
+#include <lv2/lv2plug.in/ns/ext/worker/worker.h>
+#include <lv2/lv2plug.in/ns/ext/log/log.h>
+#include <lv2/lv2plug.in/ns/ext/log/logger.h>
+#include <lv2/lv2plug.in/ns/ext/state/state.h>
+#include <lv2/lv2plug.in/ns/ext/buf-size/buf-size.h>
+#include <lv2/lv2plug.in/ns/ext/options/options.h>
+#include <lv2/lv2plug.in/ns/ext/patch/patch.h>
+#include <lv2/lv2plug.in/ns/ext/parameters/parameters.h>
+#include <lv2/lv2plug.in/ns/lv2core/lv2.h>
+#include <lv2/lv2plug.in/ns/extensions/ui/ui.h>
+#include <lv2/lv2plug.in/ns/extensions/units/units.h>
+
+typedef struct _atom_ser_t atom_ser_t;
+
+struct _atom_ser_t {
+ void *data; // e.g. use rt-memory pool?
+ uint32_t size;
+ union {
+ uint8_t *buf;
+ const LV2_Atom *atom;
+ };
+ uint32_t offset;
+};
+
+#include <api_vm.h>
+#include <osc.lv2/osc.h>
+#include <xpress.lv2/xpress.h>
+#include <varchunk.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+
+#include <canvas.lv2/forge.h>
+
+#define __realtime __attribute__((annotate("realtime")))
+#define __non_realtime __attribute__((annotate("non-realtime")))
+
+#ifdef LV2_ATOM_TUPLE_FOREACH
+# undef LV2_ATOM_TUPLE_FOREACH
+# define LV2_ATOM_TUPLE_FOREACH(tuple, iter) \
+ for (LV2_Atom* (iter) = lv2_atom_tuple_begin(tuple); \
+ !lv2_atom_tuple_is_end(LV2_ATOM_BODY(tuple), (tuple)->atom.size, (iter)); \
+ (iter) = lv2_atom_tuple_next(iter))
+#endif
+
+#define MOONY_MAX_CHUNK_LEN 0x20000 // 128KB
+#define MOONY_MAX_ERROR_LEN 0x800 // 2KB
+
+#define MOONY_URI "http://open-music-kontrollers.ch/lv2/moony"
+#define MOONY_PREFIX MOONY_URI"#"
+
+#define MOONY_CODE_URI MOONY_URI"#code"
+#define MOONY_ERROR_URI MOONY_URI"#error"
+#define MOONY_TRACE_URI MOONY_URI"#trace"
+#define MOONY_STATE_URI MOONY_URI"#state"
+#define MOONY_PANIC_URI MOONY_URI"#panic"
+
+#define MOONY__color MOONY_URI"#color"
+#define MOONY__syntax MOONY_URI"#syntax"
+
+#define MOONY_EDITOR_HIDDEN_URI MOONY_URI"#editorHidden"
+#define MOONY_LOG_HIDDEN_URI MOONY_URI"#logHidden"
+#define MOONY_LOG_FOLLOW_URI MOONY_URI"#logFollow"
+#define MOONY_LOG_RESET_URI MOONY_URI"#logReset"
+#define MOONY_PARAM_HIDDEN_URI MOONY_URI"#paramHidden"
+
+#define MOONY_PARAM_COLS_URI MOONY_URI"#paramCols"
+#define MOONY_PARAM_ROWS_URI MOONY_URI"#paramRows"
+
+#define MOONY_NK_URI MOONY_URI"#moony_ui"
+#define MOONY_SIMPLE_UI_URI MOONY_URI"#moony_zimple_ui"
+#define MOONY_SIMPLE_KX_URI MOONY_URI"#moony_zimple_kx"
+
+#define MOONY_C1XC1_URI MOONY_URI"#c1xc1"
+#define MOONY_C2XC2_URI MOONY_URI"#c2xc2"
+#define MOONY_C4XC4_URI MOONY_URI"#c4xc4"
+
+#define MOONY_A1XA1_URI MOONY_URI"#a1xa1"
+#define MOONY_A2XA2_URI MOONY_URI"#a2xa2"
+#define MOONY_A4XA4_URI MOONY_URI"#a4xa4"
+
+#define MOONY_C1A1XC1A1_URI MOONY_URI"#c1a1xc1a1"
+#define MOONY_C2A1XC2A1_URI MOONY_URI"#c2a1xc2a1"
+#define MOONY_C4A1XC4A1_URI MOONY_URI"#c4a1xc4a1"
+
+#define LUA__lang "http://lua.org#lang"
+
+extern const LV2_Descriptor c1xc1;
+extern const LV2_Descriptor c2xc2;
+extern const LV2_Descriptor c4xc4;
+
+extern const LV2_Descriptor a1xa1;
+extern const LV2_Descriptor a2xa2;
+extern const LV2_Descriptor a4xa4;
+
+extern const LV2_Descriptor c1a1xc1a1;
+extern const LV2_Descriptor c2a1xc2a1;
+extern const LV2_Descriptor c4a1xc4a1;
+
+extern const LV2UI_Descriptor nk_ui;
+extern const LV2UI_Descriptor simple_ui;
+extern const LV2UI_Descriptor simple_kx;
+
+typedef enum _moony_udata_t {
+ MOONY_UDATA_ATOM,
+ MOONY_UDATA_FORGE,
+ MOONY_UDATA_STASH,
+
+ MOONY_UDATA_COUNT
+} moony_udata_t;
+
+typedef enum _moony_upclosure_t {
+ MOONY_UPCLOSURE_TUPLE_FOREACH,
+ MOONY_UPCLOSURE_VECTOR_FOREACH,
+ MOONY_UPCLOSURE_OBJECT_FOREACH,
+ MOONY_UPCLOSURE_SEQUENCE_FOREACH,
+ MOONY_UPCLOSURE_SEQUENCE_MULTIPLEX,
+
+ MOONY_UPCLOSURE_COUNT
+} moony_upclosure_t;
+
+// from api_atom.c
+typedef struct _lheader_t lheader_t;
+typedef struct _latom_driver_t latom_driver_t;
+typedef struct _latom_driver_hash_t latom_driver_hash_t;
+
+struct _lheader_t {
+ moony_udata_t type;
+ bool cache;
+};
+
+struct _latom_driver_hash_t {
+ LV2_URID type;
+ const latom_driver_t *driver;
+};
+
+#define DRIVER_HASH_MAX 15
+
+// from moony.c
+typedef struct _patch_t patch_t;
+typedef struct _moony_t moony_t;
+
+struct _patch_t {
+ LV2_URID self;
+
+ LV2_URID get;
+ LV2_URID set;
+ LV2_URID put;
+ LV2_URID patch;
+ LV2_URID body;
+ LV2_URID subject;
+ LV2_URID property;
+ LV2_URID value;
+ LV2_URID add;
+ LV2_URID remove;
+ LV2_URID wildcard;
+ LV2_URID writable;
+ LV2_URID readable;
+ LV2_URID destination;
+ LV2_URID sequence;
+ LV2_URID error;
+ LV2_URID ack;
+ LV2_URID delete;
+ LV2_URID copy;
+ LV2_URID move;
+ LV2_URID insert;
+};
+
+struct _moony_t {
+ LV2_URID_Map *map;
+ LV2_URID_Unmap *unmap;
+ LV2_Options_Option *opts;
+
+ LV2_Atom_Forge forge;
+ LV2_Atom_Forge state_forge;
+ LV2_Atom_Forge stash_forge;
+ LV2_Atom_Forge notify_forge;
+
+ LV2_Atom_Forge_Frame notify_frame;
+ LV2_Atom_Forge_Ref notify_ref;
+ LV2_Atom_Forge notify_snapshot;
+
+ LV2_Atom_Float sample_rate;
+
+ struct {
+ LV2_URID moony_code;
+ LV2_URID moony_error;
+ LV2_URID moony_trace;
+ LV2_URID moony_panic;
+ LV2_URID moony_state;
+ LV2_URID moony_editorHidden;
+ LV2_URID moony_logHidden;
+ LV2_URID moony_logFollow;
+ LV2_URID moony_logReset;
+ LV2_URID moony_paramHidden;
+ LV2_URID moony_paramCols;
+ LV2_URID moony_paramRows;
+ LV2_URID moony_color;
+ LV2_URID moony_syntax;
+
+ LV2_URID midi_event;
+
+ patch_t patch;
+
+ LV2_URID rdfs_label;
+ LV2_URID rdfs_range;
+ LV2_URID rdfs_comment;
+
+ LV2_URID rdf_value;
+
+ LV2_URID lv2_minimum;
+ LV2_URID lv2_maximum;
+ LV2_URID lv2_scale_point;
+ LV2_URID lv2_minor_version;
+ LV2_URID lv2_micro_version;
+
+ LV2_URID units_unit;
+ LV2_URID units_symbol;
+
+ LV2_URID atom_beat_time;
+ LV2_URID atom_frame_time;
+ LV2_URID atom_child_type;
+
+ LV2_URID xpress_Token;
+ LV2_URID xpress_Alive;
+ LV2_URID xpress_source;
+ LV2_URID xpress_uuid;
+ LV2_URID xpress_zone;
+ LV2_URID xpress_body;
+ LV2_URID xpress_pitch;
+ LV2_URID xpress_pressure;
+ LV2_URID xpress_timbre;
+ LV2_URID xpress_dPitch;
+ LV2_URID xpress_dPressure;
+ LV2_URID xpress_dTimbre;
+
+ LV2_URID param_sampleRate;
+ } uris;
+
+ LV2_OSC_URID osc_urid;
+ LV2_OSC_Schedule *osc_sched;
+
+ LV2_Worker_Schedule *sched;
+
+ LV2_Log_Log *log;
+ LV2_Log_Logger logger;
+
+ xpress_t xpress;
+
+ LV2_Canvas_URID canvas_urid;
+
+ moony_vm_t *vm;
+ atomic_uintptr_t vm_new;
+
+ bool once;
+ bool error_out;
+
+ // udata cache
+ int itr [MOONY_UDATA_COUNT];
+ int upc [MOONY_UPCLOSURE_COUNT];
+
+ atomic_flag state_lock;
+
+ LV2_Atom *state_atom;
+ atomic_uintptr_t state_atom_new;
+
+ LV2_Atom *stash_atom;
+ uint32_t stash_size;
+
+ varchunk_t *from_dsp;
+
+ latom_driver_hash_t atom_driver_hash [DRIVER_HASH_MAX];
+
+ size_t mem_size;
+ bool testing;
+
+ atomic_int editor_hidden;
+ atomic_int log_hidden;
+ atomic_int log_follow;
+ atomic_int log_reset;
+ atomic_int param_hidden;
+ atomic_int param_cols;
+ atomic_int param_rows;
+
+ char error [MOONY_MAX_ERROR_LEN];
+ atomic_uintptr_t err_new;
+
+ char chunk [MOONY_MAX_CHUNK_LEN];
+ atomic_uintptr_t chunk_new;
+ char *chunk_nrt;
+};
+
+// in api.c
+int moony_init(moony_t *moony, const char *subject, double sample_rate,
+ const LV2_Feature *const *features, size_t mem_size, bool testing);
+void moony_deinit(moony_t *moony);
+void moony_open(moony_t *moony, moony_vm_t *vm, lua_State *L);
+void moony_pre(moony_t *moony, LV2_Atom_Sequence *notify);
+bool moony_in(moony_t *moony, const LV2_Atom_Sequence *control, LV2_Atom_Sequence *notify);
+void moony_out(moony_t *moony, LV2_Atom_Sequence *notify, uint32_t frames);
+const void* extension_data(const char* uri);
+void *moony_newuserdata(lua_State *L, moony_t *moony, moony_udata_t type, bool cache);
+LV2_Worker_Status moony_wake_worker(const LV2_Worker_Schedule *work_sched);
+
+__realtime static inline void
+moony_freeuserdata(moony_t *moony)
+{
+ for(unsigned i=0; i<MOONY_UDATA_COUNT; i++)
+ moony->itr[i] = 1; // reset iterator
+ for(unsigned i=0; i<MOONY_UPCLOSURE_COUNT; i++)
+ moony->upc[i] = 1; // reset iterator
+}
+
+__realtime static inline bool
+moony_bypass(moony_t *moony)
+{
+ return moony->error[0] != 0x0;
+}
+
+__realtime static inline const char *
+_err_skip(const char *msg)
+{
+ const char *err = strstr(msg, "\"]:"); // search end mark of header [string ""]:
+ err = err
+ ? err + 3 // skip header end mark
+ : msg; // use whole error string alternatively
+
+ return err;
+}
+
+__non_realtime static inline void
+moony_err_async(moony_t *moony, const char *msg)
+{
+ const char *err = _err_skip(msg);
+
+ if(moony->log)
+ lv2_log_error(&moony->logger, "%s\n", err);
+
+ char *err_new = strdup(err);
+ if(err_new)
+ {
+ char *err_old = (char *)atomic_exchange_explicit(&moony->err_new, (uintptr_t)err_new, memory_order_relaxed);
+
+ if(err_old)
+ free(err_old);
+ }
+}
+
+__realtime static inline void
+moony_err(moony_t *moony, const char *msg)
+{
+ const char *err = _err_skip(msg);
+
+ if(moony->log)
+ lv2_log_trace(&moony->logger, "%s\n", err);
+
+ if(moony->error[0] == 0x0) // don't overwrite any previous error message
+ snprintf(moony->error, MOONY_MAX_ERROR_LEN, "%s", err);
+ moony->error_out = true;
+}
+
+__realtime static inline void
+moony_trace(moony_t *moony, const char *msg)
+{
+ const char *err = _err_skip(msg);
+
+ if(moony->log)
+ lv2_log_trace(&moony->logger, "%s\n", err);
+}
+
+__realtime static inline lua_State *
+moony_current(moony_t *moony)
+{
+ return moony->vm->L;
+}
+
+__realtime static inline void
+moony_error(moony_t *moony)
+{
+ lua_State *L = moony_current(moony);
+
+ const char *msg = lua_tostring(L, -1);
+ if(msg)
+ moony_err(moony, msg);
+ lua_pop(L, 1);
+}
+
+#define _spin_lock(FLAG) while(atomic_flag_test_and_set_explicit((FLAG), memory_order_acquire)) {}
+#define _try_lock(FLAG) !atomic_flag_test_and_set_explicit((FLAG), memory_order_acquire)
+#define _unlock(FLAG) atomic_flag_clear_explicit((FLAG), memory_order_release)
+
+__realtime static inline LV2_Atom_Forge_Ref
+_moony_patch(patch_t *patch, LV2_Atom_Forge *forge, LV2_URID key,
+ const char *str, uint32_t size)
+{
+ LV2_Atom_Forge_Frame frame;
+
+ LV2_Atom_Forge_Ref ref = lv2_atom_forge_object(forge, &frame, 0, str ? patch->set : patch->get)
+ && lv2_atom_forge_key(forge, patch->subject)
+ && lv2_atom_forge_urid(forge, patch->self)
+ && lv2_atom_forge_key(forge, patch->property)
+ && lv2_atom_forge_urid(forge, key);
+
+ if(ref && str)
+ {
+ ref = lv2_atom_forge_key(forge, patch->value)
+ && lv2_atom_forge_string(forge, str, size);
+ }
+
+ if(ref)
+ {
+ lv2_atom_forge_pop(forge, &frame);
+ return 1; // success
+ }
+
+ return 0; // overflow
+}
+
+void *
+moony_rt_alloc(moony_vm_t *vm, size_t nsize);
+
+void *
+moony_rt_realloc(moony_vm_t *vm, void *buf, size_t osize, size_t nsize);
+
+void
+moony_rt_free(moony_vm_t *vm, void *buf, size_t osize);
+
+LV2_Atom_Forge_Ref
+_sink_rt(LV2_Atom_Forge_Sink_Handle handle, const void *buf, uint32_t size);
+LV2_Atom_Forge_Ref
+_sink_non_rt(LV2_Atom_Forge_Sink_Handle handle, const void *buf, uint32_t size);
+
+LV2_Atom *
+_deref(LV2_Atom_Forge_Sink_Handle handle, LV2_Atom_Forge_Ref ref);
+
+#endif // _MOONY_H
diff --git a/laes128/laes128.c b/laes128/laes128.c
new file mode 100644
index 0000000..ac9046b
--- /dev/null
+++ b/laes128/laes128.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <stdbool.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <laes128.h>
+
+#include <lauxlib.h>
+
+#define ECB 0
+#define CBC 1
+#include <aes.h>
+
+static int
+_hex2data(uint8_t *key, const char *pass)
+{
+ const char *pos = pass;
+ char *endptr;
+
+ for(unsigned count = 0; count < 16; count++, pos += 2)
+ {
+ char buf [5] = {'0', 'x', pos[0], pos[1], '\0'};
+
+ key[count] = strtol(buf, &endptr, 16);
+
+ if(endptr[0] != '\0') //non-hexadecimal character encountered
+ return -1;
+ }
+
+ return 0;
+}
+
+static bool
+_parse_key(lua_State *L, int idx, uint8_t key [16])
+{
+ size_t key_len;
+ const char *pass = luaL_checklstring(L, idx, &key_len);
+
+ switch(key_len)
+ {
+ case 16: // raw key
+ memcpy(key, pass, 16);
+ break;
+ case 32: // hex-encoded key
+ if(_hex2data(key, pass))
+ luaL_error(L, "invalid hexadecimal string");
+ break;
+ default: // invalid key
+ return false;
+ }
+
+ return true; // success
+}
+
+static const uint8_t iv [] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+};
+
+static int
+_laes128_encode(lua_State *L)
+{
+ size_t input_len;
+ const uint8_t *input = (const uint8_t *)luaL_checklstring(L, 1, &input_len);
+
+ uint8_t key [16];
+ if(!_parse_key(L, 2, key))
+ luaL_error(L, "invalid key length");
+
+ const size_t output_len = ((input_len + 15) & (~15)); // round to next 16 byte boundary
+
+ luaL_Buffer input_buf;
+ luaL_Buffer output_buf;
+
+ // create a 16-byte padded copy of input
+ uint8_t *input_copy = (uint8_t *)luaL_buffinitsize(L, &input_buf, output_len);
+ memset(input_copy + output_len - 16, 0x0, 16); // pad last 16 bytes
+ memcpy(input_copy, input, input_len);
+ luaL_pushresultsize(&input_buf, output_len);
+
+ uint8_t *output = (uint8_t *)luaL_buffinitsize(L, &output_buf, output_len);
+
+ aes_t aes;
+ memset(&aes, 0x0, sizeof(aes_t));
+ AES128_CBC_encrypt_buffer(&aes, output, input_copy, output_len, key, iv);
+
+ luaL_pushresultsize(&output_buf, output_len);
+ lua_pushinteger(L, input_len);
+
+ return 2;
+}
+
+static int
+_laes128_decode(lua_State *L)
+{
+ size_t input_len;
+ const uint8_t *input = (const uint8_t *)luaL_checklstring(L, 1, &input_len);
+
+ uint8_t key [16];
+ if(!_parse_key(L, 2, key))
+ luaL_error(L, "invalid key length");
+
+ const size_t encoded_len = luaL_optinteger(L, 3, input_len);
+
+ const size_t output_len = ((input_len + 15) & (~15)); // round to next 16 byte boundary
+
+ luaL_Buffer input_buf;
+ luaL_Buffer output_buf;
+
+ // create a 16-byte padded copy of input
+ uint8_t *input_copy = (uint8_t *)luaL_buffinitsize(L, &input_buf, output_len);
+ memset(input_copy + output_len - 16, 0x0, 16); // pad last 16 bytes
+ memcpy(input_copy, input, input_len);
+ luaL_pushresultsize(&input_buf, output_len);
+
+ uint8_t *output = (uint8_t *)luaL_buffinitsize(L, &output_buf, output_len);
+
+ aes_t aes;
+ memset(&aes, 0x0, sizeof(aes_t));
+ AES128_CBC_decrypt_buffer(&aes, output, input_copy, output_len, key, iv);
+
+ // crop output to encoded_len
+ luaL_pushresultsize(&output_buf, encoded_len);
+
+ return 1;
+}
+
+static const luaL_Reg laes128 [] = {
+ {"encode", _laes128_encode},
+ {"decode", _laes128_decode},
+
+ {NULL, NULL}
+};
+
+LUA_API int
+luaopen_aes128(lua_State *L)
+{
+ luaL_newlib(L, laes128);
+
+ return 1;
+}
diff --git a/laes128/laes128.h b/laes128/laes128.h
new file mode 100644
index 0000000..9bcc496
--- /dev/null
+++ b/laes128/laes128.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <lua.h>
+
+LUA_API int
+luaopen_aes128(lua_State *L);
diff --git a/lascii85/Makefile b/lascii85/Makefile
new file mode 100644
index 0000000..0f3b4be
--- /dev/null
+++ b/lascii85/Makefile
@@ -0,0 +1,48 @@
+# makefile for ascii85 library for Lua
+
+# change these to reflect your Lua installation
+LUA= /tmp/lhf/lua-5.3.3
+LUAINC= $(LUA)/src
+LUALIB= $(LUA)/src
+LUABIN= $(LUA)/src
+
+# these will probably work if Lua has been installed globally
+#LUA= /usr/local
+#LUAINC= $(LUA)/include
+#LUALIB= $(LUA)/lib
+#LUABIN= $(LUA)/bin
+
+# probably no need to change anything below here
+CC= gcc -std=c99
+CFLAGS= $(INCS) $(WARN) -O2 $G
+WARN= -pedantic -Wall -Wextra
+INCS= -I$(LUAINC)
+MAKESO= $(CC) -shared
+#MAKESO= $(CC) -bundle -undefined dynamic_lookup
+
+MYNAME= ascii85
+MYLIB= l$(MYNAME)
+T= $(MYNAME).so
+OBJS= $(MYLIB).o
+TEST= test.lua
+
+all: test
+
+test: $T
+ $(LUABIN)/lua $(TEST)
+
+o: $(MYLIB).o
+
+so: $T
+
+$T: $(OBJS)
+ $(MAKESO) -o $@ $(OBJS)
+
+clean:
+ rm -f $(OBJS) $T core core.*
+
+doc:
+ @echo "$(MYNAME) library:"
+ @fgrep '/**' $(MYLIB).c | cut -f2 -d/ | tr -d '*' | sort | column
+
+# eof
diff --git a/lascii85/README b/lascii85/README
new file mode 100644
index 0000000..c9dbd16
--- /dev/null
+++ b/lascii85/README
@@ -0,0 +1,20 @@
+This is an ascii85 library for Lua 5.2. For information on ascii85 see
+ http://en.wikipedia.org/wiki/Ascii85
+
+To try the library, just edit Makefile to reflect your installation of Lua and
+then run make. This will build the library and run a simple test. For detailed
+installation instructions, see
+ http://www.tecgraf.puc-rio.br/~lhf/ftp/lua/install.html
+
+There is no manual but the library is simple and intuitive; see the summary
+below. Read also test.lua, which shows the library in action.
+
+This code is hereby placed in the public domain.
+Please send comments, suggestions, and bug reports to lhf@tecgraf.puc-rio.br .
+
+-------------------------------------------------------------------------------
+
+ascii85 library:
+ decode(s) encode(s) version
+
+-------------------------------------------------------------------------------
diff --git a/lascii85/lascii85.c b/lascii85/lascii85.c
new file mode 100644
index 0000000..ce1f8cd
--- /dev/null
+++ b/lascii85/lascii85.c
@@ -0,0 +1,130 @@
+/*
+* lascii85.c
+* ascii85 encoding and decoding for Lua 5.2
+* Luiz Henrique de Figueiredo <lhf@tecgraf.puc-rio.br>
+* 27 Sep 2012 19:36:45
+* This code is hereby placed in the public domain.
+*/
+
+#include <string.h>
+
+#include "lua.h"
+#include "lauxlib.h"
+
+#define MYNAME "ascii85"
+#define MYVERSION MYNAME " library for " LUA_VERSION " / Sep 2012"
+
+#define uint unsigned int
+
+static void encode(luaL_Buffer *b, uint c1, uint c2, uint c3, uint c4, int n)
+{
+ unsigned long tuple=c4+256UL*(c3+256UL*(c2+256UL*c1));
+ if (tuple==0 && n==4)
+ luaL_addlstring(b,"z",1);
+ else
+ {
+ int i;
+ char s[5];
+ for (i=0; i<5; i++) {
+ s[4-i] = '!' + (tuple % 85);
+ tuple /= 85;
+ }
+ luaL_addlstring(b,s,n+1);
+ }
+}
+
+static int Lencode(lua_State *L) /** encode(s) */
+{
+ size_t l;
+ const unsigned char *s=(const unsigned char*)luaL_checklstring(L,1,&l);
+ luaL_Buffer b;
+ int n;
+ luaL_buffinit(L,&b);
+ luaL_addlstring(&b,"<~",2);
+ for (n=l/4; n--; s+=4) encode(&b,s[0],s[1],s[2],s[3],4);
+ switch (l%4)
+ {
+ case 1: encode(&b,s[0],0,0,0,1); break;
+ case 2: encode(&b,s[0],s[1],0,0,2); break;
+ case 3: encode(&b,s[0],s[1],s[2],0,3); break;
+ }
+ luaL_addlstring(&b,"~>",2);
+ luaL_pushresult(&b);
+ return 1;
+}
+
+static void decode(luaL_Buffer *b, int c1, int c2, int c3, int c4, int c5, int n)
+{
+ unsigned long tuple=c5+85L*(c4+85L*(c3+85L*(c2+85L*c1)));
+ char s[4];
+ switch (--n)
+ {
+ case 4: s[3]=tuple;
+ case 3: s[2]=tuple >> 8;
+ case 2: s[1]=tuple >> 16;
+ case 1: s[0]=tuple >> 24;
+ }
+ luaL_addlstring(b,s,n);
+}
+
+static int Ldecode(lua_State *L) /** decode(s) */
+{
+ size_t l;
+ const char *s=luaL_checklstring(L,1,&l);
+ luaL_Buffer b;
+ int n=0;
+ char t[5];
+ if (*s++!='<') return 0;
+ if (*s++!='~') return 0;
+ luaL_buffinit(L,&b);
+ for (;;)
+ {
+ int c=*s++;
+ switch (c)
+ {
+ default:
+ if (c<'!' || c>'u') return 0;
+ t[n++]=c-'!';
+ if (n==5)
+ {
+ decode(&b,t[0],t[1],t[2],t[3],t[4],5);
+ n=0;
+ }
+ break;
+ case 'z':
+ luaL_addlstring(&b,"\0\0\0\0",4);
+ break;
+ case '~':
+ if (*s!='>') return 0;
+ switch (n)
+ {
+ case 1: decode(&b,t[0],85,0,0,0,1); break;
+ case 2: decode(&b,t[0],t[1],85,0,0,2); break;
+ case 3: decode(&b,t[0],t[1],t[2],85,0,3); break;
+ case 4: decode(&b,t[0],t[1],t[2],t[3],85,4); break;
+ }
+ luaL_pushresult(&b);
+ return 1;
+ case '\n': case '\r': case '\t': case ' ':
+ case '\0': case '\f': case '\b': case 0177:
+ break;
+ }
+ }
+ return 0;
+}
+
+static const luaL_Reg R[] =
+{
+ { "encode", Lencode },
+ { "decode", Ldecode },
+ { NULL, NULL }
+};
+
+LUALIB_API int luaopen_ascii85(lua_State *L)
+{
+ luaL_newlib(L,R);
+ lua_pushliteral(L,"version"); /** version */
+ lua_pushliteral(L,MYVERSION);
+ lua_settable(L,-3);
+ return 1;
+}
diff --git a/lascii85/test.lua b/lascii85/test.lua
new file mode 100644
index 0000000..ed01483
--- /dev/null
+++ b/lascii85/test.lua
@@ -0,0 +1,43 @@
+-- test ascii85 library
+
+local ascii85=require"ascii85"
+
+print(ascii85.version)
+print""
+
+function test(s)
+ --print""
+ --print(string.len(s),s)
+ local a=ascii85.encode(s)
+ --print(string.len(a),a)
+ local b=ascii85.decode(a)
+ --print(string.len(b),b)
+ print(string.len(s),b==s,a,s)
+ assert(b==s)
+end
+
+for i=0,13 do
+ local s=string.sub("Lua-scripting-language",1,i)
+ test(s)
+end
+
+function test(p)
+ print("testing prefix "..string.len(p))
+ for i=0,255 do
+ local s=p..string.char(i)
+ local a=ascii85.encode(s)
+ local b=ascii85.decode(a)
+ assert(b==s,i)
+ end
+end
+
+print""
+test""
+test"x"
+test"xy"
+test"xyz"
+
+print""
+print(ascii85.version)
+
+-- eof
diff --git a/lbase64/Makefile b/lbase64/Makefile
new file mode 100644
index 0000000..ac9289d
--- /dev/null
+++ b/lbase64/Makefile
@@ -0,0 +1,48 @@
+# makefile for base64 library for Lua
+
+# change these to reflect your Lua installation
+LUA= /tmp/lhf/lua-5.3.2
+LUAINC= $(LUA)/src
+LUALIB= $(LUA)/src
+LUABIN= $(LUA)/src
+
+# these will probably work if Lua has been installed globally
+#LUA= /usr/local
+#LUAINC= $(LUA)/include
+#LUALIB= $(LUA)/lib
+#LUABIN= $(LUA)/bin
+
+# probably no need to change anything below here
+CC= gcc -std=c99
+CFLAGS= $(INCS) $(WARN) -O2 $G
+WARN= -pedantic -Wall -Wextra
+INCS= -I$(LUAINC)
+MAKESO= $(CC) -shared
+#MAKESO= $(CC) -bundle -undefined dynamic_lookup
+
+MYNAME= base64
+MYLIB= l$(MYNAME)
+T= $(MYNAME).so
+OBJS= $(MYLIB).o
+TEST= test.lua
+
+all: test
+
+test: $T
+ $(LUABIN)/lua $(TEST)
+
+o: $(MYLIB).o
+
+so: $T
+
+$T: $(OBJS)
+ $(MAKESO) -o $@ $(OBJS)
+
+clean:
+ rm -f $(OBJS) $T core core.*
+
+doc:
+ @echo "$(MYNAME) library:"
+ @fgrep '/**' $(MYLIB).c | cut -f2 -d/ | tr -d '*' | sort | column
+
+# eof
diff --git a/lbase64/README b/lbase64/README
new file mode 100644
index 0000000..2ca1ad0
--- /dev/null
+++ b/lbase64/README
@@ -0,0 +1,20 @@
+This is a base64 library for Lua 5.3. For information on base64 see
+ http://en.wikipedia.org/wiki/Base64
+
+To try the library, just edit Makefile to reflect your installation of Lua and
+then run make. This will build the library and run a simple test. For detailed
+installation instructions, see
+ http://www.tecgraf.puc-rio.br/~lhf/ftp/lua/install.html
+
+There is no manual but the library is simple and intuitive; see the summary
+below. Read also test.lua, which shows the library in action.
+
+This code is hereby placed in the public domain.
+Please send comments, suggestions, and bug reports to lhf@tecgraf.puc-rio.br .
+
+-------------------------------------------------------------------------------
+
+base64 library:
+ decode(s) encode(s) version
+
+-------------------------------------------------------------------------------
diff --git a/lbase64/lbase64.c b/lbase64/lbase64.c
new file mode 100644
index 0000000..2cc94ae
--- /dev/null
+++ b/lbase64/lbase64.c
@@ -0,0 +1,119 @@
+/*
+* lbase64.c
+* base64 encoding and decoding for Lua 5.2
+* Luiz Henrique de Figueiredo <lhf@tecgraf.puc-rio.br>
+* 07 Aug 2012 23:22:44
+* This code is hereby placed in the public domain.
+*/
+
+#include <string.h>
+
+#include "lua.h"
+#include "lauxlib.h"
+
+#define MYNAME "base64"
+#define MYVERSION MYNAME " library for " LUA_VERSION " / Aug 2012"
+
+#define uint unsigned int
+
+static const char code[]=
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static void encode(luaL_Buffer *b, uint c1, uint c2, uint c3, int n)
+{
+ unsigned long tuple=c3+256UL*(c2+256UL*c1);
+ int i;
+ char s[4];
+ for (i=0; i<4; i++) {
+ s[3-i] = code[tuple % 64];
+ tuple /= 64;
+ }
+ for (i=n+1; i<4; i++) s[i]='=';
+ luaL_addlstring(b,s,4);
+}
+
+static int Lencode(lua_State *L) /** encode(s) */
+{
+ size_t l;
+ const unsigned char *s=(const unsigned char*)luaL_checklstring(L,1,&l);
+ luaL_Buffer b;
+ int n;
+ luaL_buffinit(L,&b);
+ for (n=l/3; n--; s+=3) encode(&b,s[0],s[1],s[2],3);
+ switch (l%3)
+ {
+ case 1: encode(&b,s[0],0,0,1); break;
+ case 2: encode(&b,s[0],s[1],0,2); break;
+ }
+ luaL_pushresult(&b);
+ return 1;
+}
+
+static void decode(luaL_Buffer *b, int c1, int c2, int c3, int c4, int n)
+{
+ unsigned long tuple=c4+64L*(c3+64L*(c2+64L*c1));
+ char s[3];
+ switch (--n)
+ {
+ case 3: s[2]=tuple;
+ case 2: s[1]=tuple >> 8;
+ case 1: s[0]=tuple >> 16;
+ }
+ luaL_addlstring(b,s,n);
+}
+
+static int Ldecode(lua_State *L) /** decode(s) */
+{
+ size_t l;
+ const char *s=luaL_checklstring(L,1,&l);
+ luaL_Buffer b;
+ int n=0;
+ char t[4];
+ luaL_buffinit(L,&b);
+ for (;;)
+ {
+ int c=*s++;
+ switch (c)
+ {
+ const char *p;
+ default:
+ p=strchr(code,c); if (p==NULL) return 0;
+ t[n++]= p-code;
+ if (n==4)
+ {
+ decode(&b,t[0],t[1],t[2],t[3],4);
+ n=0;
+ }
+ break;
+ case '=':
+ switch (n)
+ {
+ case 1: decode(&b,t[0],0,0,0,1); break;
+ case 2: decode(&b,t[0],t[1],0,0,2); break;
+ case 3: decode(&b,t[0],t[1],t[2],0,3); break;
+ }
+ case 0:
+ luaL_pushresult(&b);
+ return 1;
+ case '\n': case '\r': case '\t': case ' ': case '\f': case '\b':
+ break;
+ }
+ }
+ return 0;
+}
+
+static const luaL_Reg R[] =
+{
+ { "encode", Lencode },
+ { "decode", Ldecode },
+ { NULL, NULL }
+};
+
+LUALIB_API int luaopen_base64(lua_State *L)
+{
+ luaL_newlib(L,R);
+ lua_pushliteral(L,"version"); /** version */
+ lua_pushliteral(L,MYVERSION);
+ lua_settable(L,-3);
+ return 1;
+}
diff --git a/lbase64/test.lua b/lbase64/test.lua
new file mode 100644
index 0000000..1ff7ec7
--- /dev/null
+++ b/lbase64/test.lua
@@ -0,0 +1,65 @@
+-- test base64 library
+
+local base64=require"base64"
+
+print(base64.version)
+print""
+
+function test(s)
+ local a=base64.encode(s)
+ local b=base64.decode(a)
+ print(string.len(s),b==s,a,s)
+ assert(b==s)
+end
+
+for i=0,9 do
+ local s=string.sub("Lua-scripting-language",1,i)
+ test(s)
+end
+
+function test(p)
+ print("testing prefix "..string.len(p))
+ for i=0,255 do
+ local s=p..string.char(i)
+ local a=base64.encode(s)
+ local b=base64.decode(a)
+ assert(b==s,i)
+ end
+end
+
+print""
+test""
+test"x"
+test"xy"
+test"xyz"
+
+print""
+s="Lua-scripting-language"
+a=base64.encode(s)
+b=base64.decode(a)
+print(a,b,string.len(b))
+
+a=base64.encode(s)
+a=string.gsub(a,"[A-Z]","?")
+b=base64.decode(a)
+print(a,b)
+
+a=base64.encode(s)
+a=string.gsub(a,"[a-z]","?")
+b=base64.decode(a)
+print(a,b)
+
+a=base64.encode(s)
+a=string.gsub(a,"[A-Z]","=")
+b=base64.decode(a)
+print(a,b,string.len(b))
+
+a=base64.encode(s)
+a=string.gsub(a,"[a-z]","=")
+b=base64.decode(a)
+print(a,b,string.len(b))
+
+print""
+print(base64.version)
+
+-- eof
diff --git a/lcomplex/Makefile b/lcomplex/Makefile
new file mode 100644
index 0000000..975267c
--- /dev/null
+++ b/lcomplex/Makefile
@@ -0,0 +1,48 @@
+# makefile for complex library for Lua
+
+# change these to reflect your Lua installation
+LUA= /var/tmp/lhf/lua-5.3.2
+LUAINC= $(LUA)/src
+LUALIB= $(LUA)/src
+LUABIN= $(LUA)/src
+
+# these will probably work if Lua has been installed globally
+#LUA= /usr/local
+#LUAINC= $(LUA)/include
+#LUALIB= $(LUA)/lib
+#LUABIN= $(LUA)/bin
+
+# probably no need to change anything below here
+CC= gcc
+CFLAGS= -std=c99 $(INCS) $(WARN) -O2 $G
+WARN= -pedantic -Wall -Wextra
+INCS= -I$(LUAINC)
+MAKESO= $(CC) -shared
+#MAKESO= $(CC) -bundle -undefined dynamic_lookup
+
+MYNAME= complex
+MYLIB= l$(MYNAME)
+T= $(MYNAME).so
+OBJS= $(MYLIB).o
+TEST= test.lua
+
+all: test
+
+test: $T
+ $(LUABIN)/lua $(TEST)
+
+o: $(MYLIB).o
+
+so: $T
+
+$T: $(OBJS)
+ $(MAKESO) -o $@ $(OBJS)
+
+clean:
+ rm -f $(OBJS) $T core core.*
+
+doc:
+ @echo "$(MYNAME) library:"
+ @fgrep '/**' $(MYLIB).c | cut -f2 -d/ | tr -d '*' | sort | column
+
+# eof
diff --git a/lcomplex/README b/lcomplex/README
new file mode 100644
index 0000000..4512bfa
--- /dev/null
+++ b/lcomplex/README
@@ -0,0 +1,29 @@
+This code provides support for complex numbers in Lua 5.3 using the functions
+available in C99.
+
+To try the library, just edit Makefile to reflect your installation of Lua and
+then run make. This will build the library and run a simple test. For detailed
+installation instructions, see
+ http://www.tecgraf.puc-rio.br/~lhf/ftp/lua/install.html
+
+There is no manual: see the summary below and a C99 reference manual, e.g.
+http://www.dinkumware.com/manuals/default.aspx?manual=compleat&page=complex2.html
+
+Read also test.lua, which shows the library in action.
+
+This code is hereby placed in the public domain.
+
+Please send comments, suggestions, and bug reports to lhf@tecgraf.puc-rio.br .
+
+-------------------------------------------------------------------------------
+
+complex library:
+ __add(z,w) __unm(z) atan(z) log(z) sqrt(z)
+ __div(z,w) abs(z) atanh(z) new(x,y) tan(z)
+ __eq(z,w) acos(z) conj(z) pow(z,w) tanh(z)
+ __mul(z,w) acosh(z) cos(z) proj(z) tostring(z)
+ __pow(z,w) arg(z) cosh(z) real(z) version
+ __sub(z,w) asin(z) exp(z) sin(z) I
+ __tostring(z) asinh(z) imag(z) sinh(z)
+
+-------------------------------------------------------------------------------
diff --git a/lcomplex/lcomplex.c b/lcomplex/lcomplex.c
new file mode 100644
index 0000000..0168f6e
--- /dev/null
+++ b/lcomplex/lcomplex.c
@@ -0,0 +1,172 @@
+/*
+* lcomplex.c
+* C99 complex numbers for Lua 5.3
+* Luiz Henrique de Figueiredo <lhf@tecgraf.puc-rio.br>
+* 20 May 2016 14:00:47
+* This code is hereby placed in the public domain.
+*/
+
+#include <complex.h>
+
+#include "lua.h"
+#include "lauxlib.h"
+
+#define Complex LUA_NUMBER complex
+#define MYNAME "complex"
+#define MYTYPE MYNAME " number"
+#define MYVERSION MYTYPE " library for " LUA_VERSION " / May 2016"
+
+#define Z(i) Pget(L,i)
+#define O(i) luaL_optnumber(L,i,0)
+
+#define cadd(z,w) ((z)+(w))
+#define csub(z,w) ((z)-(w))
+#define cmul(z,w) ((z)*(w))
+#define cdiv(z,w) ((z)/(w))
+#define cneg(z) (-(z))
+#define cconj l_mathop(conj)
+
+static Complex Pget(lua_State *L, int i)
+{
+ switch (lua_type(L,i))
+ {
+ case LUA_TNUMBER:
+ case LUA_TSTRING:
+ return luaL_checknumber(L,i);
+ default:
+ return *((Complex*)luaL_checkudata(L,i,MYTYPE));
+ }
+}
+
+static int pushcomplex(lua_State *L, Complex z)
+{
+ Complex *p=lua_newuserdata(L,sizeof(Complex));
+ *p=z;
+ luaL_setmetatable(L,MYTYPE);
+ return 1;
+}
+
+static int Leq(lua_State *L) /** __eq(z,w) */
+{
+ lua_pushboolean(L,Z(1)==Z(2));
+ return 1;
+}
+
+static int Ltostring(lua_State *L) /** tostring(z) */
+{
+ Complex z=Z(1);
+ LUA_NUMBER x=creal(z);
+ LUA_NUMBER y=cimag(z);
+ lua_settop(L,0);
+ if (x!=0 || y==0) lua_pushnumber(L,x);
+ if (y!=0)
+ {
+ if (y==1)
+ {
+ if (x!=0) lua_pushliteral(L,"+");
+ }
+ else if (y==-1)
+ lua_pushliteral(L,"-");
+ else
+ {
+ if (y>0 && x!=0) lua_pushliteral(L,"+");
+ lua_pushnumber(L,y);
+ }
+ lua_pushliteral(L,"i");
+ }
+ lua_concat(L,lua_gettop(L));
+ return 1;
+}
+
+#define A(f,e) static int L##f(lua_State *L) { return pushcomplex(L,e); }
+#define B(f) A(f,l_mathop(c##f)(Z(1),Z(2)))
+#define F(f) A(f,l_mathop(c##f)(Z(1)))
+#define G(f) static int L##f(lua_State *L) { lua_pushnumber(L,l_mathop(c##f)(Z(1))); return 1; }
+
+A(new,O(1)+O(2)*I) /** new(x,y) */
+B(add) /** __add(z,w) */
+B(div) /** __div(z,w) */
+B(mul) /** __mul(z,w) */
+B(sub) /** __sub(z,w) */
+F(neg) /** __unm(z) */
+G(abs) /** abs(z) */
+F(acos) /** acos(z) */
+F(acosh) /** acosh(z) */
+G(arg) /** arg(z) */
+F(asin) /** asin(z) */
+F(asinh) /** asinh(z) */
+F(atan) /** atan(z) */
+F(atanh) /** atanh(z) */
+F(conj) /** conj(z) */
+F(cos) /** cos(z) */
+F(cosh) /** cosh(z) */
+F(exp) /** exp(z) */
+G(imag) /** imag(z) */
+F(log) /** log(z) */
+B(pow) /** pow(z,w) */
+F(proj) /** proj(z) */
+G(real) /** real(z) */
+F(sin) /** sin(z) */
+F(sinh) /** sinh(z) */
+F(sqrt) /** sqrt(z) */
+F(tan) /** tan(z) */
+F(tanh) /** tanh(z) */
+
+static const luaL_Reg R[] =
+{
+ { "__add", Ladd },
+ { "__div", Ldiv },
+ { "__eq", Leq },
+ { "__mul", Lmul },
+ { "__sub", Lsub },
+ { "__unm", Lneg },
+ { "abs", Labs },
+ { "acos", Lacos },
+ { "acosh", Lacosh },
+ { "arg", Larg },
+ { "asin", Lasin },
+ { "asinh", Lasinh },
+ { "atan", Latan },
+ { "atanh", Latanh },
+ { "conj", Lconj },
+ { "cos", Lcos },
+ { "cosh", Lcosh },
+ { "exp", Lexp },
+ { "imag", Limag },
+ { "log", Llog },
+ { "new", Lnew },
+ { "pow", Lpow },
+ { "proj", Lproj },
+ { "real", Lreal },
+ { "sin", Lsin },
+ { "sinh", Lsinh },
+ { "sqrt", Lsqrt },
+ { "tan", Ltan },
+ { "tanh", Ltanh },
+ { "tostring", Ltostring},
+ { NULL, NULL }
+};
+
+LUALIB_API int luaopen_complex(lua_State *L)
+{
+ luaL_newmetatable(L,MYTYPE);
+ luaL_setfuncs(L,R,0);
+ lua_pushliteral(L,"version"); /** version */
+ lua_pushliteral(L,MYVERSION);
+ lua_settable(L,-3);
+ lua_pushliteral(L,"__index");
+ lua_pushvalue(L,-2);
+ lua_settable(L,-3);
+ lua_pushliteral(L,"I"); /** I */
+ pushcomplex(L,I);
+ lua_settable(L,-3);
+ lua_pushliteral(L,"__pow"); /** __pow(z,w) */
+ lua_pushliteral(L,"pow");
+ lua_gettable(L,-3);
+ lua_settable(L,-3);
+ lua_pushliteral(L,"__tostring"); /** __tostring(z) */
+ lua_pushliteral(L,"tostring");
+ lua_gettable(L,-3);
+ lua_settable(L,-3);
+ return 1;
+}
diff --git a/lcomplex/test.lua b/lcomplex/test.lua
new file mode 100644
index 0000000..ffc9dcb
--- /dev/null
+++ b/lcomplex/test.lua
@@ -0,0 +1,41 @@
+-- test complex library
+
+------------------------------------------------------------------------------
+local complex=require"complex"
+
+print(complex.version)
+
+z=complex.new(3,4)
+print(z,"ABS",z:abs())
+print(z,"CONJ",z:conj())
+print(z,"CABS",z:conj():abs())
+print(z,"NEG",-z)
+
+z=complex.new(0,math.pi)
+print(z,"EXP",z:exp())
+
+z=complex.new(0,2*math.pi/3)
+print(z,"EXP",z:exp(),(2*z:exp():imag())^2)
+
+--I=complex.new(0,1)
+I=complex.I
+
+z=1
+for n=0,7 do
+ print(n,"MUL",z,"POW",I^n)
+ z=z*I
+end
+
+z=3+4*I
+print(z,"ABS",z:abs())
+print(z,"ARG",z:arg())
+print(z,"SQRT",z:sqrt())
+z=2+I
+print(z,"SQR",z^2)
+print(z,"CUB",z^3)
+
+z=2+11*I
+print(z,"CBRT",z^(1/3))
+
+print(complex.version)
+------------------------------------------------------------------------------
diff --git a/lmathx/Makefile b/lmathx/Makefile
new file mode 100644
index 0000000..3665f1a
--- /dev/null
+++ b/lmathx/Makefile
@@ -0,0 +1,48 @@
+# makefile for mathx library for Lua
+
+# change these to reflect your Lua installation
+LUA= /var/tmp/lhf/lua-5.3.1
+LUAINC= $(LUA)/src
+LUALIB= $(LUA)/src
+LUABIN= $(LUA)/src
+
+# these will probably work if Lua has been installed globally
+#LUA= /usr/local
+#LUAINC= $(LUA)/include
+#LUALIB= $(LUA)/lib
+#LUABIN= $(LUA)/bin
+
+# probably no need to change anything below here
+CC= gcc
+CFLAGS= -std=c99 $(INCS) $(WARN) -O2 $G
+WARN= -pedantic -Wall -Wextra
+INCS= -I$(LUAINC)
+MAKESO= $(CC) -shared
+#MAKESO= $(CC) -bundle -undefined dynamic_lookup
+
+MYNAME= mathx
+MYLIB= l$(MYNAME)
+T= $(MYNAME).so
+OBJS= $(MYLIB).o
+TEST= test.lua
+
+all: test
+
+test: $T
+ $(LUABIN)/lua $(TEST)
+
+o: $(MYLIB).o
+
+so: $T
+
+$T: $(OBJS)
+ $(MAKESO) -o $@ $(OBJS)
+
+clean:
+ rm -f $(OBJS) $T core core.*
+
+doc:
+ @echo "$(MYNAME) library:"
+ @fgrep '/**' $(MYLIB).c | cut -f2 -d/ | tr -d '*' | sort | column
+
+# eof
diff --git a/lmathx/README b/lmathx/README
new file mode 100644
index 0000000..e6407b9
--- /dev/null
+++ b/lmathx/README
@@ -0,0 +1,34 @@
+This is a complete math library for Lua 5.3 with the functions available
+in C99. It can replace the standard Lua math library, except that mathx
+deals exclusively with floats.
+
+To try the library, just edit Makefile to reflect your installation of Lua and
+then run make. This will build the library and run a simple test. For detailed
+installation instructions, see
+ http://www.tecgraf.puc-rio.br/~lhf/ftp/lua/install.html
+
+There is no manual: see the summary below and a C99 reference manual, e.g.
+ http://en.wikipedia.org/wiki/C_mathematical_functions
+
+Read also test.lua, which shows the library in action.
+
+This code is hereby placed in the public domain.
+
+Please send comments, suggestions, and bug reports to lhf@tecgraf.puc-rio.br .
+
+-------------------------------------------------------------------------------
+
+mathx library:
+ acos cosh fmax lgamma remainder
+ acosh deg fmin log round
+ asin erf fmod log10 scalbn
+ asinh erfc frexp log1p sin
+ atan exp gamma log2 sinh
+ atan2 exp2 hypot logb sqrt
+ atanh expm1 isfinite modf tan
+ cbrt fabs isinf nearbyint tanh
+ ceil fdim isnan nextafter trunc
+ copysign floor isnormal pow version
+ cos fma ldexp rad
+
+-------------------------------------------------------------------------------
diff --git a/lmathx/lmathx.c b/lmathx/lmathx.c
new file mode 100644
index 0000000..a727eb6
--- /dev/null
+++ b/lmathx/lmathx.c
@@ -0,0 +1,438 @@
+/*
+* lmathx.c
+* C99 math functions for Lua 5.3
+* Luiz Henrique de Figueiredo <lhf@tecgraf.puc-rio.br>
+* 24 Jun 2015 09:51:50
+* This code is hereby placed in the public domain.
+*/
+
+#include <math.h>
+
+#include "lua.h"
+#include "lauxlib.h"
+
+#define MYNAME "mathx"
+#define MYVERSION MYNAME " library for " LUA_VERSION " / Jun 2015"
+
+#define A(i) luaL_checknumber(L,i)
+#define I(i) ((int)luaL_checkinteger(L,i))
+
+#undef PI
+#define PI (l_mathop(3.141592653589793238462643383279502884))
+#define rad(x) ((x)*(PI/l_mathop(180.0)))
+#define deg(x) ((x)*(l_mathop(180.0)/PI))
+
+static int Lfmax(lua_State *L) /** fmax */
+{
+ int i,n=lua_gettop(L);
+ lua_Number m=A(1);
+ for (i=2; i<=n; i++) m=l_mathop(fmax)(m,A(i));
+ lua_pushnumber(L,m);
+ return 1;
+}
+
+static int Lfmin(lua_State *L) /** fmin */
+{
+ int i,n=lua_gettop(L);
+ lua_Number m=A(1);
+ for (i=2; i<=n; i++) m=l_mathop(fmin)(m,A(i));
+ lua_pushnumber(L,m);
+ return 1;
+}
+
+static int Lfrexp(lua_State *L) /** frexp */
+{
+ int e;
+ lua_pushnumber(L,l_mathop(frexp)(A(1),&e));
+ lua_pushinteger(L,e);
+ return 2;
+}
+
+static int Lldexp(lua_State *L) /** ldexp */
+{
+ lua_pushnumber(L,l_mathop(ldexp)(A(1),I(2)));
+ return 1;
+}
+
+static int Lmodf(lua_State *L) /** modf */
+{
+ lua_Number ip;
+ lua_Number fp=l_mathop(modf)(A(1),&ip);
+ lua_pushnumber(L,ip);
+ lua_pushnumber(L,fp);
+ return 2;
+}
+
+static int Lfabs(lua_State *L) /** fabs */
+{
+ lua_pushnumber(L,l_mathop(fabs)(A(1)));
+ return 1;
+}
+
+static int Lacos(lua_State *L) /** acos */
+{
+ lua_pushnumber(L,l_mathop(acos)(A(1)));
+ return 1;
+}
+
+static int Lacosh(lua_State *L) /** acosh */
+{
+ lua_pushnumber(L,l_mathop(acosh)(A(1)));
+ return 1;
+}
+
+static int Lasin(lua_State *L) /** asin */
+{
+ lua_pushnumber(L,l_mathop(asin)(A(1)));
+ return 1;
+}
+
+static int Lasinh(lua_State *L) /** asinh */
+{
+ lua_pushnumber(L,l_mathop(asinh)(A(1)));
+ return 1;
+}
+
+static int Latan(lua_State *L) /** atan */
+{
+ int n=lua_gettop(L);
+ if (n==1)
+ lua_pushnumber(L,l_mathop(atan)(A(1)));
+ else
+ lua_pushnumber(L,l_mathop(atan2)(A(1),A(2)));
+ return 1;
+}
+
+static int Latan2(lua_State *L) /** atan2 */
+{
+ lua_pushnumber(L,l_mathop(atan2)(A(1),A(2)));
+ return 1;
+}
+
+static int Latanh(lua_State *L) /** atanh */
+{
+ lua_pushnumber(L,l_mathop(atanh)(A(1)));
+ return 1;
+}
+
+static int Lcbrt(lua_State *L) /** cbrt */
+{
+ lua_pushnumber(L,l_mathop(cbrt)(A(1)));
+ return 1;
+}
+
+static int Lceil(lua_State *L) /** ceil */
+{
+ lua_pushnumber(L,l_mathop(ceil)(A(1)));
+ return 1;
+}
+
+static int Lcopysign(lua_State *L) /** copysign */
+{
+ lua_pushnumber(L,l_mathop(copysign)(A(1),A(2)));
+ return 1;
+}
+
+static int Lcos(lua_State *L) /** cos */
+{
+ lua_pushnumber(L,l_mathop(cos)(A(1)));
+ return 1;
+}
+
+static int Lcosh(lua_State *L) /** cosh */
+{
+ lua_pushnumber(L,l_mathop(cosh)(A(1)));
+ return 1;
+}
+
+static int Ldeg(lua_State *L) /** deg */
+{
+ lua_pushnumber(L,deg(A(1)));
+ return 1;
+}
+
+static int Lerf(lua_State *L) /** erf */
+{
+ lua_pushnumber(L,l_mathop(erf)(A(1)));
+ return 1;
+}
+
+static int Lerfc(lua_State *L) /** erfc */
+{
+ lua_pushnumber(L,l_mathop(erfc)(A(1)));
+ return 1;
+}
+
+static int Lexp(lua_State *L) /** exp */
+{
+ lua_pushnumber(L,l_mathop(exp)(A(1)));
+ return 1;
+}
+
+static int Lexp2(lua_State *L) /** exp2 */
+{
+ lua_pushnumber(L,l_mathop(exp2)(A(1)));
+ return 1;
+}
+
+static int Lexpm1(lua_State *L) /** expm1 */
+{
+ lua_pushnumber(L,l_mathop(expm1)(A(1)));
+ return 1;
+}
+
+static int Lfdim(lua_State *L) /** fdim */
+{
+ lua_pushnumber(L,l_mathop(fdim)(A(1),A(2)));
+ return 1;
+}
+
+static int Lfloor(lua_State *L) /** floor */
+{
+ lua_pushnumber(L,l_mathop(floor)(A(1)));
+ return 1;
+}
+
+static int Lfma(lua_State *L) /** fma */
+{
+ lua_pushnumber(L,l_mathop(fma)(A(1),A(2),A(3)));
+ return 1;
+}
+
+static int Lfmod(lua_State *L) /** fmod */
+{
+ lua_pushnumber(L,l_mathop(fmod)(A(1),A(2)));
+ return 1;
+}
+
+static int Lgamma(lua_State *L) /** gamma */
+{
+ lua_pushnumber(L,l_mathop(tgamma)(A(1)));
+ return 1;
+}
+
+static int Lhypot(lua_State *L) /** hypot */
+{
+ lua_pushnumber(L,l_mathop(hypot)(A(1),A(2)));
+ return 1;
+}
+
+static int Lisfinite(lua_State *L) /** isfinite */
+{
+ lua_pushboolean(L,isfinite(A(1)));
+ return 1;
+}
+
+static int Lisinf(lua_State *L) /** isinf */
+{
+ lua_pushboolean(L,isinf(A(1)));
+ return 1;
+}
+
+static int Lisnan(lua_State *L) /** isnan */
+{
+ lua_pushboolean(L,isnan(A(1)));
+ return 1;
+}
+
+static int Lisnormal(lua_State *L) /** isnormal */
+{
+ lua_pushboolean(L,isnormal(A(1)));
+ return 1;
+}
+
+static int Llgamma(lua_State *L) /** lgamma */
+{
+ lua_pushnumber(L,l_mathop(lgamma)(A(1)));
+ return 1;
+}
+
+static int Llog(lua_State *L) /** log */
+{
+ int n=lua_gettop(L);
+ if (n==1)
+ lua_pushnumber(L,l_mathop(log)(A(1)));
+ else
+ {
+ lua_Number b=A(2);
+ if (b==10.0)
+ lua_pushnumber(L,l_mathop(log10)(A(1)));
+ else if (b==2.0)
+ lua_pushnumber(L,l_mathop(log2)(A(1)));
+ else
+ lua_pushnumber(L,l_mathop(log)(A(1))/l_mathop(log)(b));
+ }
+ return 1;
+}
+
+static int Llog10(lua_State *L) /** log10 */
+{
+ lua_pushnumber(L,l_mathop(log10)(A(1)));
+ return 1;
+}
+
+static int Llog1p(lua_State *L) /** log1p */
+{
+ lua_pushnumber(L,l_mathop(log1p)(A(1)));
+ return 1;
+}
+
+static int Llog2(lua_State *L) /** log2 */
+{
+ lua_pushnumber(L,l_mathop(log2)(A(1)));
+ return 1;
+}
+
+static int Llogb(lua_State *L) /** logb */
+{
+ lua_pushnumber(L,l_mathop(logb)(A(1)));
+ return 1;
+}
+
+static int Lnearbyint(lua_State *L) /** nearbyint */
+{
+ lua_pushnumber(L,l_mathop(nearbyint)(A(1)));
+ return 1;
+}
+
+static int Lnextafter(lua_State *L) /** nextafter */
+{
+ lua_pushnumber(L,l_mathop(nextafter)(A(1),A(2)));
+ return 1;
+}
+
+static int Lpow(lua_State *L) /** pow */
+{
+ lua_pushnumber(L,l_mathop(pow)(A(1),A(2)));
+ return 1;
+}
+
+static int Lrad(lua_State *L) /** rad */
+{
+ lua_pushnumber(L,rad(A(1)));
+ return 1;
+}
+
+static int Lremainder(lua_State *L) /** remainder */
+{
+ lua_pushnumber(L,l_mathop(remainder)(A(1),A(2)));
+ return 1;
+}
+
+static int Lround(lua_State *L) /** round */
+{
+ lua_pushnumber(L,l_mathop(round)(A(1)));
+ return 1;
+}
+
+static int Lscalbn(lua_State *L) /** scalbn */
+{
+ lua_pushnumber(L,l_mathop(scalbn)(A(1),A(2)));
+ return 1;
+}
+
+static int Lsin(lua_State *L) /** sin */
+{
+ lua_pushnumber(L,l_mathop(sin)(A(1)));
+ return 1;
+}
+
+static int Lsinh(lua_State *L) /** sinh */
+{
+ lua_pushnumber(L,l_mathop(sinh)(A(1)));
+ return 1;
+}
+
+static int Lsqrt(lua_State *L) /** sqrt */
+{
+ lua_pushnumber(L,l_mathop(sqrt)(A(1)));
+ return 1;
+}
+
+static int Ltan(lua_State *L) /** tan */
+{
+ lua_pushnumber(L,l_mathop(tan)(A(1)));
+ return 1;
+}
+
+static int Ltanh(lua_State *L) /** tanh */
+{
+ lua_pushnumber(L,l_mathop(tanh)(A(1)));
+ return 1;
+}
+
+static int Ltrunc(lua_State *L) /** trunc */
+{
+ lua_pushnumber(L,l_mathop(trunc)(A(1)));
+ return 1;
+}
+
+static const luaL_Reg R[] =
+{
+ { "fabs", Lfabs },
+ { "acos", Lacos },
+ { "acosh", Lacosh },
+ { "asin", Lasin },
+ { "asinh", Lasinh },
+ { "atan", Latan },
+ { "atan2", Latan2 },
+ { "atanh", Latanh },
+ { "cbrt", Lcbrt },
+ { "ceil", Lceil },
+ { "copysign", Lcopysign },
+ { "cos", Lcos },
+ { "cosh", Lcosh },
+ { "deg", Ldeg },
+ { "erf", Lerf },
+ { "erfc", Lerfc },
+ { "exp", Lexp },
+ { "exp2", Lexp2 },
+ { "expm1", Lexpm1 },
+ { "fdim", Lfdim },
+ { "floor", Lfloor },
+ { "fma", Lfma },
+ { "fmax", Lfmax },
+ { "fmin", Lfmin },
+ { "fmod", Lfmod },
+ { "frexp", Lfrexp },
+ { "gamma", Lgamma },
+ { "hypot", Lhypot },
+ { "isfinite", Lisfinite },
+ { "isinf", Lisinf },
+ { "isnan", Lisnan },
+ { "isnormal", Lisnormal },
+ { "ldexp", Lldexp },
+ { "lgamma", Llgamma },
+ { "log", Llog },
+ { "log10", Llog10 },
+ { "log1p", Llog1p },
+ { "log2", Llog2 },
+ { "logb", Llogb },
+ { "modf", Lmodf },
+ { "nearbyint", Lnearbyint },
+ { "nextafter", Lnextafter },
+ { "pow", Lpow },
+ { "rad", Lrad },
+ { "remainder", Lremainder },
+ { "round", Lround },
+ { "scalbn", Lscalbn },
+ { "sin", Lsin },
+ { "sinh", Lsinh },
+ { "sqrt", Lsqrt },
+ { "tan", Ltan },
+ { "tanh", Ltanh },
+ { "trunc", Ltrunc },
+ { NULL, NULL }
+};
+
+LUALIB_API int luaopen_mathx(lua_State *L)
+{
+ luaL_newlib(L,R);
+ lua_pushliteral(L,"version"); /** version */
+ lua_pushliteral(L,MYVERSION);
+ lua_settable(L,-3);
+ lua_pushnumber(L,INFINITY); lua_setfield(L,-2,"inf");
+ lua_pushnumber(L,NAN); lua_setfield(L,-2,"nan");
+ lua_pushnumber(L,PI); lua_setfield(L,-2,"pi");
+ return 1;
+}
diff --git a/lmathx/test.lua b/lmathx/test.lua
new file mode 100644
index 0000000..36f150d
--- /dev/null
+++ b/lmathx/test.lua
@@ -0,0 +1,87 @@
+-- test mathx library
+
+------------------------------------------------------------------------------
+local math=require"mathx"
+
+print(math.version)
+
+print""
+function f(x)
+ print(x,math.isfinite(x),math.isnan(x),math.isnormal(x))
+end
+
+print("x","finite","nan","normal")
+f(0)
+f(1/-math.inf)
+f(1/0)
+f(-1/0)
+f(0/0)
+f(math.inf)
+--f(math.huge)
+f(math.nan)
+f(3.45)
+
+print""
+print("x","\t","finite","nan","normal")
+x=1
+while x~=0 do
+ y,x=x,x/2
+ if not math.isnormal(x) then break end
+end
+f(y)
+f(x)
+f(math.nextafter(x,1))
+f(math.nextafter(0,1))
+f(math.nextafter(0,-1))
+f(math.nextafter(2,3)-2)
+while x~=0 do
+ y,x=x,x/2
+end
+f(y)
+
+print""
+print("x down","x","x up","diff down","diff up")
+function f(x)
+ local a=math.nextafter(x,-1/0)
+ local b=math.nextafter(x, 1/0)
+ print(a,x,b,x-a,b-x)
+end
+f(1)
+f(0)
+
+math.max=math.fmax
+math.min=math.fmin
+
+print""
+print("max",math.fmax(1,math.nan,2), math.max(math.nan,1,2))
+print("min",math.fmin(1,math.nan,2), math.min(math.nan,1,2))
+
+print""
+print("log2(32)",math.log2(32))
+print("log(32,2)",math.log(32,2))
+print("exp2(5)","",math.exp2(5))
+print("cbrt(2)","",math.cbrt(2))
+print("cbrt(64)",math.cbrt(64))
+print("6!","",math.gamma(6+1))
+print("hypot(5,12)",math.hypot(5,12))
+print("fma(3,2,1)",math.fma(3,2,1))
+
+print""
+function f(x)
+ print(x,math.floor(x),math.trunc(x),math.round(x),math.ceil(x))
+end
+print("x","floor","trunc","round","ceil")
+f(-1.2)
+f(-1.7)
+f(1.2)
+f(1.7)
+
+print""
+function f(x,y)
+ print(x,y,x%y,math.remainder(x,y),math.fmod(x,y))
+end
+print("x","y","%","rem","fmod")
+for x=0,10 do f(x,7) end
+
+print""
+print(math.version)
diff --git a/logo/COPYING b/logo/COPYING
new file mode 100644
index 0000000..5363584
--- /dev/null
+++ b/logo/COPYING
@@ -0,0 +1,6 @@
+Moony logo based on work by https://lorcblog.blogspot.ch/
+Released under http://creativecommons.org/licenses/by/3.0/
+Downloaded from http://game-icons.net/lorc/originals/wolf-howl.html
+
+OMK logo is original work by Hanspeter Portner
+Released under http://creativecommons.org/licenses/by-sa/4.0/
diff --git a/logo/moony_logo.png b/logo/moony_logo.png
new file mode 100644
index 0000000..413438c
--- /dev/null
+++ b/logo/moony_logo.png
Binary files differ
diff --git a/logo/moony_logo.svg b/logo/moony_logo.svg
new file mode 100644
index 0000000..2ff51ed
--- /dev/null
+++ b/logo/moony_logo.svg
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ id="svg6"
+ version="1.1"
+ style="height: 512px; width: 512px;"
+ viewBox="0 0 512 512"
+ sodipodi:docname="moony_logo.svg"
+ inkscape:version="0.92.1 r"
+ inkscape:export-filename="/home/hp/omk/moony.lv2/logo/moony_logo.png"
+ inkscape:export-xdpi="48"
+ inkscape:export-ydpi="48">
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1342"
+ inkscape:window-height="1043"
+ id="namedview9"
+ showgrid="false"
+ inkscape:zoom="1.0630986"
+ inkscape:cx="240.71682"
+ inkscape:cy="265.74423"
+ inkscape:window-x="571"
+ inkscape:window-y="0"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg6" />
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs10" />
+ <path
+ style="fill:#000000;stroke-width:1;fill-opacity:1"
+ id="path4"
+ d="M 207.54636,75.022388 C 106.21329,102.27861 46.171381,206.5048 73.427602,307.83786 c 5.459414,20.29698 14.015631,38.9435 25.012515,55.5582 26.884663,-27.97106 51.898123,-49.23823 74.767303,-64.69551 -27.36231,12.17135 -47.97839,33.17948 -86.915081,26.80575 19.245261,-28.96855 55.914221,-71.46008 95.298051,-87.03718 16.59403,-22.98544 44.97957,-49.76978 63.62987,-49.06043 18.58995,-28.02791 41.19281,-63.4309 67.97111,-91.762394 11.99647,10.617424 21.40852,25.038844 22.4783,43.546484 -59.88034,33.42023 -17.28746,132.26734 25.25759,50.9961 7.88058,9.07493 13.25078,19.28528 16.1739,31.15638 -18.83905,34.05642 -39.96866,64.75805 -62.56371,92.15544 5.52872,19.07009 6.89449,43.48877 3.04671,71.85733 -1.61843,11.93502 1.96766,30.66729 29.87945,38.21039 C 423.12285,384.6418 463.70227,295.82378 440.38499,209.13491 413.12878,107.80185 308.87804,47.767394 207.54496,75.02362 Z m 37.97695,130.639162 c -13.77469,5.6074 -24.26116,14.27725 -32.55672,25.01081 9.64768,-2.62598 21.08583,-3.20201 31.28917,-3.14095 3.52111,-6.68666 4.19888,-16.99932 1.26813,-21.87088 z" />
+ <path
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:none;fill-opacity:0.50196078;fill-rule:nonzero;stroke:#000000;stroke-width:6;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:26.1;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 256 6.5 A 249.5 249.5 0 0 0 6.5 256 A 249.5 249.5 0 0 0 256 505.5 A 249.5 249.5 0 0 0 505.5 256 A 249.5 249.5 0 0 0 464.16797 118.70703 A 55.5 55.5 0 0 1 445.5 122 A 55.5 55.5 0 0 1 390 66.5 A 55.5 55.5 0 0 1 393.28906 47.876953 A 249.5 249.5 0 0 0 256 6.5 z "
+ id="path4491" />
+</svg>
diff --git a/logo/omk_logo_256x256.png b/logo/omk_logo_256x256.png
new file mode 100644
index 0000000..5f3ff48
--- /dev/null
+++ b/logo/omk_logo_256x256.png
Binary files differ
diff --git a/lpeg-1.0.1/HISTORY b/lpeg-1.0.1/HISTORY
new file mode 100644
index 0000000..0c10edd
--- /dev/null
+++ b/lpeg-1.0.1/HISTORY
@@ -0,0 +1,96 @@
+HISTORY for LPeg 1.0
+
+* Changes from version 0.12 to 1.0
+ ---------------------------------
+ + group "names" can be any Lua value
+ + some bugs fixed
+ + other small improvements
+
+* Changes from version 0.11 to 0.12
+ ---------------------------------
+ + no "unsigned short" limit for pattern sizes
+ + mathtime captures considered nullable
+ + some bugs fixed
+
+* Changes from version 0.10 to 0.11
+ -------------------------------
+ + complete reimplementation of the code generator
+ + new syntax for table captures
+ + new functions in module 're'
+ + other small improvements
+
+* Changes from version 0.9 to 0.10
+ -------------------------------
+ + backtrack stack has configurable size
+ + better error messages
+ + Notation for non-terminals in 're' back to A instead o <A>
+ + experimental look-behind pattern
+ + support for external extensions
+ + works with Lua 5.2
+ + consumes less C stack
+
+ - "and" predicates do not keep captures
+
+* Changes from version 0.8 to 0.9
+ -------------------------------
+ + The accumulator capture was replaced by a fold capture;
+ programs that used the old 'lpeg.Ca' will need small changes.
+ + Some support for character classes from old C locales.
+ + A new named-group capture.
+
+* Changes from version 0.7 to 0.8
+ -------------------------------
+ + New "match-time" capture.
+ + New "argument capture" that allows passing arguments into the pattern.
+ + Better documentation for 're'.
+ + Several small improvements for 're'.
+ + The 're' module has an incompatibility with previous versions:
+ now, any use of a non-terminal must be enclosed in angle brackets
+ (like <B>).
+
+* Changes from version 0.6 to 0.7
+ -------------------------------
+ + Several improvements in module 're':
+ - better documentation;
+ - support for most captures (all but accumulator);
+ - limited repetitions p{n,m}.
+ + Small improvements in efficiency.
+ + Several small bugs corrected (special thanks to Hans Hagen
+ and Taco Hoekwater).
+
+* Changes from version 0.5 to 0.6
+ -------------------------------
+ + Support for non-numeric indices in grammars.
+ + Some bug fixes (thanks to the luatex team).
+ + Some new optimizations; (thanks to Mike Pall).
+ + A new page layout (thanks to Andre Carregal).
+ + Minimal documentation for module 're'.
+
+* Changes from version 0.4 to 0.5
+ -------------------------------
+ + Several optimizations.
+ + lpeg.P now accepts booleans.
+ + Some new examples.
+ + A proper license.
+ + Several small improvements.
+
+* Changes from version 0.3 to 0.4
+ -------------------------------
+ + Static check for loops in repetitions and grammars.
+ + Removed label option in captures.
+ + The implementation of captures uses less memory.
+
+* Changes from version 0.2 to 0.3
+ -------------------------------
+ + User-defined patterns in Lua.
+ + Several new captures.
+
+* Changes from version 0.1 to 0.2
+ -------------------------------
+ + Several small corrections.
+ + Handles embedded zeros like any other character.
+ + Capture "name" can be any Lua value.
+ + Unlimited number of captures.
+ + Match gets an optional initial position.
+
+(end of HISTORY)
diff --git a/lpeg-1.0.1/lpcap.c b/lpeg-1.0.1/lpcap.c
new file mode 100644
index 0000000..c9085de
--- /dev/null
+++ b/lpeg-1.0.1/lpcap.c
@@ -0,0 +1,537 @@
+/*
+** $Id: lpcap.c,v 1.6 2015/06/15 16:09:57 roberto Exp $
+** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
+*/
+
+#include "lua.h"
+#include "lauxlib.h"
+
+#include "lpcap.h"
+#include "lptypes.h"
+
+
+#define captype(cap) ((cap)->kind)
+
+#define isclosecap(cap) (captype(cap) == Cclose)
+
+#define closeaddr(c) ((c)->s + (c)->siz - 1)
+
+#define isfullcap(cap) ((cap)->siz != 0)
+
+#define getfromktable(cs,v) lua_rawgeti((cs)->L, ktableidx((cs)->ptop), v)
+
+#define pushluaval(cs) getfromktable(cs, (cs)->cap->idx)
+
+
+
+/*
+** Put at the cache for Lua values the value indexed by 'v' in ktable
+** of the running pattern (if it is not there yet); returns its index.
+*/
+static int updatecache (CapState *cs, int v) {
+ int idx = cs->ptop + 1; /* stack index of cache for Lua values */
+ if (v != cs->valuecached) { /* not there? */
+ getfromktable(cs, v); /* get value from 'ktable' */
+ lua_replace(cs->L, idx); /* put it at reserved stack position */
+ cs->valuecached = v; /* keep track of what is there */
+ }
+ return idx;
+}
+
+
+static int pushcapture (CapState *cs);
+
+
+/*
+** Goes back in a list of captures looking for an open capture
+** corresponding to a close
+*/
+static Capture *findopen (Capture *cap) {
+ int n = 0; /* number of closes waiting an open */
+ for (;;) {
+ cap--;
+ if (isclosecap(cap)) n++; /* one more open to skip */
+ else if (!isfullcap(cap))
+ if (n-- == 0) return cap;
+ }
+}
+
+
+/*
+** Go to the next capture
+*/
+static void nextcap (CapState *cs) {
+ Capture *cap = cs->cap;
+ if (!isfullcap(cap)) { /* not a single capture? */
+ int n = 0; /* number of opens waiting a close */
+ for (;;) { /* look for corresponding close */
+ cap++;
+ if (isclosecap(cap)) {
+ if (n-- == 0) break;
+ }
+ else if (!isfullcap(cap)) n++;
+ }
+ }
+ cs->cap = cap + 1; /* + 1 to skip last close (or entire single capture) */
+}
+
+
+/*
+** Push on the Lua stack all values generated by nested captures inside
+** the current capture. Returns number of values pushed. 'addextra'
+** makes it push the entire match after all captured values. The
+** entire match is pushed also if there are no other nested values,
+** so the function never returns zero.
+*/
+static int pushnestedvalues (CapState *cs, int addextra) {
+ Capture *co = cs->cap;
+ if (isfullcap(cs->cap++)) { /* no nested captures? */
+ lua_pushlstring(cs->L, co->s, co->siz - 1); /* push whole match */
+ return 1; /* that is it */
+ }
+ else {
+ int n = 0;
+ while (!isclosecap(cs->cap)) /* repeat for all nested patterns */
+ n += pushcapture(cs);
+ if (addextra || n == 0) { /* need extra? */
+ lua_pushlstring(cs->L, co->s, cs->cap->s - co->s); /* push whole match */
+ n++;
+ }
+ cs->cap++; /* skip close entry */
+ return n;
+ }
+}
+
+
+/*
+** Push only the first value generated by nested captures
+*/
+static void pushonenestedvalue (CapState *cs) {
+ int n = pushnestedvalues(cs, 0);
+ if (n > 1)
+ lua_pop(cs->L, n - 1); /* pop extra values */
+}
+
+
+/*
+** Try to find a named group capture with the name given at the top of
+** the stack; goes backward from 'cap'.
+*/
+static Capture *findback (CapState *cs, Capture *cap) {
+ lua_State *L = cs->L;
+ while (cap-- > cs->ocap) { /* repeat until end of list */
+ if (isclosecap(cap))
+ cap = findopen(cap); /* skip nested captures */
+ else if (!isfullcap(cap))
+ continue; /* opening an enclosing capture: skip and get previous */
+ if (captype(cap) == Cgroup) {
+ getfromktable(cs, cap->idx); /* get group name */
+ if (lp_equal(L, -2, -1)) { /* right group? */
+ lua_pop(L, 2); /* remove reference name and group name */
+ return cap;
+ }
+ else lua_pop(L, 1); /* remove group name */
+ }
+ }
+ luaL_error(L, "back reference '%s' not found", lua_tostring(L, -1));
+ return NULL; /* to avoid warnings */
+}
+
+
+/*
+** Back-reference capture. Return number of values pushed.
+*/
+static int backrefcap (CapState *cs) {
+ int n;
+ Capture *curr = cs->cap;
+ pushluaval(cs); /* reference name */
+ cs->cap = findback(cs, curr); /* find corresponding group */
+ n = pushnestedvalues(cs, 0); /* push group's values */
+ cs->cap = curr + 1;
+ return n;
+}
+
+
+/*
+** Table capture: creates a new table and populates it with nested
+** captures.
+*/
+static int tablecap (CapState *cs) {
+ lua_State *L = cs->L;
+ int n = 0;
+ lua_newtable(L);
+ if (isfullcap(cs->cap++))
+ return 1; /* table is empty */
+ while (!isclosecap(cs->cap)) {
+ if (captype(cs->cap) == Cgroup && cs->cap->idx != 0) { /* named group? */
+ pushluaval(cs); /* push group name */
+ pushonenestedvalue(cs);
+ lua_settable(L, -3);
+ }
+ else { /* not a named group */
+ int i;
+ int k = pushcapture(cs);
+ for (i = k; i > 0; i--) /* store all values into table */
+ lua_rawseti(L, -(i + 1), n + i);
+ n += k;
+ }
+ }
+ cs->cap++; /* skip close entry */
+ return 1; /* number of values pushed (only the table) */
+}
+
+
+/*
+** Table-query capture
+*/
+static int querycap (CapState *cs) {
+ int idx = cs->cap->idx;
+ pushonenestedvalue(cs); /* get nested capture */
+ lua_gettable(cs->L, updatecache(cs, idx)); /* query cap. value at table */
+ if (!lua_isnil(cs->L, -1))
+ return 1;
+ else { /* no value */
+ lua_pop(cs->L, 1); /* remove nil */
+ return 0;
+ }
+}
+
+
+/*
+** Fold capture
+*/
+static int foldcap (CapState *cs) {
+ int n;
+ lua_State *L = cs->L;
+ int idx = cs->cap->idx;
+ if (isfullcap(cs->cap++) || /* no nested captures? */
+ isclosecap(cs->cap) || /* no nested captures (large subject)? */
+ (n = pushcapture(cs)) == 0) /* nested captures with no values? */
+ return luaL_error(L, "no initial value for fold capture");
+ if (n > 1)
+ lua_pop(L, n - 1); /* leave only one result for accumulator */
+ while (!isclosecap(cs->cap)) {
+ lua_pushvalue(L, updatecache(cs, idx)); /* get folding function */
+ lua_insert(L, -2); /* put it before accumulator */
+ n = pushcapture(cs); /* get next capture's values */
+ lua_call(L, n + 1, 1); /* call folding function */
+ }
+ cs->cap++; /* skip close entry */
+ return 1; /* only accumulator left on the stack */
+}
+
+
+/*
+** Function capture
+*/
+static int functioncap (CapState *cs) {
+ int n;
+ int top = lua_gettop(cs->L);
+ pushluaval(cs); /* push function */
+ n = pushnestedvalues(cs, 0); /* push nested captures */
+ lua_call(cs->L, n, LUA_MULTRET); /* call function */
+ return lua_gettop(cs->L) - top; /* return function's results */
+}
+
+
+/*
+** Select capture
+*/
+static int numcap (CapState *cs) {
+ int idx = cs->cap->idx; /* value to select */
+ if (idx == 0) { /* no values? */
+ nextcap(cs); /* skip entire capture */
+ return 0; /* no value produced */
+ }
+ else {
+ int n = pushnestedvalues(cs, 0);
+ if (n < idx) /* invalid index? */
+ return luaL_error(cs->L, "no capture '%d'", idx);
+ else {
+ lua_pushvalue(cs->L, -(n - idx + 1)); /* get selected capture */
+ lua_replace(cs->L, -(n + 1)); /* put it in place of 1st capture */
+ lua_pop(cs->L, n - 1); /* remove other captures */
+ return 1;
+ }
+ }
+}
+
+
+/*
+** Return the stack index of the first runtime capture in the given
+** list of captures (or zero if no runtime captures)
+*/
+int finddyncap (Capture *cap, Capture *last) {
+ for (; cap < last; cap++) {
+ if (cap->kind == Cruntime)
+ return cap->idx; /* stack position of first capture */
+ }
+ return 0; /* no dynamic captures in this segment */
+}
+
+
+/*
+** Calls a runtime capture. Returns number of captures removed by
+** the call, including the initial Cgroup. (Captures to be added are
+** on the Lua stack.)
+*/
+int runtimecap (CapState *cs, Capture *close, const char *s, int *rem) {
+ int n, id;
+ lua_State *L = cs->L;
+ int otop = lua_gettop(L);
+ Capture *open = findopen(close);
+ assert(captype(open) == Cgroup);
+ id = finddyncap(open, close); /* get first dynamic capture argument */
+ close->kind = Cclose; /* closes the group */
+ close->s = s;
+ cs->cap = open; cs->valuecached = 0; /* prepare capture state */
+ luaL_checkstack(L, 4, "too many runtime captures");
+ pushluaval(cs); /* push function to be called */
+ lua_pushvalue(L, SUBJIDX); /* push original subject */
+ lua_pushinteger(L, s - cs->s + 1); /* push current position */
+ n = pushnestedvalues(cs, 0); /* push nested captures */
+ lua_call(L, n + 2, LUA_MULTRET); /* call dynamic function */
+ if (id > 0) { /* are there old dynamic captures to be removed? */
+ int i;
+ for (i = id; i <= otop; i++)
+ lua_remove(L, id); /* remove old dynamic captures */
+ *rem = otop - id + 1; /* total number of dynamic captures removed */
+ }
+ else
+ *rem = 0; /* no dynamic captures removed */
+ return close - open; /* number of captures of all kinds removed */
+}
+
+
+/*
+** Auxiliary structure for substitution and string captures: keep
+** information about nested captures for future use, avoiding to push
+** string results into Lua
+*/
+typedef struct StrAux {
+ int isstring; /* whether capture is a string */
+ union {
+ Capture *cp; /* if not a string, respective capture */
+ struct { /* if it is a string... */
+ const char *s; /* ... starts here */
+ const char *e; /* ... ends here */
+ } s;
+ } u;
+} StrAux;
+
+#define MAXSTRCAPS 10
+
+/*
+** Collect values from current capture into array 'cps'. Current
+** capture must be Cstring (first call) or Csimple (recursive calls).
+** (In first call, fills %0 with whole match for Cstring.)
+** Returns number of elements in the array that were filled.
+*/
+static int getstrcaps (CapState *cs, StrAux *cps, int n) {
+ int k = n++;
+ cps[k].isstring = 1; /* get string value */
+ cps[k].u.s.s = cs->cap->s; /* starts here */
+ if (!isfullcap(cs->cap++)) { /* nested captures? */
+ while (!isclosecap(cs->cap)) { /* traverse them */
+ if (n >= MAXSTRCAPS) /* too many captures? */
+ nextcap(cs); /* skip extra captures (will not need them) */
+ else if (captype(cs->cap) == Csimple) /* string? */
+ n = getstrcaps(cs, cps, n); /* put info. into array */
+ else {
+ cps[n].isstring = 0; /* not a string */
+ cps[n].u.cp = cs->cap; /* keep original capture */
+ nextcap(cs);
+ n++;
+ }
+ }
+ cs->cap++; /* skip close */
+ }
+ cps[k].u.s.e = closeaddr(cs->cap - 1); /* ends here */
+ return n;
+}
+
+
+/*
+** add next capture value (which should be a string) to buffer 'b'
+*/
+static int addonestring (luaL_Buffer *b, CapState *cs, const char *what);
+
+
+/*
+** String capture: add result to buffer 'b' (instead of pushing
+** it into the stack)
+*/
+static void stringcap (luaL_Buffer *b, CapState *cs) {
+ StrAux cps[MAXSTRCAPS];
+ int n;
+ size_t len, i;
+ const char *fmt; /* format string */
+ fmt = lua_tolstring(cs->L, updatecache(cs, cs->cap->idx), &len);
+ n = getstrcaps(cs, cps, 0) - 1; /* collect nested captures */
+ for (i = 0; i < len; i++) { /* traverse them */
+ if (fmt[i] != '%') /* not an escape? */
+ luaL_addchar(b, fmt[i]); /* add it to buffer */
+ else if (fmt[++i] < '0' || fmt[i] > '9') /* not followed by a digit? */
+ luaL_addchar(b, fmt[i]); /* add to buffer */
+ else {
+ int l = fmt[i] - '0'; /* capture index */
+ if (l > n)
+ luaL_error(cs->L, "invalid capture index (%d)", l);
+ else if (cps[l].isstring)
+ luaL_addlstring(b, cps[l].u.s.s, cps[l].u.s.e - cps[l].u.s.s);
+ else {
+ Capture *curr = cs->cap;
+ cs->cap = cps[l].u.cp; /* go back to evaluate that nested capture */
+ if (!addonestring(b, cs, "capture"))
+ luaL_error(cs->L, "no values in capture index %d", l);
+ cs->cap = curr; /* continue from where it stopped */
+ }
+ }
+ }
+}
+
+
+/*
+** Substitution capture: add result to buffer 'b'
+*/
+static void substcap (luaL_Buffer *b, CapState *cs) {
+ const char *curr = cs->cap->s;
+ if (isfullcap(cs->cap)) /* no nested captures? */
+ luaL_addlstring(b, curr, cs->cap->siz - 1); /* keep original text */
+ else {
+ cs->cap++; /* skip open entry */
+ while (!isclosecap(cs->cap)) { /* traverse nested captures */
+ const char *next = cs->cap->s;
+ luaL_addlstring(b, curr, next - curr); /* add text up to capture */
+ if (addonestring(b, cs, "replacement"))
+ curr = closeaddr(cs->cap - 1); /* continue after match */
+ else /* no capture value */
+ curr = next; /* keep original text in final result */
+ }
+ luaL_addlstring(b, curr, cs->cap->s - curr); /* add last piece of text */
+ }
+ cs->cap++; /* go to next capture */
+}
+
+
+/*
+** Evaluates a capture and adds its first value to buffer 'b'; returns
+** whether there was a value
+*/
+static int addonestring (luaL_Buffer *b, CapState *cs, const char *what) {
+ switch (captype(cs->cap)) {
+ case Cstring:
+ stringcap(b, cs); /* add capture directly to buffer */
+ return 1;
+ case Csubst:
+ substcap(b, cs); /* add capture directly to buffer */
+ return 1;
+ default: {
+ lua_State *L = cs->L;
+ int n = pushcapture(cs);
+ if (n > 0) {
+ if (n > 1) lua_pop(L, n - 1); /* only one result */
+ if (!lua_isstring(L, -1))
+ luaL_error(L, "invalid %s value (a %s)", what, luaL_typename(L, -1));
+ luaL_addvalue(b);
+ }
+ return n;
+ }
+ }
+}
+
+
+/*
+** Push all values of the current capture into the stack; returns
+** number of values pushed
+*/
+static int pushcapture (CapState *cs) {
+ lua_State *L = cs->L;
+ luaL_checkstack(L, 4, "too many captures");
+ switch (captype(cs->cap)) {
+ case Cposition: {
+ lua_pushinteger(L, cs->cap->s - cs->s + 1);
+ cs->cap++;
+ return 1;
+ }
+ case Cconst: {
+ pushluaval(cs);
+ cs->cap++;
+ return 1;
+ }
+ case Carg: {
+ int arg = (cs->cap++)->idx;
+ if (arg + FIXEDARGS > cs->ptop)
+ return luaL_error(L, "reference to absent extra argument #%d", arg);
+ lua_pushvalue(L, arg + FIXEDARGS);
+ return 1;
+ }
+ case Csimple: {
+ int k = pushnestedvalues(cs, 1);
+ lua_insert(L, -k); /* make whole match be first result */
+ return k;
+ }
+ case Cruntime: {
+ lua_pushvalue(L, (cs->cap++)->idx); /* value is in the stack */
+ return 1;
+ }
+ case Cstring: {
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ stringcap(&b, cs);
+ luaL_pushresult(&b);
+ return 1;
+ }
+ case Csubst: {
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ substcap(&b, cs);
+ luaL_pushresult(&b);
+ return 1;
+ }
+ case Cgroup: {
+ if (cs->cap->idx == 0) /* anonymous group? */
+ return pushnestedvalues(cs, 0); /* add all nested values */
+ else { /* named group: add no values */
+ nextcap(cs); /* skip capture */
+ return 0;
+ }
+ }
+ case Cbackref: return backrefcap(cs);
+ case Ctable: return tablecap(cs);
+ case Cfunction: return functioncap(cs);
+ case Cnum: return numcap(cs);
+ case Cquery: return querycap(cs);
+ case Cfold: return foldcap(cs);
+ default: assert(0); return 0;
+ }
+}
+
+
+/*
+** Prepare a CapState structure and traverse the entire list of
+** captures in the stack pushing its results. 's' is the subject
+** string, 'r' is the final position of the match, and 'ptop'
+** the index in the stack where some useful values were pushed.
+** Returns the number of results pushed. (If the list produces no
+** results, push the final position of the match.)
+*/
+int getcaptures (lua_State *L, const char *s, const char *r, int ptop) {
+ Capture *capture = (Capture *)lua_touserdata(L, caplistidx(ptop));
+ int n = 0;
+ if (!isclosecap(capture)) { /* is there any capture? */
+ CapState cs;
+ cs.ocap = cs.cap = capture; cs.L = L;
+ cs.s = s; cs.valuecached = 0; cs.ptop = ptop;
+ do { /* collect their values */
+ n += pushcapture(&cs);
+ } while (!isclosecap(cs.cap));
+ }
+ if (n == 0) { /* no capture values? */
+ lua_pushinteger(L, r - s + 1); /* return only end position */
+ n = 1;
+ }
+ return n;
+}
+
+
diff --git a/lpeg-1.0.1/lpcap.h b/lpeg-1.0.1/lpcap.h
new file mode 100644
index 0000000..6133df2
--- /dev/null
+++ b/lpeg-1.0.1/lpcap.h
@@ -0,0 +1,56 @@
+/*
+** $Id: lpcap.h,v 1.3 2016/09/13 17:45:58 roberto Exp $
+*/
+
+#if !defined(lpcap_h)
+#define lpcap_h
+
+
+#include "lptypes.h"
+
+
+/* kinds of captures */
+typedef enum CapKind {
+ Cclose, /* not used in trees */
+ Cposition,
+ Cconst, /* ktable[key] is Lua constant */
+ Cbackref, /* ktable[key] is "name" of group to get capture */
+ Carg, /* 'key' is arg's number */
+ Csimple, /* next node is pattern */
+ Ctable, /* next node is pattern */
+ Cfunction, /* ktable[key] is function; next node is pattern */
+ Cquery, /* ktable[key] is table; next node is pattern */
+ Cstring, /* ktable[key] is string; next node is pattern */
+ Cnum, /* numbered capture; 'key' is number of value to return */
+ Csubst, /* substitution capture; next node is pattern */
+ Cfold, /* ktable[key] is function; next node is pattern */
+ Cruntime, /* not used in trees (is uses another type for tree) */
+ Cgroup /* ktable[key] is group's "name" */
+} CapKind;
+
+
+typedef struct Capture {
+ const char *s; /* subject position */
+ unsigned short idx; /* extra info (group name, arg index, etc.) */
+ byte kind; /* kind of capture */
+ byte siz; /* size of full capture + 1 (0 = not a full capture) */
+} Capture;
+
+
+typedef struct CapState {
+ Capture *cap; /* current capture */
+ Capture *ocap; /* (original) capture list */
+ lua_State *L;
+ int ptop; /* index of last argument to 'match' */
+ const char *s; /* original string */
+ int valuecached; /* value stored in cache slot */
+} CapState;
+
+
+int runtimecap (CapState *cs, Capture *close, const char *s, int *rem);
+int getcaptures (lua_State *L, const char *s, const char *r, int ptop);
+int finddyncap (Capture *cap, Capture *last);
+
+#endif
+
+
diff --git a/lpeg-1.0.1/lpcode.c b/lpeg-1.0.1/lpcode.c
new file mode 100644
index 0000000..2722d71
--- /dev/null
+++ b/lpeg-1.0.1/lpcode.c
@@ -0,0 +1,1014 @@
+/*
+** $Id: lpcode.c,v 1.24 2016/09/15 17:46:13 roberto Exp $
+** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
+*/
+
+#include <limits.h>
+
+
+#include "lua.h"
+#include "lauxlib.h"
+
+#include "lptypes.h"
+#include "lpcode.h"
+
+
+/* signals a "no-instruction */
+#define NOINST -1
+
+
+
+static const Charset fullset_ =
+ {{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};
+
+static const Charset *fullset = &fullset_;
+
+/*
+** {======================================================
+** Analysis and some optimizations
+** =======================================================
+*/
+
+/*
+** Check whether a charset is empty (returns IFail), singleton (IChar),
+** full (IAny), or none of those (ISet). When singleton, '*c' returns
+** which character it is. (When generic set, the set was the input,
+** so there is no need to return it.)
+*/
+static Opcode charsettype (const byte *cs, int *c) {
+ int count = 0; /* number of characters in the set */
+ int i;
+ int candidate = -1; /* candidate position for the singleton char */
+ for (i = 0; i < CHARSETSIZE; i++) { /* for each byte */
+ int b = cs[i];
+ if (b == 0) { /* is byte empty? */
+ if (count > 1) /* was set neither empty nor singleton? */
+ return ISet; /* neither full nor empty nor singleton */
+ /* else set is still empty or singleton */
+ }
+ else if (b == 0xFF) { /* is byte full? */
+ if (count < (i * BITSPERCHAR)) /* was set not full? */
+ return ISet; /* neither full nor empty nor singleton */
+ else count += BITSPERCHAR; /* set is still full */
+ }
+ else if ((b & (b - 1)) == 0) { /* has byte only one bit? */
+ if (count > 0) /* was set not empty? */
+ return ISet; /* neither full nor empty nor singleton */
+ else { /* set has only one char till now; track it */
+ count++;
+ candidate = i;
+ }
+ }
+ else return ISet; /* byte is neither empty, full, nor singleton */
+ }
+ switch (count) {
+ case 0: return IFail; /* empty set */
+ case 1: { /* singleton; find character bit inside byte */
+ int b = cs[candidate];
+ *c = candidate * BITSPERCHAR;
+ if ((b & 0xF0) != 0) { *c += 4; b >>= 4; }
+ if ((b & 0x0C) != 0) { *c += 2; b >>= 2; }
+ if ((b & 0x02) != 0) { *c += 1; }
+ return IChar;
+ }
+ default: {
+ assert(count == CHARSETSIZE * BITSPERCHAR); /* full set */
+ return IAny;
+ }
+ }
+}
+
+
+/*
+** A few basic operations on Charsets
+*/
+static void cs_complement (Charset *cs) {
+ loopset(i, cs->cs[i] = ~cs->cs[i]);
+}
+
+static int cs_equal (const byte *cs1, const byte *cs2) {
+ loopset(i, if (cs1[i] != cs2[i]) return 0);
+ return 1;
+}
+
+static int cs_disjoint (const Charset *cs1, const Charset *cs2) {
+ loopset(i, if ((cs1->cs[i] & cs2->cs[i]) != 0) return 0;)
+ return 1;
+}
+
+
+/*
+** If 'tree' is a 'char' pattern (TSet, TChar, TAny), convert it into a
+** charset and return 1; else return 0.
+*/
+int tocharset (TTree *tree, Charset *cs) {
+ switch (tree->tag) {
+ case TSet: { /* copy set */
+ loopset(i, cs->cs[i] = treebuffer(tree)[i]);
+ return 1;
+ }
+ case TChar: { /* only one char */
+ assert(0 <= tree->u.n && tree->u.n <= UCHAR_MAX);
+ loopset(i, cs->cs[i] = 0); /* erase all chars */
+ setchar(cs->cs, tree->u.n); /* add that one */
+ return 1;
+ }
+ case TAny: {
+ loopset(i, cs->cs[i] = 0xFF); /* add all characters to the set */
+ return 1;
+ }
+ default: return 0;
+ }
+}
+
+
+/*
+** Visit a TCall node taking care to stop recursion. If node not yet
+** visited, return 'f(sib2(tree))', otherwise return 'def' (default
+** value)
+*/
+static int callrecursive (TTree *tree, int f (TTree *t), int def) {
+ int key = tree->key;
+ assert(tree->tag == TCall);
+ assert(sib2(tree)->tag == TRule);
+ if (key == 0) /* node already visited? */
+ return def; /* return default value */
+ else { /* first visit */
+ int result;
+ tree->key = 0; /* mark call as already visited */
+ result = f(sib2(tree)); /* go to called rule */
+ tree->key = key; /* restore tree */
+ return result;
+ }
+}
+
+
+/*
+** Check whether a pattern tree has captures
+*/
+int hascaptures (TTree *tree) {
+ tailcall:
+ switch (tree->tag) {
+ case TCapture: case TRunTime:
+ return 1;
+ case TCall:
+ return callrecursive(tree, hascaptures, 0);
+ case TRule: /* do not follow siblings */
+ tree = sib1(tree); goto tailcall;
+ case TOpenCall: assert(0);
+ default: {
+ switch (numsiblings[tree->tag]) {
+ case 1: /* return hascaptures(sib1(tree)); */
+ tree = sib1(tree); goto tailcall;
+ case 2:
+ if (hascaptures(sib1(tree)))
+ return 1;
+ /* else return hascaptures(sib2(tree)); */
+ tree = sib2(tree); goto tailcall;
+ default: assert(numsiblings[tree->tag] == 0); return 0;
+ }
+ }
+ }
+}
+
+
+/*
+** Checks how a pattern behaves regarding the empty string,
+** in one of two different ways:
+** A pattern is *nullable* if it can match without consuming any character;
+** A pattern is *nofail* if it never fails for any string
+** (including the empty string).
+** The difference is only for predicates and run-time captures;
+** for other patterns, the two properties are equivalent.
+** (With predicates, &'a' is nullable but not nofail. Of course,
+** nofail => nullable.)
+** These functions are all convervative in the following way:
+** p is nullable => nullable(p)
+** nofail(p) => p cannot fail
+** The function assumes that TOpenCall is not nullable;
+** this will be checked again when the grammar is fixed.
+** Run-time captures can do whatever they want, so the result
+** is conservative.
+*/
+int checkaux (TTree *tree, int pred) {
+ tailcall:
+ switch (tree->tag) {
+ case TChar: case TSet: case TAny:
+ case TFalse: case TOpenCall:
+ return 0; /* not nullable */
+ case TRep: case TTrue:
+ return 1; /* no fail */
+ case TNot: case TBehind: /* can match empty, but can fail */
+ if (pred == PEnofail) return 0;
+ else return 1; /* PEnullable */
+ case TAnd: /* can match empty; fail iff body does */
+ if (pred == PEnullable) return 1;
+ /* else return checkaux(sib1(tree), pred); */
+ tree = sib1(tree); goto tailcall;
+ case TRunTime: /* can fail; match empty iff body does */
+ if (pred == PEnofail) return 0;
+ /* else return checkaux(sib1(tree), pred); */
+ tree = sib1(tree); goto tailcall;
+ case TSeq:
+ if (!checkaux(sib1(tree), pred)) return 0;
+ /* else return checkaux(sib2(tree), pred); */
+ tree = sib2(tree); goto tailcall;
+ case TChoice:
+ if (checkaux(sib2(tree), pred)) return 1;
+ /* else return checkaux(sib1(tree), pred); */
+ tree = sib1(tree); goto tailcall;
+ case TCapture: case TGrammar: case TRule:
+ /* return checkaux(sib1(tree), pred); */
+ tree = sib1(tree); goto tailcall;
+ case TCall: /* return checkaux(sib2(tree), pred); */
+ tree = sib2(tree); goto tailcall;
+ default: assert(0); return 0;
+ }
+}
+
+
+/*
+** number of characters to match a pattern (or -1 if variable)
+*/
+int fixedlen (TTree *tree) {
+ int len = 0; /* to accumulate in tail calls */
+ tailcall:
+ switch (tree->tag) {
+ case TChar: case TSet: case TAny:
+ return len + 1;
+ case TFalse: case TTrue: case TNot: case TAnd: case TBehind:
+ return len;
+ case TRep: case TRunTime: case TOpenCall:
+ return -1;
+ case TCapture: case TRule: case TGrammar:
+ /* return fixedlen(sib1(tree)); */
+ tree = sib1(tree); goto tailcall;
+ case TCall: {
+ int n1 = callrecursive(tree, fixedlen, -1);
+ if (n1 < 0)
+ return -1;
+ else
+ return len + n1;
+ }
+ case TSeq: {
+ int n1 = fixedlen(sib1(tree));
+ if (n1 < 0)
+ return -1;
+ /* else return fixedlen(sib2(tree)) + len; */
+ len += n1; tree = sib2(tree); goto tailcall;
+ }
+ case TChoice: {
+ int n1 = fixedlen(sib1(tree));
+ int n2 = fixedlen(sib2(tree));
+ if (n1 != n2 || n1 < 0)
+ return -1;
+ else
+ return len + n1;
+ }
+ default: assert(0); return 0;
+ };
+}
+
+
+/*
+** Computes the 'first set' of a pattern.
+** The result is a conservative aproximation:
+** match p ax -> x (for some x) ==> a belongs to first(p)
+** or
+** a not in first(p) ==> match p ax -> fail (for all x)
+**
+** The set 'follow' is the first set of what follows the
+** pattern (full set if nothing follows it).
+**
+** The function returns 0 when this resulting set can be used for
+** test instructions that avoid the pattern altogether.
+** A non-zero return can happen for two reasons:
+** 1) match p '' -> '' ==> return has bit 1 set
+** (tests cannot be used because they would always fail for an empty input);
+** 2) there is a match-time capture ==> return has bit 2 set
+** (optimizations should not bypass match-time captures).
+*/
+static int getfirst (TTree *tree, const Charset *follow, Charset *firstset) {
+ tailcall:
+ switch (tree->tag) {
+ case TChar: case TSet: case TAny: {
+ tocharset(tree, firstset);
+ return 0;
+ }
+ case TTrue: {
+ loopset(i, firstset->cs[i] = follow->cs[i]);
+ return 1; /* accepts the empty string */
+ }
+ case TFalse: {
+ loopset(i, firstset->cs[i] = 0);
+ return 0;
+ }
+ case TChoice: {
+ Charset csaux;
+ int e1 = getfirst(sib1(tree), follow, firstset);
+ int e2 = getfirst(sib2(tree), follow, &csaux);
+ loopset(i, firstset->cs[i] |= csaux.cs[i]);
+ return e1 | e2;
+ }
+ case TSeq: {
+ if (!nullable(sib1(tree))) {
+ /* when p1 is not nullable, p2 has nothing to contribute;
+ return getfirst(sib1(tree), fullset, firstset); */
+ tree = sib1(tree); follow = fullset; goto tailcall;
+ }
+ else { /* FIRST(p1 p2, fl) = FIRST(p1, FIRST(p2, fl)) */
+ Charset csaux;
+ int e2 = getfirst(sib2(tree), follow, &csaux);
+ int e1 = getfirst(sib1(tree), &csaux, firstset);
+ if (e1 == 0) return 0; /* 'e1' ensures that first can be used */
+ else if ((e1 | e2) & 2) /* one of the children has a matchtime? */
+ return 2; /* pattern has a matchtime capture */
+ else return e2; /* else depends on 'e2' */
+ }
+ }
+ case TRep: {
+ getfirst(sib1(tree), follow, firstset);
+ loopset(i, firstset->cs[i] |= follow->cs[i]);
+ return 1; /* accept the empty string */
+ }
+ case TCapture: case TGrammar: case TRule: {
+ /* return getfirst(sib1(tree), follow, firstset); */
+ tree = sib1(tree); goto tailcall;
+ }
+ case TRunTime: { /* function invalidates any follow info. */
+ int e = getfirst(sib1(tree), fullset, firstset);
+ if (e) return 2; /* function is not "protected"? */
+ else return 0; /* pattern inside capture ensures first can be used */
+ }
+ case TCall: {
+ /* return getfirst(sib2(tree), follow, firstset); */
+ tree = sib2(tree); goto tailcall;
+ }
+ case TAnd: {
+ int e = getfirst(sib1(tree), follow, firstset);
+ loopset(i, firstset->cs[i] &= follow->cs[i]);
+ return e;
+ }
+ case TNot: {
+ if (tocharset(sib1(tree), firstset)) {
+ cs_complement(firstset);
+ return 1;
+ }
+ /* else go through */
+ }
+ case TBehind: { /* instruction gives no new information */
+ /* call 'getfirst' only to check for math-time captures */
+ int e = getfirst(sib1(tree), follow, firstset);
+ loopset(i, firstset->cs[i] = follow->cs[i]); /* uses follow */
+ return e | 1; /* always can accept the empty string */
+ }
+ default: assert(0); return 0;
+ }
+}
+
+
+/*
+** If 'headfail(tree)' true, then 'tree' can fail only depending on the
+** next character of the subject.
+*/
+static int headfail (TTree *tree) {
+ tailcall:
+ switch (tree->tag) {
+ case TChar: case TSet: case TAny: case TFalse:
+ return 1;
+ case TTrue: case TRep: case TRunTime: case TNot:
+ case TBehind:
+ return 0;
+ case TCapture: case TGrammar: case TRule: case TAnd:
+ tree = sib1(tree); goto tailcall; /* return headfail(sib1(tree)); */
+ case TCall:
+ tree = sib2(tree); goto tailcall; /* return headfail(sib2(tree)); */
+ case TSeq:
+ if (!nofail(sib2(tree))) return 0;
+ /* else return headfail(sib1(tree)); */
+ tree = sib1(tree); goto tailcall;
+ case TChoice:
+ if (!headfail(sib1(tree))) return 0;
+ /* else return headfail(sib2(tree)); */
+ tree = sib2(tree); goto tailcall;
+ default: assert(0); return 0;
+ }
+}
+
+
+/*
+** Check whether the code generation for the given tree can benefit
+** from a follow set (to avoid computing the follow set when it is
+** not needed)
+*/
+static int needfollow (TTree *tree) {
+ tailcall:
+ switch (tree->tag) {
+ case TChar: case TSet: case TAny:
+ case TFalse: case TTrue: case TAnd: case TNot:
+ case TRunTime: case TGrammar: case TCall: case TBehind:
+ return 0;
+ case TChoice: case TRep:
+ return 1;
+ case TCapture:
+ tree = sib1(tree); goto tailcall;
+ case TSeq:
+ tree = sib2(tree); goto tailcall;
+ default: assert(0); return 0;
+ }
+}
+
+/* }====================================================== */
+
+
+
+/*
+** {======================================================
+** Code generation
+** =======================================================
+*/
+
+
+/*
+** size of an instruction
+*/
+int sizei (const Instruction *i) {
+ switch((Opcode)i->i.code) {
+ case ISet: case ISpan: return CHARSETINSTSIZE;
+ case ITestSet: return CHARSETINSTSIZE + 1;
+ case ITestChar: case ITestAny: case IChoice: case IJmp: case ICall:
+ case IOpenCall: case ICommit: case IPartialCommit: case IBackCommit:
+ return 2;
+ default: return 1;
+ }
+}
+
+
+/*
+** state for the compiler
+*/
+typedef struct CompileState {
+ Pattern *p; /* pattern being compiled */
+ int ncode; /* next position in p->code to be filled */
+ lua_State *L;
+} CompileState;
+
+
+/*
+** code generation is recursive; 'opt' indicates that the code is being
+** generated as the last thing inside an optional pattern (so, if that
+** code is optional too, it can reuse the 'IChoice' already in place for
+** the outer pattern). 'tt' points to a previous test protecting this
+** code (or NOINST). 'fl' is the follow set of the pattern.
+*/
+static void codegen (CompileState *compst, TTree *tree, int opt, int tt,
+ const Charset *fl);
+
+
+void realloccode (lua_State *L, Pattern *p, int nsize) {
+ void *ud;
+ lua_Alloc f = lua_getallocf(L, &ud);
+ void *newblock = f(ud, p->code, p->codesize * sizeof(Instruction),
+ nsize * sizeof(Instruction));
+ if (newblock == NULL && nsize > 0)
+ luaL_error(L, "not enough memory");
+ p->code = (Instruction *)newblock;
+ p->codesize = nsize;
+}
+
+
+static int nextinstruction (CompileState *compst) {
+ int size = compst->p->codesize;
+ if (compst->ncode >= size)
+ realloccode(compst->L, compst->p, size * 2);
+ return compst->ncode++;
+}
+
+
+#define getinstr(cs,i) ((cs)->p->code[i])
+
+
+static int addinstruction (CompileState *compst, Opcode op, int aux) {
+ int i = nextinstruction(compst);
+ getinstr(compst, i).i.code = op;
+ getinstr(compst, i).i.aux = aux;
+ return i;
+}
+
+
+/*
+** Add an instruction followed by space for an offset (to be set later)
+*/
+static int addoffsetinst (CompileState *compst, Opcode op) {
+ int i = addinstruction(compst, op, 0); /* instruction */
+ addinstruction(compst, (Opcode)0, 0); /* open space for offset */
+ assert(op == ITestSet || sizei(&getinstr(compst, i)) == 2);
+ return i;
+}
+
+
+/*
+** Set the offset of an instruction
+*/
+static void setoffset (CompileState *compst, int instruction, int offset) {
+ getinstr(compst, instruction + 1).offset = offset;
+}
+
+
+/*
+** Add a capture instruction:
+** 'op' is the capture instruction; 'cap' the capture kind;
+** 'key' the key into ktable; 'aux' is the optional capture offset
+**
+*/
+static int addinstcap (CompileState *compst, Opcode op, int cap, int key,
+ int aux) {
+ int i = addinstruction(compst, op, joinkindoff(cap, aux));
+ getinstr(compst, i).i.key = key;
+ return i;
+}
+
+
+#define gethere(compst) ((compst)->ncode)
+
+#define target(code,i) ((i) + code[i + 1].offset)
+
+
+/*
+** Patch 'instruction' to jump to 'target'
+*/
+static void jumptothere (CompileState *compst, int instruction, int target) {
+ if (instruction >= 0)
+ setoffset(compst, instruction, target - instruction);
+}
+
+
+/*
+** Patch 'instruction' to jump to current position
+*/
+static void jumptohere (CompileState *compst, int instruction) {
+ jumptothere(compst, instruction, gethere(compst));
+}
+
+
+/*
+** Code an IChar instruction, or IAny if there is an equivalent
+** test dominating it
+*/
+static void codechar (CompileState *compst, int c, int tt) {
+ if (tt >= 0 && getinstr(compst, tt).i.code == ITestChar &&
+ getinstr(compst, tt).i.aux == c)
+ addinstruction(compst, IAny, 0);
+ else
+ addinstruction(compst, IChar, c);
+}
+
+
+/*
+** Add a charset posfix to an instruction
+*/
+static void addcharset (CompileState *compst, const byte *cs) {
+ int p = gethere(compst);
+ int i;
+ for (i = 0; i < (int)CHARSETINSTSIZE - 1; i++)
+ nextinstruction(compst); /* space for buffer */
+ /* fill buffer with charset */
+ loopset(j, getinstr(compst, p).buff[j] = cs[j]);
+}
+
+
+/*
+** code a char set, optimizing unit sets for IChar, "complete"
+** sets for IAny, and empty sets for IFail; also use an IAny
+** when instruction is dominated by an equivalent test.
+*/
+static void codecharset (CompileState *compst, const byte *cs, int tt) {
+ int c = 0; /* (=) to avoid warnings */
+ Opcode op = charsettype(cs, &c);
+ switch (op) {
+ case IChar: codechar(compst, c, tt); break;
+ case ISet: { /* non-trivial set? */
+ if (tt >= 0 && getinstr(compst, tt).i.code == ITestSet &&
+ cs_equal(cs, getinstr(compst, tt + 2).buff))
+ addinstruction(compst, IAny, 0);
+ else {
+ addinstruction(compst, ISet, 0);
+ addcharset(compst, cs);
+ }
+ break;
+ }
+ default: addinstruction(compst, op, c); break;
+ }
+}
+
+
+/*
+** code a test set, optimizing unit sets for ITestChar, "complete"
+** sets for ITestAny, and empty sets for IJmp (always fails).
+** 'e' is true iff test should accept the empty string. (Test
+** instructions in the current VM never accept the empty string.)
+*/
+static int codetestset (CompileState *compst, Charset *cs, int e) {
+ if (e) return NOINST; /* no test */
+ else {
+ int c = 0;
+ Opcode op = charsettype(cs->cs, &c);
+ switch (op) {
+ case IFail: return addoffsetinst(compst, IJmp); /* always jump */
+ case IAny: return addoffsetinst(compst, ITestAny);
+ case IChar: {
+ int i = addoffsetinst(compst, ITestChar);
+ getinstr(compst, i).i.aux = c;
+ return i;
+ }
+ case ISet: {
+ int i = addoffsetinst(compst, ITestSet);
+ addcharset(compst, cs->cs);
+ return i;
+ }
+ default: assert(0); return 0;
+ }
+ }
+}
+
+
+/*
+** Find the final destination of a sequence of jumps
+*/
+static int finaltarget (Instruction *code, int i) {
+ while (code[i].i.code == IJmp)
+ i = target(code, i);
+ return i;
+}
+
+
+/*
+** final label (after traversing any jumps)
+*/
+static int finallabel (Instruction *code, int i) {
+ return finaltarget(code, target(code, i));
+}
+
+
+/*
+** <behind(p)> == behind n; <p> (where n = fixedlen(p))
+*/
+static void codebehind (CompileState *compst, TTree *tree) {
+ if (tree->u.n > 0)
+ addinstruction(compst, IBehind, tree->u.n);
+ codegen(compst, sib1(tree), 0, NOINST, fullset);
+}
+
+
+/*
+** Choice; optimizations:
+** - when p1 is headfail or
+** when first(p1) and first(p2) are disjoint, than
+** a character not in first(p1) cannot go to p1, and a character
+** in first(p1) cannot go to p2 (at it is not in first(p2)).
+** (The optimization is not valid if p1 accepts the empty string,
+** as then there is no character at all...)
+** - when p2 is empty and opt is true; a IPartialCommit can reuse
+** the Choice already active in the stack.
+*/
+static void codechoice (CompileState *compst, TTree *p1, TTree *p2, int opt,
+ const Charset *fl) {
+ int emptyp2 = (p2->tag == TTrue);
+ Charset cs1, cs2;
+ int e1 = getfirst(p1, fullset, &cs1);
+ if (headfail(p1) ||
+ (!e1 && (getfirst(p2, fl, &cs2), cs_disjoint(&cs1, &cs2)))) {
+ /* <p1 / p2> == test (fail(p1)) -> L1 ; p1 ; jmp L2; L1: p2; L2: */
+ int test = codetestset(compst, &cs1, 0);
+ int jmp = NOINST;
+ codegen(compst, p1, 0, test, fl);
+ if (!emptyp2)
+ jmp = addoffsetinst(compst, IJmp);
+ jumptohere(compst, test);
+ codegen(compst, p2, opt, NOINST, fl);
+ jumptohere(compst, jmp);
+ }
+ else if (opt && emptyp2) {
+ /* p1? == IPartialCommit; p1 */
+ jumptohere(compst, addoffsetinst(compst, IPartialCommit));
+ codegen(compst, p1, 1, NOINST, fullset);
+ }
+ else {
+ /* <p1 / p2> ==
+ test(first(p1)) -> L1; choice L1; <p1>; commit L2; L1: <p2>; L2: */
+ int pcommit;
+ int test = codetestset(compst, &cs1, e1);
+ int pchoice = addoffsetinst(compst, IChoice);
+ codegen(compst, p1, emptyp2, test, fullset);
+ pcommit = addoffsetinst(compst, ICommit);
+ jumptohere(compst, pchoice);
+ jumptohere(compst, test);
+ codegen(compst, p2, opt, NOINST, fl);
+ jumptohere(compst, pcommit);
+ }
+}
+
+
+/*
+** And predicate
+** optimization: fixedlen(p) = n ==> <&p> == <p>; behind n
+** (valid only when 'p' has no captures)
+*/
+static void codeand (CompileState *compst, TTree *tree, int tt) {
+ int n = fixedlen(tree);
+ if (n >= 0 && n <= MAXBEHIND && !hascaptures(tree)) {
+ codegen(compst, tree, 0, tt, fullset);
+ if (n > 0)
+ addinstruction(compst, IBehind, n);
+ }
+ else { /* default: Choice L1; p1; BackCommit L2; L1: Fail; L2: */
+ int pcommit;
+ int pchoice = addoffsetinst(compst, IChoice);
+ codegen(compst, tree, 0, tt, fullset);
+ pcommit = addoffsetinst(compst, IBackCommit);
+ jumptohere(compst, pchoice);
+ addinstruction(compst, IFail, 0);
+ jumptohere(compst, pcommit);
+ }
+}
+
+
+/*
+** Captures: if pattern has fixed (and not too big) length, and it
+** has no nested captures, use a single IFullCapture instruction
+** after the match; otherwise, enclose the pattern with OpenCapture -
+** CloseCapture.
+*/
+static void codecapture (CompileState *compst, TTree *tree, int tt,
+ const Charset *fl) {
+ int len = fixedlen(sib1(tree));
+ if (len >= 0 && len <= MAXOFF && !hascaptures(sib1(tree))) {
+ codegen(compst, sib1(tree), 0, tt, fl);
+ addinstcap(compst, IFullCapture, tree->cap, tree->key, len);
+ }
+ else {
+ addinstcap(compst, IOpenCapture, tree->cap, tree->key, 0);
+ codegen(compst, sib1(tree), 0, tt, fl);
+ addinstcap(compst, ICloseCapture, Cclose, 0, 0);
+ }
+}
+
+
+static void coderuntime (CompileState *compst, TTree *tree, int tt) {
+ addinstcap(compst, IOpenCapture, Cgroup, tree->key, 0);
+ codegen(compst, sib1(tree), 0, tt, fullset);
+ addinstcap(compst, ICloseRunTime, Cclose, 0, 0);
+}
+
+
+/*
+** Repetion; optimizations:
+** When pattern is a charset, can use special instruction ISpan.
+** When pattern is head fail, or if it starts with characters that
+** are disjoint from what follows the repetions, a simple test
+** is enough (a fail inside the repetition would backtrack to fail
+** again in the following pattern, so there is no need for a choice).
+** When 'opt' is true, the repetion can reuse the Choice already
+** active in the stack.
+*/
+static void coderep (CompileState *compst, TTree *tree, int opt,
+ const Charset *fl) {
+ Charset st;
+ if (tocharset(tree, &st)) {
+ addinstruction(compst, ISpan, 0);
+ addcharset(compst, st.cs);
+ }
+ else {
+ int e1 = getfirst(tree, fullset, &st);
+ if (headfail(tree) || (!e1 && cs_disjoint(&st, fl))) {
+ /* L1: test (fail(p1)) -> L2; <p>; jmp L1; L2: */
+ int jmp;
+ int test = codetestset(compst, &st, 0);
+ codegen(compst, tree, 0, test, fullset);
+ jmp = addoffsetinst(compst, IJmp);
+ jumptohere(compst, test);
+ jumptothere(compst, jmp, test);
+ }
+ else {
+ /* test(fail(p1)) -> L2; choice L2; L1: <p>; partialcommit L1; L2: */
+ /* or (if 'opt'): partialcommit L1; L1: <p>; partialcommit L1; */
+ int commit, l2;
+ int test = codetestset(compst, &st, e1);
+ int pchoice = NOINST;
+ if (opt)
+ jumptohere(compst, addoffsetinst(compst, IPartialCommit));
+ else
+ pchoice = addoffsetinst(compst, IChoice);
+ l2 = gethere(compst);
+ codegen(compst, tree, 0, NOINST, fullset);
+ commit = addoffsetinst(compst, IPartialCommit);
+ jumptothere(compst, commit, l2);
+ jumptohere(compst, pchoice);
+ jumptohere(compst, test);
+ }
+ }
+}
+
+
+/*
+** Not predicate; optimizations:
+** In any case, if first test fails, 'not' succeeds, so it can jump to
+** the end. If pattern is headfail, that is all (it cannot fail
+** in other parts); this case includes 'not' of simple sets. Otherwise,
+** use the default code (a choice plus a failtwice).
+*/
+static void codenot (CompileState *compst, TTree *tree) {
+ Charset st;
+ int e = getfirst(tree, fullset, &st);
+ int test = codetestset(compst, &st, e);
+ if (headfail(tree)) /* test (fail(p1)) -> L1; fail; L1: */
+ addinstruction(compst, IFail, 0);
+ else {
+ /* test(fail(p))-> L1; choice L1; <p>; failtwice; L1: */
+ int pchoice = addoffsetinst(compst, IChoice);
+ codegen(compst, tree, 0, NOINST, fullset);
+ addinstruction(compst, IFailTwice, 0);
+ jumptohere(compst, pchoice);
+ }
+ jumptohere(compst, test);
+}
+
+
+/*
+** change open calls to calls, using list 'positions' to find
+** correct offsets; also optimize tail calls
+*/
+static void correctcalls (CompileState *compst, int *positions,
+ int from, int to) {
+ int i;
+ Instruction *code = compst->p->code;
+ for (i = from; i < to; i += sizei(&code[i])) {
+ if (code[i].i.code == IOpenCall) {
+ int n = code[i].i.key; /* rule number */
+ int rule = positions[n]; /* rule position */
+ assert(rule == from || code[rule - 1].i.code == IRet);
+ if (code[finaltarget(code, i + 2)].i.code == IRet) /* call; ret ? */
+ code[i].i.code = IJmp; /* tail call */
+ else
+ code[i].i.code = ICall;
+ jumptothere(compst, i, rule); /* call jumps to respective rule */
+ }
+ }
+ assert(i == to);
+}
+
+
+/*
+** Code for a grammar:
+** call L1; jmp L2; L1: rule 1; ret; rule 2; ret; ...; L2:
+*/
+static void codegrammar (CompileState *compst, TTree *grammar) {
+ int positions[MAXRULES];
+ int rulenumber = 0;
+ TTree *rule;
+ int firstcall = addoffsetinst(compst, ICall); /* call initial rule */
+ int jumptoend = addoffsetinst(compst, IJmp); /* jump to the end */
+ int start = gethere(compst); /* here starts the initial rule */
+ jumptohere(compst, firstcall);
+ for (rule = sib1(grammar); rule->tag == TRule; rule = sib2(rule)) {
+ positions[rulenumber++] = gethere(compst); /* save rule position */
+ codegen(compst, sib1(rule), 0, NOINST, fullset); /* code rule */
+ addinstruction(compst, IRet, 0);
+ }
+ assert(rule->tag == TTrue);
+ jumptohere(compst, jumptoend);
+ correctcalls(compst, positions, start, gethere(compst));
+}
+
+
+static void codecall (CompileState *compst, TTree *call) {
+ int c = addoffsetinst(compst, IOpenCall); /* to be corrected later */
+ getinstr(compst, c).i.key = sib2(call)->cap; /* rule number */
+ assert(sib2(call)->tag == TRule);
+}
+
+
+/*
+** Code first child of a sequence
+** (second child is called in-place to allow tail call)
+** Return 'tt' for second child
+*/
+static int codeseq1 (CompileState *compst, TTree *p1, TTree *p2,
+ int tt, const Charset *fl) {
+ if (needfollow(p1)) {
+ Charset fl1;
+ getfirst(p2, fl, &fl1); /* p1 follow is p2 first */
+ codegen(compst, p1, 0, tt, &fl1);
+ }
+ else /* use 'fullset' as follow */
+ codegen(compst, p1, 0, tt, fullset);
+ if (fixedlen(p1) != 0) /* can 'p1' consume anything? */
+ return NOINST; /* invalidate test */
+ else return tt; /* else 'tt' still protects sib2 */
+}
+
+
+/*
+** Main code-generation function: dispatch to auxiliar functions
+** according to kind of tree. ('needfollow' should return true
+** only for consructions that use 'fl'.)
+*/
+static void codegen (CompileState *compst, TTree *tree, int opt, int tt,
+ const Charset *fl) {
+ tailcall:
+ switch (tree->tag) {
+ case TChar: codechar(compst, tree->u.n, tt); break;
+ case TAny: addinstruction(compst, IAny, 0); break;
+ case TSet: codecharset(compst, treebuffer(tree), tt); break;
+ case TTrue: break;
+ case TFalse: addinstruction(compst, IFail, 0); break;
+ case TChoice: codechoice(compst, sib1(tree), sib2(tree), opt, fl); break;
+ case TRep: coderep(compst, sib1(tree), opt, fl); break;
+ case TBehind: codebehind(compst, tree); break;
+ case TNot: codenot(compst, sib1(tree)); break;
+ case TAnd: codeand(compst, sib1(tree), tt); break;
+ case TCapture: codecapture(compst, tree, tt, fl); break;
+ case TRunTime: coderuntime(compst, tree, tt); break;
+ case TGrammar: codegrammar(compst, tree); break;
+ case TCall: codecall(compst, tree); break;
+ case TSeq: {
+ tt = codeseq1(compst, sib1(tree), sib2(tree), tt, fl); /* code 'p1' */
+ /* codegen(compst, p2, opt, tt, fl); */
+ tree = sib2(tree); goto tailcall;
+ }
+ default: assert(0);
+ }
+}
+
+
+/*
+** Optimize jumps and other jump-like instructions.
+** * Update labels of instructions with labels to their final
+** destinations (e.g., choice L1; ... L1: jmp L2: becomes
+** choice L2)
+** * Jumps to other instructions that do jumps become those
+** instructions (e.g., jump to return becomes a return; jump
+** to commit becomes a commit)
+*/
+static void peephole (CompileState *compst) {
+ Instruction *code = compst->p->code;
+ int i;
+ for (i = 0; i < compst->ncode; i += sizei(&code[i])) {
+ redo:
+ switch (code[i].i.code) {
+ case IChoice: case ICall: case ICommit: case IPartialCommit:
+ case IBackCommit: case ITestChar: case ITestSet:
+ case ITestAny: { /* instructions with labels */
+ jumptothere(compst, i, finallabel(code, i)); /* optimize label */
+ break;
+ }
+ case IJmp: {
+ int ft = finaltarget(code, i);
+ switch (code[ft].i.code) { /* jumping to what? */
+ case IRet: case IFail: case IFailTwice:
+ case IEnd: { /* instructions with unconditional implicit jumps */
+ code[i] = code[ft]; /* jump becomes that instruction */
+ code[i + 1].i.code = IAny; /* 'no-op' for target position */
+ break;
+ }
+ case ICommit: case IPartialCommit:
+ case IBackCommit: { /* inst. with unconditional explicit jumps */
+ int fft = finallabel(code, ft);
+ code[i] = code[ft]; /* jump becomes that instruction... */
+ jumptothere(compst, i, fft); /* but must correct its offset */
+ goto redo; /* reoptimize its label */
+ }
+ default: {
+ jumptothere(compst, i, ft); /* optimize label */
+ break;
+ }
+ }
+ break;
+ }
+ default: break;
+ }
+ }
+ assert(code[i - 1].i.code == IEnd);
+}
+
+
+/*
+** Compile a pattern
+*/
+Instruction *compile (lua_State *L, Pattern *p) {
+ CompileState compst;
+ compst.p = p; compst.ncode = 0; compst.L = L;
+ realloccode(L, p, 2); /* minimum initial size */
+ codegen(&compst, p->tree, 0, NOINST, fullset);
+ addinstruction(&compst, IEnd, 0);
+ realloccode(L, p, compst.ncode); /* set final size */
+ peephole(&compst);
+ return p->code;
+}
+
+
+/* }====================================================== */
+
diff --git a/lpeg-1.0.1/lpcode.h b/lpeg-1.0.1/lpcode.h
new file mode 100644
index 0000000..2a5861e
--- /dev/null
+++ b/lpeg-1.0.1/lpcode.h
@@ -0,0 +1,40 @@
+/*
+** $Id: lpcode.h,v 1.8 2016/09/15 17:46:13 roberto Exp $
+*/
+
+#if !defined(lpcode_h)
+#define lpcode_h
+
+#include "lua.h"
+
+#include "lptypes.h"
+#include "lptree.h"
+#include "lpvm.h"
+
+int tocharset (TTree *tree, Charset *cs);
+int checkaux (TTree *tree, int pred);
+int fixedlen (TTree *tree);
+int hascaptures (TTree *tree);
+int lp_gc (lua_State *L);
+Instruction *compile (lua_State *L, Pattern *p);
+void realloccode (lua_State *L, Pattern *p, int nsize);
+int sizei (const Instruction *i);
+
+
+#define PEnullable 0
+#define PEnofail 1
+
+/*
+** nofail(t) implies that 't' cannot fail with any input
+*/
+#define nofail(t) checkaux(t, PEnofail)
+
+/*
+** (not nullable(t)) implies 't' cannot match without consuming
+** something
+*/
+#define nullable(t) checkaux(t, PEnullable)
+
+
+
+#endif
diff --git a/lpeg-1.0.1/lpeg-128.gif b/lpeg-1.0.1/lpeg-128.gif
new file mode 100644
index 0000000..bbf5e78
--- /dev/null
+++ b/lpeg-1.0.1/lpeg-128.gif
Binary files differ
diff --git a/lpeg-1.0.1/lpeg.html b/lpeg-1.0.1/lpeg.html
new file mode 100644
index 0000000..5c9535f
--- /dev/null
+++ b/lpeg-1.0.1/lpeg.html
@@ -0,0 +1,1445 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>LPeg - Parsing Expression Grammars For Lua</title>
+ <link rel="stylesheet"
+ href="http://www.inf.puc-rio.br/~roberto/lpeg/doc.css"
+ type="text/css"/>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+</head>
+<body>
+
+<!-- $Id: lpeg.html,v 1.77 2017/01/13 13:40:05 roberto Exp $ -->
+
+<div id="container">
+
+<div id="product">
+ <div id="product_logo">
+ <a href="http://www.inf.puc-rio.br/~roberto/lpeg/">
+ <img alt="LPeg logo" src="lpeg-128.gif"/></a>
+
+ </div>
+ <div id="product_name"><big><strong>LPeg</strong></big></div>
+ <div id="product_description">
+ Parsing Expression Grammars For Lua, version 1.0
+ </div>
+</div> <!-- id="product" -->
+
+<div id="main">
+
+<div id="navigation">
+<h1>LPeg</h1>
+
+<ul>
+ <li><strong>Home</strong>
+ <ul>
+ <li><a href="#intro">Introduction</a></li>
+ <li><a href="#func">Functions</a></li>
+ <li><a href="#basic">Basic Constructions</a></li>
+ <li><a href="#grammar">Grammars</a></li>
+ <li><a href="#captures">Captures</a></li>
+ <li><a href="#ex">Some Examples</a></li>
+ <li><a href="re.html">The <code>re</code> Module</a></li>
+ <li><a href="#download">Download</a></li>
+ <li><a href="#license">License</a></li>
+ </ul>
+ </li>
+</ul>
+</div> <!-- id="navigation" -->
+
+<div id="content">
+
+
+<h2><a name="intro">Introduction</a></h2>
+
+<p>
+<em>LPeg</em> is a new pattern-matching library for Lua,
+based on
+<a href="http://pdos.csail.mit.edu/%7Ebaford/packrat/">
+Parsing Expression Grammars</a> (PEGs).
+This text is a reference manual for the library.
+For a more formal treatment of LPeg,
+as well as some discussion about its implementation,
+see
+<a href="http://www.inf.puc-rio.br/~roberto/docs/peg.pdf">
+A Text Pattern-Matching Tool based on Parsing Expression Grammars</a>.
+(You may also be interested in my
+<a href="http://vimeo.com/1485123">talk about LPeg</a>
+given at the III Lua Workshop.)
+</p>
+
+<p>
+Following the Snobol tradition,
+LPeg defines patterns as first-class objects.
+That is, patterns are regular Lua values
+(represented by userdata).
+The library offers several functions to create
+and compose patterns.
+With the use of metamethods,
+several of these functions are provided as infix or prefix
+operators.
+On the one hand,
+the result is usually much more verbose than the typical
+encoding of patterns using the so called
+<em>regular expressions</em>
+(which typically are not regular expressions in the formal sense).
+On the other hand,
+first-class patterns allow much better documentation
+(as it is easy to comment the code,
+to break complex definitions in smaller parts, etc.)
+and are extensible,
+as we can define new functions to create and compose patterns.
+</p>
+
+<p>
+For a quick glance of the library,
+the following table summarizes its basic operations
+for creating patterns:
+</p>
+<table border="1">
+<tbody><tr><td><b>Operator</b></td><td><b>Description</b></td></tr>
+<tr><td><a href="#op-p"><code>lpeg.P(string)</code></a></td>
+ <td>Matches <code>string</code> literally</td></tr>
+<tr><td><a href="#op-p"><code>lpeg.P(n)</code></a></td>
+ <td>Matches exactly <code>n</code> characters</td></tr>
+<tr><td><a href="#op-s"><code>lpeg.S(string)</code></a></td>
+ <td>Matches any character in <code>string</code> (Set)</td></tr>
+<tr><td><a href="#op-r"><code>lpeg.R("<em>xy</em>")</code></a></td>
+ <td>Matches any character between <em>x</em> and <em>y</em> (Range)</td></tr>
+<tr><td><a href="#op-pow"><code>patt^n</code></a></td>
+ <td>Matches at least <code>n</code> repetitions of <code>patt</code></td></tr>
+<tr><td><a href="#op-pow"><code>patt^-n</code></a></td>
+ <td>Matches at most <code>n</code> repetitions of <code>patt</code></td></tr>
+<tr><td><a href="#op-mul"><code>patt1 * patt2</code></a></td>
+ <td>Matches <code>patt1</code> followed by <code>patt2</code></td></tr>
+<tr><td><a href="#op-add"><code>patt1 + patt2</code></a></td>
+ <td>Matches <code>patt1</code> or <code>patt2</code>
+ (ordered choice)</td></tr>
+<tr><td><a href="#op-sub"><code>patt1 - patt2</code></a></td>
+ <td>Matches <code>patt1</code> if <code>patt2</code> does not match</td></tr>
+<tr><td><a href="#op-unm"><code>-patt</code></a></td>
+ <td>Equivalent to <code>("" - patt)</code></td></tr>
+<tr><td><a href="#op-len"><code>#patt</code></a></td>
+ <td>Matches <code>patt</code> but consumes no input</td></tr>
+<tr><td><a href="#op-behind"><code>lpeg.B(patt)</code></a></td>
+ <td>Matches <code>patt</code> behind the current position,
+ consuming no input</td></tr>
+</tbody></table>
+
+<p>As a very simple example,
+<code>lpeg.R("09")^1</code> creates a pattern that
+matches a non-empty sequence of digits.
+As a not so simple example,
+<code>-lpeg.P(1)</code>
+(which can be written as <code>lpeg.P(-1)</code>,
+or simply <code>-1</code> for operations expecting a pattern)
+matches an empty string only if it cannot match a single character;
+so, it succeeds only at the end of the subject.
+</p>
+
+<p>
+LPeg also offers the <a href="re.html"><code>re</code> module</a>,
+which implements patterns following a regular-expression style
+(e.g., <code>[09]+</code>).
+(This module is 260 lines of Lua code,
+and of course it uses LPeg to parse regular expressions and
+translate them to regular LPeg patterns.)
+</p>
+
+
+<h2><a name="func">Functions</a></h2>
+
+
+<h3><a name="f-match"></a><code>lpeg.match (pattern, subject [, init])</code></h3>
+<p>
+The matching function.
+It attempts to match the given pattern against the subject string.
+If the match succeeds,
+returns the index in the subject of the first character after the match,
+or the <a href="#captures">captured values</a>
+(if the pattern captured any value).
+</p>
+
+<p>
+An optional numeric argument <code>init</code> makes the match
+start at that position in the subject string.
+As usual in Lua libraries,
+a negative value counts from the end.
+</p>
+
+<p>
+Unlike typical pattern-matching functions,
+<code>match</code> works only in <em>anchored</em> mode;
+that is, it tries to match the pattern with a prefix of
+the given subject string (at position <code>init</code>),
+not with an arbitrary substring of the subject.
+So, if we want to find a pattern anywhere in a string,
+we must either write a loop in Lua or write a pattern that
+matches anywhere.
+This second approach is easy and quite efficient;
+see <a href="#ex">examples</a>.
+</p>
+
+<h3><a name="f-type"></a><code>lpeg.type (value)</code></h3>
+<p>
+If the given value is a pattern,
+returns the string <code>"pattern"</code>.
+Otherwise returns nil.
+</p>
+
+<h3><a name="f-version"></a><code>lpeg.version ()</code></h3>
+<p>
+Returns a string with the running version of LPeg.
+</p>
+
+<h3><a name="f-setstack"></a><code>lpeg.setmaxstack (max)</code></h3>
+<p>
+Sets a limit for the size of the backtrack stack used by LPeg to
+track calls and choices.
+(The default limit is 400.)
+Most well-written patterns need little backtrack levels and
+therefore you seldom need to change this limit;
+before changing it you should try to rewrite your
+pattern to avoid the need for extra space.
+Nevertheless, a few useful patterns may overflow.
+Also, with recursive grammars,
+subjects with deep recursion may also need larger limits.
+</p>
+
+
+<h2><a name="basic">Basic Constructions</a></h2>
+
+<p>
+The following operations build patterns.
+All operations that expect a pattern as an argument
+may receive also strings, tables, numbers, booleans, or functions,
+which are translated to patterns according to
+the rules of function <a href="#op-p"><code>lpeg.P</code></a>.
+</p>
+
+
+
+<h3><a name="op-p"></a><code>lpeg.P (value)</code></h3>
+<p>
+Converts the given value into a proper pattern,
+according to the following rules:
+</p>
+<ul>
+
+<li><p>
+If the argument is a pattern,
+it is returned unmodified.
+</p></li>
+
+<li><p>
+If the argument is a string,
+it is translated to a pattern that matches the string literally.
+</p></li>
+
+<li><p>
+If the argument is a non-negative number <em>n</em>,
+the result is a pattern that matches exactly <em>n</em> characters.
+</p></li>
+
+<li><p>
+If the argument is a negative number <em>-n</em>,
+the result is a pattern that
+succeeds only if the input string has less than <em>n</em> characters left:
+<code>lpeg.P(-n)</code>
+is equivalent to <code>-lpeg.P(n)</code>
+(see the <a href="#op-unm">unary minus operation</a>).
+</p></li>
+
+<li><p>
+If the argument is a boolean,
+the result is a pattern that always succeeds or always fails
+(according to the boolean value),
+without consuming any input.
+</p></li>
+
+<li><p>
+If the argument is a table,
+it is interpreted as a grammar
+(see <a href="#grammar">Grammars</a>).
+</p></li>
+
+<li><p>
+If the argument is a function,
+returns a pattern equivalent to a
+<a href="#matchtime">match-time capture</a> over the empty string.
+</p></li>
+
+</ul>
+
+
+<h3><a name="op-behind"></a><code>lpeg.B(patt)</code></h3>
+<p>
+Returns a pattern that
+matches only if the input string at the current position
+is preceded by <code>patt</code>.
+Pattern <code>patt</code> must match only strings
+with some fixed length,
+and it cannot contain captures.
+</p>
+
+<p>
+Like the <a href="#op-len">and predicate</a>,
+this pattern never consumes any input,
+independently of success or failure.
+</p>
+
+
+<h3><a name="op-r"></a><code>lpeg.R ({range})</code></h3>
+<p>
+Returns a pattern that matches any single character
+belonging to one of the given <em>ranges</em>.
+Each <code>range</code> is a string <em>xy</em> of length 2,
+representing all characters with code
+between the codes of <em>x</em> and <em>y</em>
+(both inclusive).
+</p>
+
+<p>
+As an example, the pattern
+<code>lpeg.R("09")</code> matches any digit,
+and <code>lpeg.R("az", "AZ")</code> matches any ASCII letter.
+</p>
+
+
+<h3><a name="op-s"></a><code>lpeg.S (string)</code></h3>
+<p>
+Returns a pattern that matches any single character that
+appears in the given string.
+(The <code>S</code> stands for <em>Set</em>.)
+</p>
+
+<p>
+As an example, the pattern
+<code>lpeg.S("+-*/")</code> matches any arithmetic operator.
+</p>
+
+<p>
+Note that, if <code>s</code> is a character
+(that is, a string of length 1),
+then <code>lpeg.P(s)</code> is equivalent to <code>lpeg.S(s)</code>
+which is equivalent to <code>lpeg.R(s..s)</code>.
+Note also that both <code>lpeg.S("")</code> and <code>lpeg.R()</code>
+are patterns that always fail.
+</p>
+
+
+<h3><a name="op-v"></a><code>lpeg.V (v)</code></h3>
+<p>
+This operation creates a non-terminal (a <em>variable</em>)
+for a grammar.
+The created non-terminal refers to the rule indexed by <code>v</code>
+in the enclosing grammar.
+(See <a href="#grammar">Grammars</a> for details.)
+</p>
+
+
+<h3><a name="op-locale"></a><code>lpeg.locale ([table])</code></h3>
+<p>
+Returns a table with patterns for matching some character classes
+according to the current locale.
+The table has fields named
+<code>alnum</code>,
+<code>alpha</code>,
+<code>cntrl</code>,
+<code>digit</code>,
+<code>graph</code>,
+<code>lower</code>,
+<code>print</code>,
+<code>punct</code>,
+<code>space</code>,
+<code>upper</code>, and
+<code>xdigit</code>,
+each one containing a correspondent pattern.
+Each pattern matches any single character that belongs to its class.
+</p>
+
+<p>
+If called with an argument <code>table</code>,
+then it creates those fields inside the given table and
+returns that table.
+</p>
+
+
+<h3><a name="op-len"></a><code>#patt</code></h3>
+<p>
+Returns a pattern that
+matches only if the input string matches <code>patt</code>,
+but without consuming any input,
+independently of success or failure.
+(This pattern is called an <em>and predicate</em>
+and it is equivalent to
+<em>&amp;patt</em> in the original PEG notation.)
+</p>
+
+
+<p>
+This pattern never produces any capture.
+</p>
+
+
+<h3><a name="op-unm"></a><code>-patt</code></h3>
+<p>
+Returns a pattern that
+matches only if the input string does not match <code>patt</code>.
+It does not consume any input,
+independently of success or failure.
+(This pattern is equivalent to
+<em>!patt</em> in the original PEG notation.)
+</p>
+
+<p>
+As an example, the pattern
+<code>-lpeg.P(1)</code> matches only the end of string.
+</p>
+
+<p>
+This pattern never produces any captures,
+because either <code>patt</code> fails
+or <code>-patt</code> fails.
+(A failing pattern never produces captures.)
+</p>
+
+
+<h3><a name="op-add"></a><code>patt1 + patt2</code></h3>
+<p>
+Returns a pattern equivalent to an <em>ordered choice</em>
+of <code>patt1</code> and <code>patt2</code>.
+(This is denoted by <em>patt1 / patt2</em> in the original PEG notation,
+not to be confused with the <code>/</code> operation in LPeg.)
+It matches either <code>patt1</code> or <code>patt2</code>,
+with no backtracking once one of them succeeds.
+The identity element for this operation is the pattern
+<code>lpeg.P(false)</code>,
+which always fails.
+</p>
+
+<p>
+If both <code>patt1</code> and <code>patt2</code> are
+character sets,
+this operation is equivalent to set union.
+</p>
+<pre class="example">
+lower = lpeg.R("az")
+upper = lpeg.R("AZ")
+letter = lower + upper
+</pre>
+
+
+<h3><a name="op-sub"></a><code>patt1 - patt2</code></h3>
+<p>
+Returns a pattern equivalent to <em>!patt2 patt1</em>.
+This pattern asserts that the input does not match
+<code>patt2</code> and then matches <code>patt1</code>.
+</p>
+
+<p>
+When successful,
+this pattern produces all captures from <code>patt1</code>.
+It never produces any capture from <code>patt2</code>
+(as either <code>patt2</code> fails or
+<code>patt1 - patt2</code> fails).
+</p>
+
+<p>
+If both <code>patt1</code> and <code>patt2</code> are
+character sets,
+this operation is equivalent to set difference.
+Note that <code>-patt</code> is equivalent to <code>"" - patt</code>
+(or <code>0 - patt</code>).
+If <code>patt</code> is a character set,
+<code>1 - patt</code> is its complement.
+</p>
+
+
+<h3><a name="op-mul"></a><code>patt1 * patt2</code></h3>
+<p>
+Returns a pattern that matches <code>patt1</code>
+and then matches <code>patt2</code>,
+starting where <code>patt1</code> finished.
+The identity element for this operation is the
+pattern <code>lpeg.P(true)</code>,
+which always succeeds.
+</p>
+
+<p>
+(LPeg uses the <code>*</code> operator
+[instead of the more obvious <code>..</code>]
+both because it has
+the right priority and because in formal languages it is
+common to use a dot for denoting concatenation.)
+</p>
+
+
+<h3><a name="op-pow"></a><code>patt^n</code></h3>
+<p>
+If <code>n</code> is nonnegative,
+this pattern is
+equivalent to <em>patt<sup>n</sup> patt*</em>:
+It matches <code>n</code> or more occurrences of <code>patt</code>.
+</p>
+
+<p>
+Otherwise, when <code>n</code> is negative,
+this pattern is equivalent to <em>(patt?)<sup>-n</sup></em>:
+It matches at most <code>|n|</code>
+occurrences of <code>patt</code>.
+</p>
+
+<p>
+In particular, <code>patt^0</code> is equivalent to <em>patt*</em>,
+<code>patt^1</code> is equivalent to <em>patt+</em>,
+and <code>patt^-1</code> is equivalent to <em>patt?</em>
+in the original PEG notation.
+</p>
+
+<p>
+In all cases,
+the resulting pattern is greedy with no backtracking
+(also called a <em>possessive</em> repetition).
+That is, it matches only the longest possible sequence
+of matches for <code>patt</code>.
+</p>
+
+
+
+<h2><a name="grammar">Grammars</a></h2>
+
+<p>
+With the use of Lua variables,
+it is possible to define patterns incrementally,
+with each new pattern using previously defined ones.
+However, this technique does not allow the definition of
+recursive patterns.
+For recursive patterns,
+we need real grammars.
+</p>
+
+<p>
+LPeg represents grammars with tables,
+where each entry is a rule.
+</p>
+
+<p>
+The call <code>lpeg.V(v)</code>
+creates a pattern that represents the nonterminal
+(or <em>variable</em>) with index <code>v</code> in a grammar.
+Because the grammar still does not exist when
+this function is evaluated,
+the result is an <em>open reference</em> to the respective rule.
+</p>
+
+<p>
+A table is <em>fixed</em> when it is converted to a pattern
+(either by calling <code>lpeg.P</code> or by using it wherein a
+pattern is expected).
+Then every open reference created by <code>lpeg.V(v)</code>
+is corrected to refer to the rule indexed by <code>v</code> in the table.
+</p>
+
+<p>
+When a table is fixed,
+the result is a pattern that matches its <em>initial rule</em>.
+The entry with index 1 in the table defines its initial rule.
+If that entry is a string,
+it is assumed to be the name of the initial rule.
+Otherwise, LPeg assumes that the entry 1 itself is the initial rule.
+</p>
+
+<p>
+As an example,
+the following grammar matches strings of a's and b's that
+have the same number of a's and b's:
+</p>
+<pre class="example">
+equalcount = lpeg.P{
+ "S"; -- initial rule name
+ S = "a" * lpeg.V"B" + "b" * lpeg.V"A" + "",
+ A = "a" * lpeg.V"S" + "b" * lpeg.V"A" * lpeg.V"A",
+ B = "b" * lpeg.V"S" + "a" * lpeg.V"B" * lpeg.V"B",
+} * -1
+</pre>
+<p>
+It is equivalent to the following grammar in standard PEG notation:
+</p>
+<pre class="example">
+ S <- 'a' B / 'b' A / ''
+ A <- 'a' S / 'b' A A
+ B <- 'b' S / 'a' B B
+</pre>
+
+
+<h2><a name="captures">Captures</a></h2>
+
+<p>
+A <em>capture</em> is a pattern that produces values
+(the so called <em>semantic information</em>)
+according to what it matches.
+LPeg offers several kinds of captures,
+which produces values based on matches and combine these values to
+produce new values.
+Each capture may produce zero or more values.
+</p>
+
+<p>
+The following table summarizes the basic captures:
+</p>
+<table border="1">
+<tbody><tr><td><b>Operation</b></td><td><b>What it Produces</b></td></tr>
+<tr><td><a href="#cap-c"><code>lpeg.C(patt)</code></a></td>
+ <td>the match for <code>patt</code> plus all captures
+ made by <code>patt</code></td></tr>
+<tr><td><a href="#cap-arg"><code>lpeg.Carg(n)</code></a></td>
+ <td>the value of the n<sup>th</sup> extra argument to
+ <code>lpeg.match</code> (matches the empty string)</td></tr>
+<tr><td><a href="#cap-b"><code>lpeg.Cb(name)</code></a></td>
+ <td>the values produced by the previous
+ group capture named <code>name</code>
+ (matches the empty string)</td></tr>
+<tr><td><a href="#cap-cc"><code>lpeg.Cc(values)</code></a></td>
+ <td>the given values (matches the empty string)</td></tr>
+<tr><td><a href="#cap-f"><code>lpeg.Cf(patt, func)</code></a></td>
+ <td>a <em>folding</em> of the captures from <code>patt</code></td></tr>
+<tr><td><a href="#cap-g"><code>lpeg.Cg(patt [, name])</code></a></td>
+ <td>the values produced by <code>patt</code>,
+ optionally tagged with <code>name</code></td></tr>
+<tr><td><a href="#cap-p"><code>lpeg.Cp()</code></a></td>
+ <td>the current position (matches the empty string)</td></tr>
+<tr><td><a href="#cap-s"><code>lpeg.Cs(patt)</code></a></td>
+ <td>the match for <code>patt</code>
+ with the values from nested captures replacing their matches</td></tr>
+<tr><td><a href="#cap-t"><code>lpeg.Ct(patt)</code></a></td>
+ <td>a table with all captures from <code>patt</code></td></tr>
+<tr><td><a href="#cap-string"><code>patt / string</code></a></td>
+ <td><code>string</code>, with some marks replaced by captures
+ of <code>patt</code></td></tr>
+<tr><td><a href="#cap-num"><code>patt / number</code></a></td>
+ <td>the n-th value captured by <code>patt</code>,
+or no value when <code>number</code> is zero.</td></tr>
+<tr><td><a href="#cap-query"><code>patt / table</code></a></td>
+ <td><code>table[c]</code>, where <code>c</code> is the (first)
+ capture of <code>patt</code></td></tr>
+<tr><td><a href="#cap-func"><code>patt / function</code></a></td>
+ <td>the returns of <code>function</code> applied to the captures
+ of <code>patt</code></td></tr>
+<tr><td><a href="#matchtime"><code>lpeg.Cmt(patt, function)</code></a></td>
+ <td>the returns of <code>function</code> applied to the captures
+ of <code>patt</code>; the application is done at match time</td></tr>
+</tbody></table>
+
+<p>
+A capture pattern produces its values only when it succeeds.
+For instance,
+the pattern <code>lpeg.C(lpeg.P"a"^-1)</code>
+produces the empty string when there is no <code>"a"</code>
+(because the pattern <code>"a"?</code> succeeds),
+while the pattern <code>lpeg.C("a")^-1</code>
+does not produce any value when there is no <code>"a"</code>
+(because the pattern <code>"a"</code> fails).
+A pattern inside a loop or inside a recursive structure
+produces values for each match.
+</p>
+
+<p>
+Usually,
+LPeg does not specify when (and if) it evaluates its captures.
+(As an example,
+consider the pattern <code>lpeg.P"a" / func / 0</code>.
+Because the "division" by 0 instructs LPeg to throw away the
+results from the pattern,
+LPeg may or may not call <code>func</code>.)
+Therefore, captures should avoid side effects.
+Moreover,
+most captures cannot affect the way a pattern matches a subject.
+The only exception to this rule is the
+so-called <a href="#matchtime"><em>match-time capture</em></a>.
+When a match-time capture matches,
+it forces the immediate evaluation of all its nested captures
+and then calls its corresponding function,
+which defines whether the match succeeds and also
+what values are produced.
+</p>
+
+<h3><a name="cap-c"></a><code>lpeg.C (patt)</code></h3>
+<p>
+Creates a <em>simple capture</em>,
+which captures the substring of the subject that matches <code>patt</code>.
+The captured value is a string.
+If <code>patt</code> has other captures,
+their values are returned after this one.
+</p>
+
+
+<h3><a name="cap-arg"></a><code>lpeg.Carg (n)</code></h3>
+<p>
+Creates an <em>argument capture</em>.
+This pattern matches the empty string and
+produces the value given as the n<sup>th</sup> extra
+argument given in the call to <code>lpeg.match</code>.
+</p>
+
+
+<h3><a name="cap-b"></a><code>lpeg.Cb (name)</code></h3>
+<p>
+Creates a <em>back capture</em>.
+This pattern matches the empty string and
+produces the values produced by the <em>most recent</em>
+<a href="#cap-g">group capture</a> named <code>name</code>
+(where <code>name</code> can be any Lua value).
+</p>
+
+<p>
+<em>Most recent</em> means the last
+<em>complete</em>
+<em>outermost</em>
+group capture with the given name.
+A <em>Complete</em> capture means that the entire pattern
+corresponding to the capture has matched.
+An <em>Outermost</em> capture means that the capture is not inside
+another complete capture.
+</p>
+
+<p>
+In the same way that LPeg does not specify when it evaluates captures,
+it does not specify whether it reuses
+values previously produced by the group
+or re-evaluates them.
+</p>
+
+<h3><a name="cap-cc"></a><code>lpeg.Cc ([value, ...])</code></h3>
+<p>
+Creates a <em>constant capture</em>.
+This pattern matches the empty string and
+produces all given values as its captured values.
+</p>
+
+
+<h3><a name="cap-f"></a><code>lpeg.Cf (patt, func)</code></h3>
+<p>
+Creates a <em>fold capture</em>.
+If <code>patt</code> produces a list of captures
+<em>C<sub>1</sub> C<sub>2</sub> ... C<sub>n</sub></em>,
+this capture will produce the value
+<em>func(...func(func(C<sub>1</sub>, C<sub>2</sub>), C<sub>3</sub>)...,
+ C<sub>n</sub>)</em>,
+that is, it will <em>fold</em>
+(or <em>accumulate</em>, or <em>reduce</em>)
+the captures from <code>patt</code> using function <code>func</code>.
+</p>
+
+<p>
+This capture assumes that <code>patt</code> should produce
+at least one capture with at least one value (of any type),
+which becomes the initial value of an <em>accumulator</em>.
+(If you need a specific initial value,
+you may prefix a <a href="#cap-cc">constant capture</a> to <code>patt</code>.)
+For each subsequent capture,
+LPeg calls <code>func</code>
+with this accumulator as the first argument and all values produced
+by the capture as extra arguments;
+the first result from this call
+becomes the new value for the accumulator.
+The final value of the accumulator becomes the captured value.
+</p>
+
+<p>
+As an example,
+the following pattern matches a list of numbers separated
+by commas and returns their addition:
+</p>
+<pre class="example">
+-- matches a numeral and captures its numerical value
+number = lpeg.R"09"^1 / tonumber
+
+-- matches a list of numbers, capturing their values
+list = number * ("," * number)^0
+
+-- auxiliary function to add two numbers
+function add (acc, newvalue) return acc + newvalue end
+
+-- folds the list of numbers adding them
+sum = lpeg.Cf(list, add)
+
+-- example of use
+print(sum:match("10,30,43")) --&gt; 83
+</pre>
+
+
+<h3><a name="cap-g"></a><code>lpeg.Cg (patt [, name])</code></h3>
+<p>
+Creates a <em>group capture</em>.
+It groups all values returned by <code>patt</code>
+into a single capture.
+The group may be anonymous (if no name is given)
+or named with the given name
+(which can be any non-nil Lua value).
+</p>
+
+<p>
+An anonymous group serves to join values from several captures into
+a single capture.
+A named group has a different behavior.
+In most situations, a named group returns no values at all.
+Its values are only relevant for a following
+<a href="#cap-b">back capture</a> or when used
+inside a <a href="#cap-t">table capture</a>.
+</p>
+
+
+<h3><a name="cap-p"></a><code>lpeg.Cp ()</code></h3>
+<p>
+Creates a <em>position capture</em>.
+It matches the empty string and
+captures the position in the subject where the match occurs.
+The captured value is a number.
+</p>
+
+
+<h3><a name="cap-s"></a><code>lpeg.Cs (patt)</code></h3>
+<p>
+Creates a <em>substitution capture</em>,
+which captures the substring of the subject that matches <code>patt</code>,
+with <em>substitutions</em>.
+For any capture inside <code>patt</code> with a value,
+the substring that matched the capture is replaced by the capture value
+(which should be a string).
+The final captured value is the string resulting from
+all replacements.
+</p>
+
+
+<h3><a name="cap-t"></a><code>lpeg.Ct (patt)</code></h3>
+<p>
+Creates a <em>table capture</em>.
+This capture returns a table with all values from all anonymous captures
+made by <code>patt</code> inside this table in successive integer keys,
+starting at 1.
+Moreover,
+for each named capture group created by <code>patt</code>,
+the first value of the group is put into the table
+with the group name as its key.
+The captured value is only the table.
+</p>
+
+
+<h3><a name="cap-string"></a><code>patt / string</code></h3>
+<p>
+Creates a <em>string capture</em>.
+It creates a capture string based on <code>string</code>.
+The captured value is a copy of <code>string</code>,
+except that the character <code>%</code> works as an escape character:
+any sequence in <code>string</code> of the form <code>%<em>n</em></code>,
+with <em>n</em> between 1 and 9,
+stands for the match of the <em>n</em>-th capture in <code>patt</code>.
+The sequence <code>%0</code> stands for the whole match.
+The sequence <code>%%</code> stands for a single&nbsp;<code>%</code>.
+</p>
+
+
+<h3><a name="cap-num"></a><code>patt / number</code></h3>
+<p>
+Creates a <em>numbered capture</em>.
+For a non-zero number,
+the captured value is the n-th value
+captured by <code>patt</code>.
+When <code>number</code> is zero,
+there are no captured values.
+</p>
+
+
+<h3><a name="cap-query"></a><code>patt / table</code></h3>
+<p>
+Creates a <em>query capture</em>.
+It indexes the given table using as key the first value captured by
+<code>patt</code>,
+or the whole match if <code>patt</code> produced no value.
+The value at that index is the final value of the capture.
+If the table does not have that key,
+there is no captured value.
+</p>
+
+
+<h3><a name="cap-func"></a><code>patt / function</code></h3>
+<p>
+Creates a <em>function capture</em>.
+It calls the given function passing all captures made by
+<code>patt</code> as arguments,
+or the whole match if <code>patt</code> made no capture.
+The values returned by the function
+are the final values of the capture.
+In particular,
+if <code>function</code> returns no value,
+there is no captured value.
+</p>
+
+
+<h3><a name="matchtime"></a><code>lpeg.Cmt(patt, function)</code></h3>
+<p>
+Creates a <em>match-time capture</em>.
+Unlike all other captures,
+this one is evaluated immediately when a match occurs
+(even if it is part of a larger pattern that fails later).
+It forces the immediate evaluation of all its nested captures
+and then calls <code>function</code>.
+</p>
+
+<p>
+The given function gets as arguments the entire subject,
+the current position (after the match of <code>patt</code>),
+plus any capture values produced by <code>patt</code>.
+</p>
+
+<p>
+The first value returned by <code>function</code>
+defines how the match happens.
+If the call returns a number,
+the match succeeds
+and the returned number becomes the new current position.
+(Assuming a subject <em>s</em> and current position <em>i</em>,
+the returned number must be in the range <em>[i, len(s) + 1]</em>.)
+If the call returns <b>true</b>,
+the match succeeds without consuming any input.
+(So, to return <b>true</b> is equivalent to return <em>i</em>.)
+If the call returns <b>false</b>, <b>nil</b>, or no value,
+the match fails.
+</p>
+
+<p>
+Any extra values returned by the function become the
+values produced by the capture.
+</p>
+
+
+
+
+<h2><a name="ex">Some Examples</a></h2>
+
+<h3>Using a Pattern</h3>
+<p>
+This example shows a very simple but complete program
+that builds and uses a pattern:
+</p>
+<pre class="example">
+local lpeg = require "lpeg"
+
+-- matches a word followed by end-of-string
+p = lpeg.R"az"^1 * -1
+
+print(p:match("hello")) --> 6
+print(lpeg.match(p, "hello")) --> 6
+print(p:match("1 hello")) --> nil
+</pre>
+<p>
+The pattern is simply a sequence of one or more lower-case letters
+followed by the end of string (-1).
+The program calls <code>match</code> both as a method
+and as a function.
+In both sucessful cases,
+the match returns
+the index of the first character after the match,
+which is the string length plus one.
+</p>
+
+
+<h3>Name-value lists</h3>
+<p>
+This example parses a list of name-value pairs and returns a table
+with those pairs:
+</p>
+<pre class="example">
+lpeg.locale(lpeg) -- adds locale entries into 'lpeg' table
+
+local space = lpeg.space^0
+local name = lpeg.C(lpeg.alpha^1) * space
+local sep = lpeg.S(",;") * space
+local pair = lpeg.Cg(name * "=" * space * name) * sep^-1
+local list = lpeg.Cf(lpeg.Ct("") * pair^0, rawset)
+t = list:match("a=b, c = hi; next = pi") --> { a = "b", c = "hi", next = "pi" }
+</pre>
+<p>
+Each pair has the format <code>name = name</code> followed by
+an optional separator (a comma or a semicolon).
+The <code>pair</code> pattern encloses the pair in a group pattern,
+so that the names become the values of a single capture.
+The <code>list</code> pattern then folds these captures.
+It starts with an empty table,
+created by a table capture matching an empty string;
+then for each capture (a pair of names) it applies <code>rawset</code>
+over the accumulator (the table) and the capture values (the pair of names).
+<code>rawset</code> returns the table itself,
+so the accumulator is always the table.
+</p>
+
+<h3>Splitting a string</h3>
+<p>
+The following code builds a pattern that
+splits a string using a given pattern
+<code>sep</code> as a separator:
+</p>
+<pre class="example">
+function split (s, sep)
+ sep = lpeg.P(sep)
+ local elem = lpeg.C((1 - sep)^0)
+ local p = elem * (sep * elem)^0
+ return lpeg.match(p, s)
+end
+</pre>
+<p>
+First the function ensures that <code>sep</code> is a proper pattern.
+The pattern <code>elem</code> is a repetition of zero of more
+arbitrary characters as long as there is not a match against
+the separator.
+It also captures its match.
+The pattern <code>p</code> matches a list of elements separated
+by <code>sep</code>.
+</p>
+
+<p>
+If the split results in too many values,
+it may overflow the maximum number of values
+that can be returned by a Lua function.
+In this case,
+we can collect these values in a table:
+</p>
+<pre class="example">
+function split (s, sep)
+ sep = lpeg.P(sep)
+ local elem = lpeg.C((1 - sep)^0)
+ local p = lpeg.Ct(elem * (sep * elem)^0) -- make a table capture
+ return lpeg.match(p, s)
+end
+</pre>
+
+
+<h3>Searching for a pattern</h3>
+<p>
+The primitive <code>match</code> works only in anchored mode.
+If we want to find a pattern anywhere in a string,
+we must write a pattern that matches anywhere.
+</p>
+
+<p>
+Because patterns are composable,
+we can write a function that,
+given any arbitrary pattern <code>p</code>,
+returns a new pattern that searches for <code>p</code>
+anywhere in a string.
+There are several ways to do the search.
+One way is like this:
+</p>
+<pre class="example">
+function anywhere (p)
+ return lpeg.P{ p + 1 * lpeg.V(1) }
+end
+</pre>
+<p>
+This grammar has a straight reading:
+it matches <code>p</code> or skips one character and tries again.
+</p>
+
+<p>
+If we want to know where the pattern is in the string
+(instead of knowing only that it is there somewhere),
+we can add position captures to the pattern:
+</p>
+<pre class="example">
+local I = lpeg.Cp()
+function anywhere (p)
+ return lpeg.P{ I * p * I + 1 * lpeg.V(1) }
+end
+
+print(anywhere("world"):match("hello world!")) -> 7 12
+</pre>
+
+<p>
+Another option for the search is like this:
+</p>
+<pre class="example">
+local I = lpeg.Cp()
+function anywhere (p)
+ return (1 - lpeg.P(p))^0 * I * p * I
+end
+</pre>
+<p>
+Again the pattern has a straight reading:
+it skips as many characters as possible while not matching <code>p</code>,
+and then matches <code>p</code> (plus appropriate captures).
+</p>
+
+<p>
+If we want to look for a pattern only at word boundaries,
+we can use the following transformer:
+</p>
+
+<pre class="example">
+local t = lpeg.locale()
+
+function atwordboundary (p)
+ return lpeg.P{
+ [1] = p + t.alpha^0 * (1 - t.alpha)^1 * lpeg.V(1)
+ }
+end
+</pre>
+
+
+<h3><a name="balanced"></a>Balanced parentheses</h3>
+<p>
+The following pattern matches only strings with balanced parentheses:
+</p>
+<pre class="example">
+b = lpeg.P{ "(" * ((1 - lpeg.S"()") + lpeg.V(1))^0 * ")" }
+</pre>
+<p>
+Reading the first (and only) rule of the given grammar,
+we have that a balanced string is
+an open parenthesis,
+followed by zero or more repetitions of either
+a non-parenthesis character or
+a balanced string (<code>lpeg.V(1)</code>),
+followed by a closing parenthesis.
+</p>
+
+
+<h3>Global substitution</h3>
+<p>
+The next example does a job somewhat similar to <code>string.gsub</code>.
+It receives a pattern and a replacement value,
+and substitutes the replacement value for all occurrences of the pattern
+in a given string:
+</p>
+<pre class="example">
+function gsub (s, patt, repl)
+ patt = lpeg.P(patt)
+ patt = lpeg.Cs((patt / repl + 1)^0)
+ return lpeg.match(patt, s)
+end
+</pre>
+<p>
+As in <code>string.gsub</code>,
+the replacement value can be a string,
+a function, or a table.
+</p>
+
+
+<h3><a name="CSV"></a>Comma-Separated Values (CSV)</h3>
+<p>
+This example breaks a string into comma-separated values,
+returning all fields:
+</p>
+<pre class="example">
+local field = '"' * lpeg.Cs(((lpeg.P(1) - '"') + lpeg.P'""' / '"')^0) * '"' +
+ lpeg.C((1 - lpeg.S',\n"')^0)
+
+local record = field * (',' * field)^0 * (lpeg.P'\n' + -1)
+
+function csv (s)
+ return lpeg.match(record, s)
+end
+</pre>
+<p>
+A field is either a quoted field
+(which may contain any character except an individual quote,
+which may be written as two quotes that are replaced by one)
+or an unquoted field
+(which cannot contain commas, newlines, or quotes).
+A record is a list of fields separated by commas,
+ending with a newline or the string end (-1).
+</p>
+
+<p>
+As it is,
+the previous pattern returns each field as a separated result.
+If we add a table capture in the definition of <code>record</code>,
+the pattern will return instead a single table
+containing all fields:
+</p>
+<pre>
+local record = lpeg.Ct(field * (',' * field)^0) * (lpeg.P'\n' + -1)
+</pre>
+
+
+<h3>UTF-8 and Latin 1</h3>
+<p>
+It is not difficult to use LPeg to convert a string from
+UTF-8 encoding to Latin 1 (ISO 8859-1):
+</p>
+
+<pre class="example">
+-- convert a two-byte UTF-8 sequence to a Latin 1 character
+local function f2 (s)
+ local c1, c2 = string.byte(s, 1, 2)
+ return string.char(c1 * 64 + c2 - 12416)
+end
+
+local utf8 = lpeg.R("\0\127")
+ + lpeg.R("\194\195") * lpeg.R("\128\191") / f2
+
+local decode_pattern = lpeg.Cs(utf8^0) * -1
+</pre>
+<p>
+In this code,
+the definition of UTF-8 is already restricted to the
+Latin 1 range (from 0 to 255).
+Any encoding outside this range (as well as any invalid encoding)
+will not match that pattern.
+</p>
+
+<p>
+As the definition of <code>decode_pattern</code> demands that
+the pattern matches the whole input (because of the -1 at its end),
+any invalid string will simply fail to match,
+without any useful information about the problem.
+We can improve this situation redefining <code>decode_pattern</code>
+as follows:
+</p>
+<pre class="example">
+local function er (_, i) error("invalid encoding at position " .. i) end
+
+local decode_pattern = lpeg.Cs(utf8^0) * (-1 + lpeg.P(er))
+</pre>
+<p>
+Now, if the pattern <code>utf8^0</code> stops
+before the end of the string,
+an appropriate error function is called.
+</p>
+
+
+<h3>UTF-8 and Unicode</h3>
+<p>
+We can extend the previous patterns to handle all Unicode code points.
+Of course,
+we cannot translate them to Latin 1 or any other one-byte encoding.
+Instead, our translation results in a array with the code points
+represented as numbers.
+The full code is here:
+</p>
+<pre class="example">
+-- decode a two-byte UTF-8 sequence
+local function f2 (s)
+ local c1, c2 = string.byte(s, 1, 2)
+ return c1 * 64 + c2 - 12416
+end
+
+-- decode a three-byte UTF-8 sequence
+local function f3 (s)
+ local c1, c2, c3 = string.byte(s, 1, 3)
+ return (c1 * 64 + c2) * 64 + c3 - 925824
+end
+
+-- decode a four-byte UTF-8 sequence
+local function f4 (s)
+ local c1, c2, c3, c4 = string.byte(s, 1, 4)
+ return ((c1 * 64 + c2) * 64 + c3) * 64 + c4 - 63447168
+end
+
+local cont = lpeg.R("\128\191") -- continuation byte
+
+local utf8 = lpeg.R("\0\127") / string.byte
+ + lpeg.R("\194\223") * cont / f2
+ + lpeg.R("\224\239") * cont * cont / f3
+ + lpeg.R("\240\244") * cont * cont * cont / f4
+
+local decode_pattern = lpeg.Ct(utf8^0) * -1
+</pre>
+
+
+<h3>Lua's long strings</h3>
+<p>
+A long string in Lua starts with the pattern <code>[=*[</code>
+and ends at the first occurrence of <code>]=*]</code> with
+exactly the same number of equal signs.
+If the opening brackets are followed by a newline,
+this newline is discarded
+(that is, it is not part of the string).
+</p>
+
+<p>
+To match a long string in Lua,
+the pattern must capture the first repetition of equal signs and then,
+whenever it finds a candidate for closing the string,
+check whether it has the same number of equal signs.
+</p>
+
+<pre class="example">
+equals = lpeg.P"="^0
+open = "[" * lpeg.Cg(equals, "init") * "[" * lpeg.P"\n"^-1
+close = "]" * lpeg.C(equals) * "]"
+closeeq = lpeg.Cmt(close * lpeg.Cb("init"), function (s, i, a, b) return a == b end)
+string = open * lpeg.C((lpeg.P(1) - closeeq)^0) * close / 1
+</pre>
+
+<p>
+The <code>open</code> pattern matches <code>[=*[</code>,
+capturing the repetitions of equal signs in a group named <code>init</code>;
+it also discharges an optional newline, if present.
+The <code>close</code> pattern matches <code>]=*]</code>,
+also capturing the repetitions of equal signs.
+The <code>closeeq</code> pattern first matches <code>close</code>;
+then it uses a back capture to recover the capture made
+by the previous <code>open</code>,
+which is named <code>init</code>;
+finally it uses a match-time capture to check
+whether both captures are equal.
+The <code>string</code> pattern starts with an <code>open</code>,
+then it goes as far as possible until matching <code>closeeq</code>,
+and then matches the final <code>close</code>.
+The final numbered capture simply discards
+the capture made by <code>close</code>.
+</p>
+
+
+<h3>Arithmetic expressions</h3>
+<p>
+This example is a complete parser and evaluator for simple
+arithmetic expressions.
+We write it in two styles.
+The first approach first builds a syntax tree and then
+traverses this tree to compute the expression value:
+</p>
+<pre class="example">
+-- Lexical Elements
+local Space = lpeg.S(" \n\t")^0
+local Number = lpeg.C(lpeg.P"-"^-1 * lpeg.R("09")^1) * Space
+local TermOp = lpeg.C(lpeg.S("+-")) * Space
+local FactorOp = lpeg.C(lpeg.S("*/")) * Space
+local Open = "(" * Space
+local Close = ")" * Space
+
+-- Grammar
+local Exp, Term, Factor = lpeg.V"Exp", lpeg.V"Term", lpeg.V"Factor"
+G = lpeg.P{ Exp,
+ Exp = lpeg.Ct(Term * (TermOp * Term)^0);
+ Term = lpeg.Ct(Factor * (FactorOp * Factor)^0);
+ Factor = Number + Open * Exp * Close;
+}
+
+G = Space * G * -1
+
+-- Evaluator
+function eval (x)
+ if type(x) == "string" then
+ return tonumber(x)
+ else
+ local op1 = eval(x[1])
+ for i = 2, #x, 2 do
+ local op = x[i]
+ local op2 = eval(x[i + 1])
+ if (op == "+") then op1 = op1 + op2
+ elseif (op == "-") then op1 = op1 - op2
+ elseif (op == "*") then op1 = op1 * op2
+ elseif (op == "/") then op1 = op1 / op2
+ end
+ end
+ return op1
+ end
+end
+
+-- Parser/Evaluator
+function evalExp (s)
+ local t = lpeg.match(G, s)
+ if not t then error("syntax error", 2) end
+ return eval(t)
+end
+
+-- small example
+print(evalExp"3 + 5*9 / (1+1) - 12") --> 13.5
+</pre>
+
+<p>
+The second style computes the expression value on the fly,
+without building the syntax tree.
+The following grammar takes this approach.
+(It assumes the same lexical elements as before.)
+</p>
+<pre class="example">
+-- Auxiliary function
+function eval (v1, op, v2)
+ if (op == "+") then return v1 + v2
+ elseif (op == "-") then return v1 - v2
+ elseif (op == "*") then return v1 * v2
+ elseif (op == "/") then return v1 / v2
+ end
+end
+
+-- Grammar
+local V = lpeg.V
+G = lpeg.P{ "Exp",
+ Exp = lpeg.Cf(V"Term" * lpeg.Cg(TermOp * V"Term")^0, eval);
+ Term = lpeg.Cf(V"Factor" * lpeg.Cg(FactorOp * V"Factor")^0, eval);
+ Factor = Number / tonumber + Open * V"Exp" * Close;
+}
+
+-- small example
+print(lpeg.match(G, "3 + 5*9 / (1+1) - 12")) --> 13.5
+</pre>
+<p>
+Note the use of the fold (accumulator) capture.
+To compute the value of an expression,
+the accumulator starts with the value of the first term,
+and then applies <code>eval</code> over
+the accumulator, the operator,
+and the new term for each repetition.
+</p>
+
+
+
+<h2><a name="download"></a>Download</h2>
+
+<p>LPeg
+<a href="http://www.inf.puc-rio.br/~roberto/lpeg/lpeg-1.0.1.tar.gz">source code</a>.</p>
+
+
+<h2><a name="license">License</a></h2>
+
+<p>
+Copyright &copy; 2007-2017 Lua.org, PUC-Rio.
+</p>
+<p>
+Permission is hereby granted, free of charge,
+to any person obtaining a copy of this software and
+associated documentation files (the "Software"),
+to deal in the Software without restriction,
+including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software,
+and to permit persons to whom the Software is
+furnished to do so,
+subject to the following conditions:
+</p>
+
+<p>
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions of the Software.
+</p>
+
+<p>
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+</p>
+
+</div> <!-- id="content" -->
+
+</div> <!-- id="main" -->
+
+<div id="about">
+<p><small>
+$Id: lpeg.html,v 1.77 2017/01/13 13:40:05 roberto Exp $
+</small></p>
+</div> <!-- id="about" -->
+
+</div> <!-- id="container" -->
+
+</body>
+</html>
diff --git a/lpeg-1.0.1/lpprint.c b/lpeg-1.0.1/lpprint.c
new file mode 100644
index 0000000..f7be408
--- /dev/null
+++ b/lpeg-1.0.1/lpprint.c
@@ -0,0 +1,244 @@
+/*
+** $Id: lpprint.c,v 1.10 2016/09/13 16:06:03 roberto Exp $
+** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
+*/
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+
+
+#include "lptypes.h"
+#include "lpprint.h"
+#include "lpcode.h"
+
+
+#if defined(LPEG_DEBUG)
+
+/*
+** {======================================================
+** Printing patterns (for debugging)
+** =======================================================
+*/
+
+
+void printcharset (const byte *st) {
+ int i;
+ printf("[");
+ for (i = 0; i <= UCHAR_MAX; i++) {
+ int first = i;
+ while (testchar(st, i) && i <= UCHAR_MAX) i++;
+ if (i - 1 == first) /* unary range? */
+ printf("(%02x)", first);
+ else if (i - 1 > first) /* non-empty range? */
+ printf("(%02x-%02x)", first, i - 1);
+ }
+ printf("]");
+}
+
+
+static const char *capkind (int kind) {
+ const char *const modes[] = {
+ "close", "position", "constant", "backref",
+ "argument", "simple", "table", "function",
+ "query", "string", "num", "substitution", "fold",
+ "runtime", "group"};
+ return modes[kind];
+}
+
+
+static void printjmp (const Instruction *op, const Instruction *p) {
+ printf("-> %d", (int)(p + (p + 1)->offset - op));
+}
+
+
+void printinst (const Instruction *op, const Instruction *p) {
+ const char *const names[] = {
+ "any", "char", "set",
+ "testany", "testchar", "testset",
+ "span", "behind",
+ "ret", "end",
+ "choice", "jmp", "call", "open_call",
+ "commit", "partial_commit", "back_commit", "failtwice", "fail", "giveup",
+ "fullcapture", "opencapture", "closecapture", "closeruntime"
+ };
+ printf("%02ld: %s ", (long)(p - op), names[p->i.code]);
+ switch ((Opcode)p->i.code) {
+ case IChar: {
+ printf("'%c'", p->i.aux);
+ break;
+ }
+ case ITestChar: {
+ printf("'%c'", p->i.aux); printjmp(op, p);
+ break;
+ }
+ case IFullCapture: {
+ printf("%s (size = %d) (idx = %d)",
+ capkind(getkind(p)), getoff(p), p->i.key);
+ break;
+ }
+ case IOpenCapture: {
+ printf("%s (idx = %d)", capkind(getkind(p)), p->i.key);
+ break;
+ }
+ case ISet: {
+ printcharset((p+1)->buff);
+ break;
+ }
+ case ITestSet: {
+ printcharset((p+2)->buff); printjmp(op, p);
+ break;
+ }
+ case ISpan: {
+ printcharset((p+1)->buff);
+ break;
+ }
+ case IOpenCall: {
+ printf("-> %d", (p + 1)->offset);
+ break;
+ }
+ case IBehind: {
+ printf("%d", p->i.aux);
+ break;
+ }
+ case IJmp: case ICall: case ICommit: case IChoice:
+ case IPartialCommit: case IBackCommit: case ITestAny: {
+ printjmp(op, p);
+ break;
+ }
+ default: break;
+ }
+ printf("\n");
+}
+
+
+void printpatt (Instruction *p, int n) {
+ Instruction *op = p;
+ while (p < op + n) {
+ printinst(op, p);
+ p += sizei(p);
+ }
+}
+
+
+#if defined(LPEG_DEBUG)
+static void printcap (Capture *cap) {
+ printf("%s (idx: %d - size: %d) -> %p\n",
+ capkind(cap->kind), cap->idx, cap->siz, cap->s);
+}
+
+
+void printcaplist (Capture *cap, Capture *limit) {
+ printf(">======\n");
+ for (; cap->s && (limit == NULL || cap < limit); cap++)
+ printcap(cap);
+ printf("=======\n");
+}
+#endif
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Printing trees (for debugging)
+** =======================================================
+*/
+
+static const char *tagnames[] = {
+ "char", "set", "any",
+ "true", "false",
+ "rep",
+ "seq", "choice",
+ "not", "and",
+ "call", "opencall", "rule", "grammar",
+ "behind",
+ "capture", "run-time"
+};
+
+
+void printtree (TTree *tree, int ident) {
+ int i;
+ for (i = 0; i < ident; i++) printf(" ");
+ printf("%s", tagnames[tree->tag]);
+ switch (tree->tag) {
+ case TChar: {
+ int c = tree->u.n;
+ if (isprint(c))
+ printf(" '%c'\n", c);
+ else
+ printf(" (%02X)\n", c);
+ break;
+ }
+ case TSet: {
+ printcharset(treebuffer(tree));
+ printf("\n");
+ break;
+ }
+ case TOpenCall: case TCall: {
+ assert(sib2(tree)->tag == TRule);
+ printf(" key: %d (rule: %d)\n", tree->key, sib2(tree)->cap);
+ break;
+ }
+ case TBehind: {
+ printf(" %d\n", tree->u.n);
+ printtree(sib1(tree), ident + 2);
+ break;
+ }
+ case TCapture: {
+ printf(" kind: '%s' key: %d\n", capkind(tree->cap), tree->key);
+ printtree(sib1(tree), ident + 2);
+ break;
+ }
+ case TRule: {
+ printf(" n: %d key: %d\n", tree->cap, tree->key);
+ printtree(sib1(tree), ident + 2);
+ break; /* do not print next rule as a sibling */
+ }
+ case TGrammar: {
+ TTree *rule = sib1(tree);
+ printf(" %d\n", tree->u.n); /* number of rules */
+ for (i = 0; i < tree->u.n; i++) {
+ printtree(rule, ident + 2);
+ rule = sib2(rule);
+ }
+ assert(rule->tag == TTrue); /* sentinel */
+ break;
+ }
+ default: {
+ int sibs = numsiblings[tree->tag];
+ printf("\n");
+ if (sibs >= 1) {
+ printtree(sib1(tree), ident + 2);
+ if (sibs >= 2)
+ printtree(sib2(tree), ident + 2);
+ }
+ break;
+ }
+ }
+}
+
+
+void printktable (lua_State *L, int idx) {
+ int n, i;
+ lua_getuservalue(L, idx);
+ if (lua_isnil(L, -1)) /* no ktable? */
+ return;
+ n = lua_rawlen(L, -1);
+ printf("[");
+ for (i = 1; i <= n; i++) {
+ printf("%d = ", i);
+ lua_rawgeti(L, -1, i);
+ if (lua_isstring(L, -1))
+ printf("%s ", lua_tostring(L, -1));
+ else
+ printf("%s ", lua_typename(L, lua_type(L, -1)));
+ lua_pop(L, 1);
+ }
+ printf("]\n");
+ /* leave ktable at the stack */
+}
+
+/* }====================================================== */
+
+#endif
diff --git a/lpeg-1.0.1/lpprint.h b/lpeg-1.0.1/lpprint.h
new file mode 100644
index 0000000..6329760
--- /dev/null
+++ b/lpeg-1.0.1/lpprint.h
@@ -0,0 +1,36 @@
+/*
+** $Id: lpprint.h,v 1.2 2015/06/12 18:18:08 roberto Exp $
+*/
+
+
+#if !defined(lpprint_h)
+#define lpprint_h
+
+
+#include "lptree.h"
+#include "lpvm.h"
+
+
+#if defined(LPEG_DEBUG)
+
+void printpatt (Instruction *p, int n);
+void printtree (TTree *tree, int ident);
+void printktable (lua_State *L, int idx);
+void printcharset (const byte *st);
+void printcaplist (Capture *cap, Capture *limit);
+void printinst (const Instruction *op, const Instruction *p);
+
+#else
+
+#define printktable(L,idx) \
+ luaL_error(L, "function only implemented in debug mode")
+#define printtree(tree,i) \
+ luaL_error(L, "function only implemented in debug mode")
+#define printpatt(p,n) \
+ luaL_error(L, "function only implemented in debug mode")
+
+#endif
+
+
+#endif
+
diff --git a/lpeg-1.0.1/lptree.c b/lpeg-1.0.1/lptree.c
new file mode 100644
index 0000000..bda61b9
--- /dev/null
+++ b/lpeg-1.0.1/lptree.c
@@ -0,0 +1,1303 @@
+/*
+** $Id: lptree.c,v 1.22 2016/09/13 18:10:22 roberto Exp $
+** Copyright 2013, Lua.org & PUC-Rio (see 'lpeg.html' for license)
+*/
+
+#include <ctype.h>
+#include <limits.h>
+#include <string.h>
+
+
+#include "lua.h"
+#include "lauxlib.h"
+
+#include "lptypes.h"
+#include "lpcap.h"
+#include "lpcode.h"
+#include "lpprint.h"
+#include "lptree.h"
+
+
+/* number of siblings for each tree */
+const byte numsiblings[] = {
+ 0, 0, 0, /* char, set, any */
+ 0, 0, /* true, false */
+ 1, /* rep */
+ 2, 2, /* seq, choice */
+ 1, 1, /* not, and */
+ 0, 0, 2, 1, /* call, opencall, rule, grammar */
+ 1, /* behind */
+ 1, 1 /* capture, runtime capture */
+};
+
+
+static TTree *newgrammar (lua_State *L, int arg);
+
+
+/*
+** returns a reasonable name for value at index 'idx' on the stack
+*/
+static const char *val2str (lua_State *L, int idx) {
+ const char *k = lua_tostring(L, idx);
+ if (k != NULL)
+ return lua_pushfstring(L, "%s", k);
+ else
+ return lua_pushfstring(L, "(a %s)", luaL_typename(L, idx));
+}
+
+
+/*
+** Fix a TOpenCall into a TCall node, using table 'postable' to
+** translate a key to its rule address in the tree. Raises an
+** error if key does not exist.
+*/
+static void fixonecall (lua_State *L, int postable, TTree *g, TTree *t) {
+ int n;
+ lua_rawgeti(L, -1, t->key); /* get rule's name */
+ lua_gettable(L, postable); /* query name in position table */
+ n = lua_tonumber(L, -1); /* get (absolute) position */
+ lua_pop(L, 1); /* remove position */
+ if (n == 0) { /* no position? */
+ lua_rawgeti(L, -1, t->key); /* get rule's name again */
+ luaL_error(L, "rule '%s' undefined in given grammar", val2str(L, -1));
+ }
+ t->tag = TCall;
+ t->u.ps = n - (t - g); /* position relative to node */
+ assert(sib2(t)->tag == TRule);
+ sib2(t)->key = t->key; /* fix rule's key */
+}
+
+
+/*
+** Transform left associative constructions into right
+** associative ones, for sequence and choice; that is:
+** (t11 + t12) + t2 => t11 + (t12 + t2)
+** (t11 * t12) * t2 => t11 * (t12 * t2)
+** (that is, Op (Op t11 t12) t2 => Op t11 (Op t12 t2))
+*/
+static void correctassociativity (TTree *tree) {
+ TTree *t1 = sib1(tree);
+ assert(tree->tag == TChoice || tree->tag == TSeq);
+ while (t1->tag == tree->tag) {
+ int n1size = tree->u.ps - 1; /* t1 == Op t11 t12 */
+ int n11size = t1->u.ps - 1;
+ int n12size = n1size - n11size - 1;
+ memmove(sib1(tree), sib1(t1), n11size * sizeof(TTree)); /* move t11 */
+ tree->u.ps = n11size + 1;
+ sib2(tree)->tag = tree->tag;
+ sib2(tree)->u.ps = n12size + 1;
+ }
+}
+
+
+/*
+** Make final adjustments in a tree. Fix open calls in tree 't',
+** making them refer to their respective rules or raising appropriate
+** errors (if not inside a grammar). Correct associativity of associative
+** constructions (making them right associative). Assume that tree's
+** ktable is at the top of the stack (for error messages).
+*/
+static void finalfix (lua_State *L, int postable, TTree *g, TTree *t) {
+ tailcall:
+ switch (t->tag) {
+ case TGrammar: /* subgrammars were already fixed */
+ return;
+ case TOpenCall: {
+ if (g != NULL) /* inside a grammar? */
+ fixonecall(L, postable, g, t);
+ else { /* open call outside grammar */
+ lua_rawgeti(L, -1, t->key);
+ luaL_error(L, "rule '%s' used outside a grammar", val2str(L, -1));
+ }
+ break;
+ }
+ case TSeq: case TChoice:
+ correctassociativity(t);
+ break;
+ }
+ switch (numsiblings[t->tag]) {
+ case 1: /* finalfix(L, postable, g, sib1(t)); */
+ t = sib1(t); goto tailcall;
+ case 2:
+ finalfix(L, postable, g, sib1(t));
+ t = sib2(t); goto tailcall; /* finalfix(L, postable, g, sib2(t)); */
+ default: assert(numsiblings[t->tag] == 0); break;
+ }
+}
+
+
+
+/*
+** {===================================================================
+** KTable manipulation
+**
+** - The ktable of a pattern 'p' can be shared by other patterns that
+** contain 'p' and no other constants. Because of this sharing, we
+** should not add elements to a 'ktable' unless it was freshly created
+** for the new pattern.
+**
+** - The maximum index in a ktable is USHRT_MAX, because trees and
+** patterns use unsigned shorts to store those indices.
+** ====================================================================
+*/
+
+/*
+** Create a new 'ktable' to the pattern at the top of the stack.
+*/
+static void newktable (lua_State *L, int n) {
+ lua_createtable(L, n, 0); /* create a fresh table */
+ lua_setuservalue(L, -2); /* set it as 'ktable' for pattern */
+}
+
+
+/*
+** Add element 'idx' to 'ktable' of pattern at the top of the stack;
+** Return index of new element.
+** If new element is nil, does not add it to table (as it would be
+** useless) and returns 0, as ktable[0] is always nil.
+*/
+static int addtoktable (lua_State *L, int idx) {
+ if (lua_isnil(L, idx)) /* nil value? */
+ return 0;
+ else {
+ int n;
+ lua_getuservalue(L, -1); /* get ktable from pattern */
+ n = lua_rawlen(L, -1);
+ if (n >= USHRT_MAX)
+ luaL_error(L, "too many Lua values in pattern");
+ lua_pushvalue(L, idx); /* element to be added */
+ lua_rawseti(L, -2, ++n);
+ lua_pop(L, 1); /* remove 'ktable' */
+ return n;
+ }
+}
+
+
+/*
+** Return the number of elements in the ktable at 'idx'.
+** In Lua 5.2/5.3, default "environment" for patterns is nil, not
+** a table. Treat it as an empty table. In Lua 5.1, assumes that
+** the environment has no numeric indices (len == 0)
+*/
+static int ktablelen (lua_State *L, int idx) {
+ if (!lua_istable(L, idx)) return 0;
+ else return lua_rawlen(L, idx);
+}
+
+
+/*
+** Concatentate the contents of table 'idx1' into table 'idx2'.
+** (Assume that both indices are negative.)
+** Return the original length of table 'idx2' (or 0, if no
+** element was added, as there is no need to correct any index).
+*/
+static int concattable (lua_State *L, int idx1, int idx2) {
+ int i;
+ int n1 = ktablelen(L, idx1);
+ int n2 = ktablelen(L, idx2);
+ if (n1 + n2 > USHRT_MAX)
+ luaL_error(L, "too many Lua values in pattern");
+ if (n1 == 0) return 0; /* nothing to correct */
+ for (i = 1; i <= n1; i++) {
+ lua_rawgeti(L, idx1, i);
+ lua_rawseti(L, idx2 - 1, n2 + i); /* correct 'idx2' */
+ }
+ return n2;
+}
+
+
+/*
+** When joining 'ktables', constants from one of the subpatterns must
+** be renumbered; 'correctkeys' corrects their indices (adding 'n'
+** to each of them)
+*/
+static void correctkeys (TTree *tree, int n) {
+ if (n == 0) return; /* no correction? */
+ tailcall:
+ switch (tree->tag) {
+ case TOpenCall: case TCall: case TRunTime: case TRule: {
+ if (tree->key > 0)
+ tree->key += n;
+ break;
+ }
+ case TCapture: {
+ if (tree->key > 0 && tree->cap != Carg && tree->cap != Cnum)
+ tree->key += n;
+ break;
+ }
+ default: break;
+ }
+ switch (numsiblings[tree->tag]) {
+ case 1: /* correctkeys(sib1(tree), n); */
+ tree = sib1(tree); goto tailcall;
+ case 2:
+ correctkeys(sib1(tree), n);
+ tree = sib2(tree); goto tailcall; /* correctkeys(sib2(tree), n); */
+ default: assert(numsiblings[tree->tag] == 0); break;
+ }
+}
+
+
+/*
+** Join the ktables from p1 and p2 the ktable for the new pattern at the
+** top of the stack, reusing them when possible.
+*/
+static void joinktables (lua_State *L, int p1, TTree *t2, int p2) {
+ int n1, n2;
+ lua_getuservalue(L, p1); /* get ktables */
+ lua_getuservalue(L, p2);
+ n1 = ktablelen(L, -2);
+ n2 = ktablelen(L, -1);
+ if (n1 == 0 && n2 == 0) /* are both tables empty? */
+ lua_pop(L, 2); /* nothing to be done; pop tables */
+ else if (n2 == 0 || lp_equal(L, -2, -1)) { /* 2nd table empty or equal? */
+ lua_pop(L, 1); /* pop 2nd table */
+ lua_setuservalue(L, -2); /* set 1st ktable into new pattern */
+ }
+ else if (n1 == 0) { /* first table is empty? */
+ lua_setuservalue(L, -3); /* set 2nd table into new pattern */
+ lua_pop(L, 1); /* pop 1st table */
+ }
+ else {
+ lua_createtable(L, n1 + n2, 0); /* create ktable for new pattern */
+ /* stack: new p; ktable p1; ktable p2; new ktable */
+ concattable(L, -3, -1); /* from p1 into new ktable */
+ concattable(L, -2, -1); /* from p2 into new ktable */
+ lua_setuservalue(L, -4); /* new ktable becomes 'p' environment */
+ lua_pop(L, 2); /* pop other ktables */
+ correctkeys(t2, n1); /* correction for indices from p2 */
+ }
+}
+
+
+/*
+** copy 'ktable' of element 'idx' to new tree (on top of stack)
+*/
+static void copyktable (lua_State *L, int idx) {
+ lua_getuservalue(L, idx);
+ lua_setuservalue(L, -2);
+}
+
+
+/*
+** merge 'ktable' from 'stree' at stack index 'idx' into 'ktable'
+** from tree at the top of the stack, and correct corresponding
+** tree.
+*/
+static void mergektable (lua_State *L, int idx, TTree *stree) {
+ int n;
+ lua_getuservalue(L, -1); /* get ktables */
+ lua_getuservalue(L, idx);
+ n = concattable(L, -1, -2);
+ lua_pop(L, 2); /* remove both ktables */
+ correctkeys(stree, n);
+}
+
+
+/*
+** Create a new 'ktable' to the pattern at the top of the stack, adding
+** all elements from pattern 'p' (if not 0) plus element 'idx' to it.
+** Return index of new element.
+*/
+static int addtonewktable (lua_State *L, int p, int idx) {
+ newktable(L, 1);
+ if (p)
+ mergektable(L, p, NULL);
+ return addtoktable(L, idx);
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Tree generation
+** =======================================================
+*/
+
+/*
+** In 5.2, could use 'luaL_testudata'...
+*/
+static int testpattern (lua_State *L, int idx) {
+ if (lua_touserdata(L, idx)) { /* value is a userdata? */
+ if (lua_getmetatable(L, idx)) { /* does it have a metatable? */
+ luaL_getmetatable(L, PATTERN_T);
+ if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */
+ lua_pop(L, 2); /* remove both metatables */
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+static Pattern *getpattern (lua_State *L, int idx) {
+ return (Pattern *)luaL_checkudata(L, idx, PATTERN_T);
+}
+
+
+static int getsize (lua_State *L, int idx) {
+ return (lua_rawlen(L, idx) - sizeof(Pattern)) / sizeof(TTree) + 1;
+}
+
+
+static TTree *gettree (lua_State *L, int idx, int *len) {
+ Pattern *p = getpattern(L, idx);
+ if (len)
+ *len = getsize(L, idx);
+ return p->tree;
+}
+
+
+/*
+** create a pattern. Set its uservalue (the 'ktable') equal to its
+** metatable. (It could be any empty sequence; the metatable is at
+** hand here, so we use it.)
+*/
+static TTree *newtree (lua_State *L, int len) {
+ size_t size = (len - 1) * sizeof(TTree) + sizeof(Pattern);
+ Pattern *p = (Pattern *)lua_newuserdata(L, size);
+ luaL_getmetatable(L, PATTERN_T);
+ lua_pushvalue(L, -1);
+ lua_setuservalue(L, -3);
+ lua_setmetatable(L, -2);
+ p->code = NULL; p->codesize = 0;
+ return p->tree;
+}
+
+
+static TTree *newleaf (lua_State *L, int tag) {
+ TTree *tree = newtree(L, 1);
+ tree->tag = tag;
+ return tree;
+}
+
+
+static TTree *newcharset (lua_State *L) {
+ TTree *tree = newtree(L, bytes2slots(CHARSETSIZE) + 1);
+ tree->tag = TSet;
+ loopset(i, treebuffer(tree)[i] = 0);
+ return tree;
+}
+
+
+/*
+** add to tree a sequence where first sibling is 'sib' (with size
+** 'sibsize'); returns position for second sibling
+*/
+static TTree *seqaux (TTree *tree, TTree *sib, int sibsize) {
+ tree->tag = TSeq; tree->u.ps = sibsize + 1;
+ memcpy(sib1(tree), sib, sibsize * sizeof(TTree));
+ return sib2(tree);
+}
+
+
+/*
+** Build a sequence of 'n' nodes, each with tag 'tag' and 'u.n' got
+** from the array 's' (or 0 if array is NULL). (TSeq is binary, so it
+** must build a sequence of sequence of sequence...)
+*/
+static void fillseq (TTree *tree, int tag, int n, const char *s) {
+ int i;
+ for (i = 0; i < n - 1; i++) { /* initial n-1 copies of Seq tag; Seq ... */
+ tree->tag = TSeq; tree->u.ps = 2;
+ sib1(tree)->tag = tag;
+ sib1(tree)->u.n = s ? (byte)s[i] : 0;
+ tree = sib2(tree);
+ }
+ tree->tag = tag; /* last one does not need TSeq */
+ tree->u.n = s ? (byte)s[i] : 0;
+}
+
+
+/*
+** Numbers as patterns:
+** 0 == true (always match); n == TAny repeated 'n' times;
+** -n == not (TAny repeated 'n' times)
+*/
+static TTree *numtree (lua_State *L, int n) {
+ if (n == 0)
+ return newleaf(L, TTrue);
+ else {
+ TTree *tree, *nd;
+ if (n > 0)
+ tree = nd = newtree(L, 2 * n - 1);
+ else { /* negative: code it as !(-n) */
+ n = -n;
+ tree = newtree(L, 2 * n);
+ tree->tag = TNot;
+ nd = sib1(tree);
+ }
+ fillseq(nd, TAny, n, NULL); /* sequence of 'n' any's */
+ return tree;
+ }
+}
+
+
+/*
+** Convert value at index 'idx' to a pattern
+*/
+static TTree *getpatt (lua_State *L, int idx, int *len) {
+ TTree *tree;
+ switch (lua_type(L, idx)) {
+ case LUA_TSTRING: {
+ size_t slen;
+ const char *s = lua_tolstring(L, idx, &slen); /* get string */
+ if (slen == 0) /* empty? */
+ tree = newleaf(L, TTrue); /* always match */
+ else {
+ tree = newtree(L, 2 * (slen - 1) + 1);
+ fillseq(tree, TChar, slen, s); /* sequence of 'slen' chars */
+ }
+ break;
+ }
+ case LUA_TNUMBER: {
+ int n = lua_tointeger(L, idx);
+ tree = numtree(L, n);
+ break;
+ }
+ case LUA_TBOOLEAN: {
+ tree = (lua_toboolean(L, idx) ? newleaf(L, TTrue) : newleaf(L, TFalse));
+ break;
+ }
+ case LUA_TTABLE: {
+ tree = newgrammar(L, idx);
+ break;
+ }
+ case LUA_TFUNCTION: {
+ tree = newtree(L, 2);
+ tree->tag = TRunTime;
+ tree->key = addtonewktable(L, 0, idx);
+ sib1(tree)->tag = TTrue;
+ break;
+ }
+ default: {
+ return gettree(L, idx, len);
+ }
+ }
+ lua_replace(L, idx); /* put new tree into 'idx' slot */
+ if (len)
+ *len = getsize(L, idx);
+ return tree;
+}
+
+
+/*
+** create a new tree, whith a new root and one sibling.
+** Sibling must be on the Lua stack, at index 1.
+*/
+static TTree *newroot1sib (lua_State *L, int tag) {
+ int s1;
+ TTree *tree1 = getpatt(L, 1, &s1);
+ TTree *tree = newtree(L, 1 + s1); /* create new tree */
+ tree->tag = tag;
+ memcpy(sib1(tree), tree1, s1 * sizeof(TTree));
+ copyktable(L, 1);
+ return tree;
+}
+
+
+/*
+** create a new tree, whith a new root and 2 siblings.
+** Siblings must be on the Lua stack, first one at index 1.
+*/
+static TTree *newroot2sib (lua_State *L, int tag) {
+ int s1, s2;
+ TTree *tree1 = getpatt(L, 1, &s1);
+ TTree *tree2 = getpatt(L, 2, &s2);
+ TTree *tree = newtree(L, 1 + s1 + s2); /* create new tree */
+ tree->tag = tag;
+ tree->u.ps = 1 + s1;
+ memcpy(sib1(tree), tree1, s1 * sizeof(TTree));
+ memcpy(sib2(tree), tree2, s2 * sizeof(TTree));
+ joinktables(L, 1, sib2(tree), 2);
+ return tree;
+}
+
+
+static int lp_P (lua_State *L) {
+ luaL_checkany(L, 1);
+ getpatt(L, 1, NULL);
+ lua_settop(L, 1);
+ return 1;
+}
+
+
+/*
+** sequence operator; optimizations:
+** false x => false, x true => x, true x => x
+** (cannot do x . false => false because x may have runtime captures)
+*/
+static int lp_seq (lua_State *L) {
+ TTree *tree1 = getpatt(L, 1, NULL);
+ TTree *tree2 = getpatt(L, 2, NULL);
+ if (tree1->tag == TFalse || tree2->tag == TTrue)
+ lua_pushvalue(L, 1); /* false . x == false, x . true = x */
+ else if (tree1->tag == TTrue)
+ lua_pushvalue(L, 2); /* true . x = x */
+ else
+ newroot2sib(L, TSeq);
+ return 1;
+}
+
+
+/*
+** choice operator; optimizations:
+** charset / charset => charset
+** true / x => true, x / false => x, false / x => x
+** (x / true is not equivalent to true)
+*/
+static int lp_choice (lua_State *L) {
+ Charset st1, st2;
+ TTree *t1 = getpatt(L, 1, NULL);
+ TTree *t2 = getpatt(L, 2, NULL);
+ if (tocharset(t1, &st1) && tocharset(t2, &st2)) {
+ TTree *t = newcharset(L);
+ loopset(i, treebuffer(t)[i] = st1.cs[i] | st2.cs[i]);
+ }
+ else if (nofail(t1) || t2->tag == TFalse)
+ lua_pushvalue(L, 1); /* true / x => true, x / false => x */
+ else if (t1->tag == TFalse)
+ lua_pushvalue(L, 2); /* false / x => x */
+ else
+ newroot2sib(L, TChoice);
+ return 1;
+}
+
+
+/*
+** p^n
+*/
+static int lp_star (lua_State *L) {
+ int size1;
+ int n = (int)luaL_checkinteger(L, 2);
+ TTree *tree1 = getpatt(L, 1, &size1);
+ if (n >= 0) { /* seq tree1 (seq tree1 ... (seq tree1 (rep tree1))) */
+ TTree *tree = newtree(L, (n + 1) * (size1 + 1));
+ if (nullable(tree1))
+ luaL_error(L, "loop body may accept empty string");
+ while (n--) /* repeat 'n' times */
+ tree = seqaux(tree, tree1, size1);
+ tree->tag = TRep;
+ memcpy(sib1(tree), tree1, size1 * sizeof(TTree));
+ }
+ else { /* choice (seq tree1 ... choice tree1 true ...) true */
+ TTree *tree;
+ n = -n;
+ /* size = (choice + seq + tree1 + true) * n, but the last has no seq */
+ tree = newtree(L, n * (size1 + 3) - 1);
+ for (; n > 1; n--) { /* repeat (n - 1) times */
+ tree->tag = TChoice; tree->u.ps = n * (size1 + 3) - 2;
+ sib2(tree)->tag = TTrue;
+ tree = sib1(tree);
+ tree = seqaux(tree, tree1, size1);
+ }
+ tree->tag = TChoice; tree->u.ps = size1 + 1;
+ sib2(tree)->tag = TTrue;
+ memcpy(sib1(tree), tree1, size1 * sizeof(TTree));
+ }
+ copyktable(L, 1);
+ return 1;
+}
+
+
+/*
+** #p == &p
+*/
+static int lp_and (lua_State *L) {
+ newroot1sib(L, TAnd);
+ return 1;
+}
+
+
+/*
+** -p == !p
+*/
+static int lp_not (lua_State *L) {
+ newroot1sib(L, TNot);
+ return 1;
+}
+
+
+/*
+** [t1 - t2] == Seq (Not t2) t1
+** If t1 and t2 are charsets, make their difference.
+*/
+static int lp_sub (lua_State *L) {
+ Charset st1, st2;
+ int s1, s2;
+ TTree *t1 = getpatt(L, 1, &s1);
+ TTree *t2 = getpatt(L, 2, &s2);
+ if (tocharset(t1, &st1) && tocharset(t2, &st2)) {
+ TTree *t = newcharset(L);
+ loopset(i, treebuffer(t)[i] = st1.cs[i] & ~st2.cs[i]);
+ }
+ else {
+ TTree *tree = newtree(L, 2 + s1 + s2);
+ tree->tag = TSeq; /* sequence of... */
+ tree->u.ps = 2 + s2;
+ sib1(tree)->tag = TNot; /* ...not... */
+ memcpy(sib1(sib1(tree)), t2, s2 * sizeof(TTree)); /* ...t2 */
+ memcpy(sib2(tree), t1, s1 * sizeof(TTree)); /* ... and t1 */
+ joinktables(L, 1, sib1(tree), 2);
+ }
+ return 1;
+}
+
+
+static int lp_set (lua_State *L) {
+ size_t l;
+ const char *s = luaL_checklstring(L, 1, &l);
+ TTree *tree = newcharset(L);
+ while (l--) {
+ setchar(treebuffer(tree), (byte)(*s));
+ s++;
+ }
+ return 1;
+}
+
+
+static int lp_range (lua_State *L) {
+ int arg;
+ int top = lua_gettop(L);
+ TTree *tree = newcharset(L);
+ for (arg = 1; arg <= top; arg++) {
+ int c;
+ size_t l;
+ const char *r = luaL_checklstring(L, arg, &l);
+ luaL_argcheck(L, l == 2, arg, "range must have two characters");
+ for (c = (byte)r[0]; c <= (byte)r[1]; c++)
+ setchar(treebuffer(tree), c);
+ }
+ return 1;
+}
+
+
+/*
+** Look-behind predicate
+*/
+static int lp_behind (lua_State *L) {
+ TTree *tree;
+ TTree *tree1 = getpatt(L, 1, NULL);
+ int n = fixedlen(tree1);
+ luaL_argcheck(L, n >= 0, 1, "pattern may not have fixed length");
+ luaL_argcheck(L, !hascaptures(tree1), 1, "pattern have captures");
+ luaL_argcheck(L, n <= MAXBEHIND, 1, "pattern too long to look behind");
+ tree = newroot1sib(L, TBehind);
+ tree->u.n = n;
+ return 1;
+}
+
+
+/*
+** Create a non-terminal
+*/
+static int lp_V (lua_State *L) {
+ TTree *tree = newleaf(L, TOpenCall);
+ luaL_argcheck(L, !lua_isnoneornil(L, 1), 1, "non-nil value expected");
+ tree->key = addtonewktable(L, 0, 1);
+ return 1;
+}
+
+
+/*
+** Create a tree for a non-empty capture, with a body and
+** optionally with an associated Lua value (at index 'labelidx' in the
+** stack)
+*/
+static int capture_aux (lua_State *L, int cap, int labelidx) {
+ TTree *tree = newroot1sib(L, TCapture);
+ tree->cap = cap;
+ tree->key = (labelidx == 0) ? 0 : addtonewktable(L, 1, labelidx);
+ return 1;
+}
+
+
+/*
+** Fill a tree with an empty capture, using an empty (TTrue) sibling.
+*/
+static TTree *auxemptycap (TTree *tree, int cap) {
+ tree->tag = TCapture;
+ tree->cap = cap;
+ sib1(tree)->tag = TTrue;
+ return tree;
+}
+
+
+/*
+** Create a tree for an empty capture
+*/
+static TTree *newemptycap (lua_State *L, int cap) {
+ return auxemptycap(newtree(L, 2), cap);
+}
+
+
+/*
+** Create a tree for an empty capture with an associated Lua value
+*/
+static TTree *newemptycapkey (lua_State *L, int cap, int idx) {
+ TTree *tree = auxemptycap(newtree(L, 2), cap);
+ tree->key = addtonewktable(L, 0, idx);
+ return tree;
+}
+
+
+/*
+** Captures with syntax p / v
+** (function capture, query capture, string capture, or number capture)
+*/
+static int lp_divcapture (lua_State *L) {
+ switch (lua_type(L, 2)) {
+ case LUA_TFUNCTION: return capture_aux(L, Cfunction, 2);
+ case LUA_TTABLE: return capture_aux(L, Cquery, 2);
+ case LUA_TSTRING: return capture_aux(L, Cstring, 2);
+ case LUA_TNUMBER: {
+ int n = lua_tointeger(L, 2);
+ TTree *tree = newroot1sib(L, TCapture);
+ luaL_argcheck(L, 0 <= n && n <= SHRT_MAX, 1, "invalid number");
+ tree->cap = Cnum;
+ tree->key = n;
+ return 1;
+ }
+ default: return luaL_argerror(L, 2, "invalid replacement value");
+ }
+}
+
+
+static int lp_substcapture (lua_State *L) {
+ return capture_aux(L, Csubst, 0);
+}
+
+
+static int lp_tablecapture (lua_State *L) {
+ return capture_aux(L, Ctable, 0);
+}
+
+
+static int lp_groupcapture (lua_State *L) {
+ if (lua_isnoneornil(L, 2))
+ return capture_aux(L, Cgroup, 0);
+ else
+ return capture_aux(L, Cgroup, 2);
+}
+
+
+static int lp_foldcapture (lua_State *L) {
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+ return capture_aux(L, Cfold, 2);
+}
+
+
+static int lp_simplecapture (lua_State *L) {
+ return capture_aux(L, Csimple, 0);
+}
+
+
+static int lp_poscapture (lua_State *L) {
+ newemptycap(L, Cposition);
+ return 1;
+}
+
+
+static int lp_argcapture (lua_State *L) {
+ int n = (int)luaL_checkinteger(L, 1);
+ TTree *tree = newemptycap(L, Carg);
+ tree->key = n;
+ luaL_argcheck(L, 0 < n && n <= SHRT_MAX, 1, "invalid argument index");
+ return 1;
+}
+
+
+static int lp_backref (lua_State *L) {
+ luaL_checkany(L, 1);
+ newemptycapkey(L, Cbackref, 1);
+ return 1;
+}
+
+
+/*
+** Constant capture
+*/
+static int lp_constcapture (lua_State *L) {
+ int i;
+ int n = lua_gettop(L); /* number of values */
+ if (n == 0) /* no values? */
+ newleaf(L, TTrue); /* no capture */
+ else if (n == 1)
+ newemptycapkey(L, Cconst, 1); /* single constant capture */
+ else { /* create a group capture with all values */
+ TTree *tree = newtree(L, 1 + 3 * (n - 1) + 2);
+ newktable(L, n); /* create a 'ktable' for new tree */
+ tree->tag = TCapture;
+ tree->cap = Cgroup;
+ tree->key = 0;
+ tree = sib1(tree);
+ for (i = 1; i <= n - 1; i++) {
+ tree->tag = TSeq;
+ tree->u.ps = 3; /* skip TCapture and its sibling */
+ auxemptycap(sib1(tree), Cconst);
+ sib1(tree)->key = addtoktable(L, i);
+ tree = sib2(tree);
+ }
+ auxemptycap(tree, Cconst);
+ tree->key = addtoktable(L, i);
+ }
+ return 1;
+}
+
+
+static int lp_matchtime (lua_State *L) {
+ TTree *tree;
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+ tree = newroot1sib(L, TRunTime);
+ tree->key = addtonewktable(L, 1, 2);
+ return 1;
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Grammar - Tree generation
+** =======================================================
+*/
+
+/*
+** push on the stack the index and the pattern for the
+** initial rule of grammar at index 'arg' in the stack;
+** also add that index into position table.
+*/
+static void getfirstrule (lua_State *L, int arg, int postab) {
+ lua_rawgeti(L, arg, 1); /* access first element */
+ if (lua_isstring(L, -1)) { /* is it the name of initial rule? */
+ lua_pushvalue(L, -1); /* duplicate it to use as key */
+ lua_gettable(L, arg); /* get associated rule */
+ }
+ else {
+ lua_pushinteger(L, 1); /* key for initial rule */
+ lua_insert(L, -2); /* put it before rule */
+ }
+ if (!testpattern(L, -1)) { /* initial rule not a pattern? */
+ if (lua_isnil(L, -1))
+ luaL_error(L, "grammar has no initial rule");
+ else
+ luaL_error(L, "initial rule '%s' is not a pattern", lua_tostring(L, -2));
+ }
+ lua_pushvalue(L, -2); /* push key */
+ lua_pushinteger(L, 1); /* push rule position (after TGrammar) */
+ lua_settable(L, postab); /* insert pair at position table */
+}
+
+/*
+** traverse grammar at index 'arg', pushing all its keys and patterns
+** into the stack. Create a new table (before all pairs key-pattern) to
+** collect all keys and their associated positions in the final tree
+** (the "position table").
+** Return the number of rules and (in 'totalsize') the total size
+** for the new tree.
+*/
+static int collectrules (lua_State *L, int arg, int *totalsize) {
+ int n = 1; /* to count number of rules */
+ int postab = lua_gettop(L) + 1; /* index of position table */
+ int size; /* accumulator for total size */
+ lua_newtable(L); /* create position table */
+ getfirstrule(L, arg, postab);
+ size = 2 + getsize(L, postab + 2); /* TGrammar + TRule + rule */
+ lua_pushnil(L); /* prepare to traverse grammar table */
+ while (lua_next(L, arg) != 0) {
+ if (lua_tonumber(L, -2) == 1 ||
+ lp_equal(L, -2, postab + 1)) { /* initial rule? */
+ lua_pop(L, 1); /* remove value (keep key for lua_next) */
+ continue;
+ }
+ if (!testpattern(L, -1)) /* value is not a pattern? */
+ luaL_error(L, "rule '%s' is not a pattern", val2str(L, -2));
+ luaL_checkstack(L, LUA_MINSTACK, "grammar has too many rules");
+ lua_pushvalue(L, -2); /* push key (to insert into position table) */
+ lua_pushinteger(L, size);
+ lua_settable(L, postab);
+ size += 1 + getsize(L, -1); /* update size */
+ lua_pushvalue(L, -2); /* push key (for next lua_next) */
+ n++;
+ }
+ *totalsize = size + 1; /* TTrue to finish list of rules */
+ return n;
+}
+
+
+static void buildgrammar (lua_State *L, TTree *grammar, int frule, int n) {
+ int i;
+ TTree *nd = sib1(grammar); /* auxiliary pointer to traverse the tree */
+ for (i = 0; i < n; i++) { /* add each rule into new tree */
+ int ridx = frule + 2*i + 1; /* index of i-th rule */
+ int rulesize;
+ TTree *rn = gettree(L, ridx, &rulesize);
+ nd->tag = TRule;
+ nd->key = 0; /* will be fixed when rule is used */
+ nd->cap = i; /* rule number */
+ nd->u.ps = rulesize + 1; /* point to next rule */
+ memcpy(sib1(nd), rn, rulesize * sizeof(TTree)); /* copy rule */
+ mergektable(L, ridx, sib1(nd)); /* merge its ktable into new one */
+ nd = sib2(nd); /* move to next rule */
+ }
+ nd->tag = TTrue; /* finish list of rules */
+}
+
+
+/*
+** Check whether a tree has potential infinite loops
+*/
+static int checkloops (TTree *tree) {
+ tailcall:
+ if (tree->tag == TRep && nullable(sib1(tree)))
+ return 1;
+ else if (tree->tag == TGrammar)
+ return 0; /* sub-grammars already checked */
+ else {
+ switch (numsiblings[tree->tag]) {
+ case 1: /* return checkloops(sib1(tree)); */
+ tree = sib1(tree); goto tailcall;
+ case 2:
+ if (checkloops(sib1(tree))) return 1;
+ /* else return checkloops(sib2(tree)); */
+ tree = sib2(tree); goto tailcall;
+ default: assert(numsiblings[tree->tag] == 0); return 0;
+ }
+ }
+}
+
+
+/*
+** Give appropriate error message for 'verifyrule'. If a rule appears
+** twice in 'passed', there is path from it back to itself without
+** advancing the subject.
+*/
+static int verifyerror (lua_State *L, int *passed, int npassed) {
+ int i, j;
+ for (i = npassed - 1; i >= 0; i--) { /* search for a repetition */
+ for (j = i - 1; j >= 0; j--) {
+ if (passed[i] == passed[j]) {
+ lua_rawgeti(L, -1, passed[i]); /* get rule's key */
+ return luaL_error(L, "rule '%s' may be left recursive", val2str(L, -1));
+ }
+ }
+ }
+ return luaL_error(L, "too many left calls in grammar");
+}
+
+
+/*
+** Check whether a rule can be left recursive; raise an error in that
+** case; otherwise return 1 iff pattern is nullable.
+** The return value is used to check sequences, where the second pattern
+** is only relevant if the first is nullable.
+** Parameter 'nb' works as an accumulator, to allow tail calls in
+** choices. ('nb' true makes function returns true.)
+** Parameter 'passed' is a list of already visited rules, 'npassed'
+** counts the elements in 'passed'.
+** Assume ktable at the top of the stack.
+*/
+static int verifyrule (lua_State *L, TTree *tree, int *passed, int npassed,
+ int nb) {
+ tailcall:
+ switch (tree->tag) {
+ case TChar: case TSet: case TAny:
+ case TFalse:
+ return nb; /* cannot pass from here */
+ case TTrue:
+ case TBehind: /* look-behind cannot have calls */
+ return 1;
+ case TNot: case TAnd: case TRep:
+ /* return verifyrule(L, sib1(tree), passed, npassed, 1); */
+ tree = sib1(tree); nb = 1; goto tailcall;
+ case TCapture: case TRunTime:
+ /* return verifyrule(L, sib1(tree), passed, npassed, nb); */
+ tree = sib1(tree); goto tailcall;
+ case TCall:
+ /* return verifyrule(L, sib2(tree), passed, npassed, nb); */
+ tree = sib2(tree); goto tailcall;
+ case TSeq: /* only check 2nd child if first is nb */
+ if (!verifyrule(L, sib1(tree), passed, npassed, 0))
+ return nb;
+ /* else return verifyrule(L, sib2(tree), passed, npassed, nb); */
+ tree = sib2(tree); goto tailcall;
+ case TChoice: /* must check both children */
+ nb = verifyrule(L, sib1(tree), passed, npassed, nb);
+ /* return verifyrule(L, sib2(tree), passed, npassed, nb); */
+ tree = sib2(tree); goto tailcall;
+ case TRule:
+ if (npassed >= MAXRULES)
+ return verifyerror(L, passed, npassed);
+ else {
+ passed[npassed++] = tree->key;
+ /* return verifyrule(L, sib1(tree), passed, npassed); */
+ tree = sib1(tree); goto tailcall;
+ }
+ case TGrammar:
+ return nullable(tree); /* sub-grammar cannot be left recursive */
+ default: assert(0); return 0;
+ }
+}
+
+
+static void verifygrammar (lua_State *L, TTree *grammar) {
+ int passed[MAXRULES];
+ TTree *rule;
+ /* check left-recursive rules */
+ for (rule = sib1(grammar); rule->tag == TRule; rule = sib2(rule)) {
+ if (rule->key == 0) continue; /* unused rule */
+ verifyrule(L, sib1(rule), passed, 0, 0);
+ }
+ assert(rule->tag == TTrue);
+ /* check infinite loops inside rules */
+ for (rule = sib1(grammar); rule->tag == TRule; rule = sib2(rule)) {
+ if (rule->key == 0) continue; /* unused rule */
+ if (checkloops(sib1(rule))) {
+ lua_rawgeti(L, -1, rule->key); /* get rule's key */
+ luaL_error(L, "empty loop in rule '%s'", val2str(L, -1));
+ }
+ }
+ assert(rule->tag == TTrue);
+}
+
+
+/*
+** Give a name for the initial rule if it is not referenced
+*/
+static void initialrulename (lua_State *L, TTree *grammar, int frule) {
+ if (sib1(grammar)->key == 0) { /* initial rule is not referenced? */
+ int n = lua_rawlen(L, -1) + 1; /* index for name */
+ lua_pushvalue(L, frule); /* rule's name */
+ lua_rawseti(L, -2, n); /* ktable was on the top of the stack */
+ sib1(grammar)->key = n;
+ }
+}
+
+
+static TTree *newgrammar (lua_State *L, int arg) {
+ int treesize;
+ int frule = lua_gettop(L) + 2; /* position of first rule's key */
+ int n = collectrules(L, arg, &treesize);
+ TTree *g = newtree(L, treesize);
+ luaL_argcheck(L, n <= MAXRULES, arg, "grammar has too many rules");
+ g->tag = TGrammar; g->u.n = n;
+ lua_newtable(L); /* create 'ktable' */
+ lua_setuservalue(L, -2);
+ buildgrammar(L, g, frule, n);
+ lua_getuservalue(L, -1); /* get 'ktable' for new tree */
+ finalfix(L, frule - 1, g, sib1(g));
+ initialrulename(L, g, frule);
+ verifygrammar(L, g);
+ lua_pop(L, 1); /* remove 'ktable' */
+ lua_insert(L, -(n * 2 + 2));