diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml new file mode 100644 index 00000000..bf97b77d --- /dev/null +++ b/.github/workflows/binaries.yml @@ -0,0 +1,347 @@ +name: "binaries" + +on: [push] + +env: + # LuaJIT has no releases; pin a commit from the v2.1 branch + LUAJIT_COMMIT: 346ab587cb235b4ef0b5777b4cd29009808d0cc0 + +jobs: + generate-headers: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + + - uses: leafo/gh-actions-lua@master + with: + luaVersion: "5.1" + + - uses: leafo/gh-actions-luarocks@master + + - name: Install dependencies + run: | + luarocks install argparse + luarocks make + + - name: Generate moonscript.h + run: | + bin/splat.moon -x moonscript.parse.slow -x moonscript.parse.grammar moonscript moon > moonscript.lua + xxd -i moonscript.lua > bin/binaries/moonscript.h + + - name: Generate moon.h + run: | + awk 'FNR>1' bin/moon > moon.lua + xxd -i moon.lua > bin/binaries/moon.h + + - name: Generate argparse.h + run: | + luarocks install argparse --tree=lua_modules + bin/splat.moon --strip-prefix -l argparse $(find lua_modules/share/lua -name "argparse.lua" -exec dirname {} \; | head -1) > bin/binaries/argparse.lua + xxd -i -n argparse_lua bin/binaries/argparse.lua > bin/binaries/argparse.h + + - name: Generate moonc.h + run: | + awk 'FNR>1' bin/moonc > moonc.lua + xxd -i moonc.lua > bin/binaries/moonc.h + + - name: Upload headers + uses: actions/upload-artifact@v6 + with: + name: generated-headers + path: bin/binaries/*.h + + linux: + runs-on: ${{ matrix.runner }} + needs: generate-headers + + strategy: + matrix: + include: + - runner: ubuntu-latest + architecture: x86_64 + artifact_suffix: "" + runtime: puc + lua_version: "5.1.5" + runtime_label: "lua5.1.5" + release_suffix: "" + lua_include: lua-5.1.5/src + lua_lib: lua-5.1.5/src/liblua.a + - runner: ubuntu-latest + architecture: x86_64 + artifact_suffix: "" + runtime: luajit + runtime_label: "luajit2.1" + release_suffix: "-luajit" + lua_include: luajit/src + lua_lib: luajit/src/libluajit.a + - runner: ubuntu-24.04-arm + architecture: arm64 + artifact_suffix: "-arm64" + runtime: puc + lua_version: "5.1.5" + runtime_label: "lua5.1.5" + release_suffix: "" + lua_include: lua-5.1.5/src + lua_lib: lua-5.1.5/src/liblua.a + - runner: ubuntu-24.04-arm + architecture: arm64 + artifact_suffix: "-arm64" + runtime: luajit + runtime_label: "luajit2.1" + release_suffix: "-luajit" + lua_include: luajit/src + lua_lib: luajit/src/libluajit.a + + steps: + - uses: actions/checkout@master + + - name: Set version suffix + run: | + if [[ "${{ github.ref_type }}" == "tag" ]]; then + echo "VERSION_SUFFIX=${{ github.ref_name }}" >> $GITHUB_ENV + else + echo "VERSION_SUFFIX=$(git rev-parse --short HEAD)" >> $GITHUB_ENV + fi + # commit time, not wall clock, so identical inputs build identical binaries + echo "BUILD_TIME=$(git show -s --format=%cI HEAD)" >> $GITHUB_ENV + + - name: Download headers + uses: actions/download-artifact@v4 + with: + name: generated-headers + path: bin/binaries/ + + - name: Show GCC + run: gcc -v + + - name: Setup Lua + if: matrix.runtime == 'puc' + run: | + curl -L -O https://www.lua.org/ftp/lua-${{ matrix.lua_version }}.tar.gz + tar -xzf lua-${{ matrix.lua_version }}.tar.gz + cd lua-${{ matrix.lua_version }}/src && make liblua.a MYCFLAGS=-DLUA_USE_POSIX + + - name: Setup LuaJIT + if: matrix.runtime == 'luajit' + run: | + curl -L -o luajit.tar.gz https://github.com/LuaJIT/LuaJIT/archive/$LUAJIT_COMMIT.tar.gz + tar -xzf luajit.tar.gz + mv LuaJIT-* luajit + make -C luajit/src -j$(nproc) amalg BUILDMODE=static + + - name: Get LPeg + run: | + curl -L -o lpeg.tar.gz https://www.inf.puc-rio.br/~roberto/lpeg/lpeg-1.0.2.tar.gz + tar -xzf lpeg.tar.gz + + - name: Get Luafilesystem + run: | + curl -L -o luafilesystem.tar.gz https://github.com/keplerproject/luafilesystem/archive/v1_8_0.tar.gz + tar -xzf luafilesystem.tar.gz + + - name: Build + run: | + mkdir -p dist + gcc -static -o dist/moon \ + -I${{ matrix.lua_include }} \ + -Ilpeg-1.0.2/ \ + -Ibin/binaries/ \ + -DMOON_BUILD_COMMIT="\"${{ env.VERSION_SUFFIX }}\"" \ + -DMOON_BUILD_TIME="\"${{ env.BUILD_TIME }}\"" \ + bin/binaries/moon.c \ + bin/binaries/moonscript.c \ + moonscript/parse/native.c \ + lpeg-1.0.2/lpvm.c \ + lpeg-1.0.2/lpcap.c \ + lpeg-1.0.2/lptree.c \ + lpeg-1.0.2/lpcode.c \ + lpeg-1.0.2/lpprint.c \ + ${{ matrix.lua_lib }} \ + -lm -ldl + gcc -static -o dist/moonc \ + -I${{ matrix.lua_include }} \ + -Ilpeg-1.0.2/ \ + -Ibin/binaries/ \ + -DMOON_BUILD_COMMIT="\"${{ env.VERSION_SUFFIX }}\"" \ + -DMOON_BUILD_TIME="\"${{ env.BUILD_TIME }}\"" \ + bin/binaries/moonc.c \ + bin/binaries/moonscript.c \ + moonscript/parse/native.c \ + lpeg-1.0.2/lpvm.c \ + lpeg-1.0.2/lpcap.c \ + lpeg-1.0.2/lptree.c \ + lpeg-1.0.2/lpcode.c \ + lpeg-1.0.2/lpprint.c \ + luafilesystem-1_8_0/src/lfs.c \ + ${{ matrix.lua_lib }} \ + -lm -ldl + + - name: Test run + run: | + dist/moon -h + dist/moon -e 'print "hello world"' + dist/moonc -h + + - name: Test JIT + if: matrix.runtime == 'luajit' + run: dist/moon -e 'print jit.version' + + - name: Upload artifact + uses: actions/upload-artifact@v6 + with: + name: moonscript-${{ env.VERSION_SUFFIX }}-linux-${{ matrix.runtime_label }}${{ matrix.artifact_suffix }} + path: dist/ + + - name: Package for release + if: github.ref_type == 'tag' + run: | + cd dist + tar -czvf ../moonscript-${{ env.VERSION_SUFFIX }}-linux-${{ matrix.architecture }}${{ matrix.release_suffix }}.tar.gz * + + - name: Upload to release + if: github.ref_type == 'tag' + uses: softprops/action-gh-release@v2 + with: + files: moonscript-${{ env.VERSION_SUFFIX }}-linux-${{ matrix.architecture }}${{ matrix.release_suffix }}.tar.gz + + windows: + runs-on: windows-latest + needs: generate-headers + + strategy: + matrix: + include: + - runtime: puc + lua_version: "5.1.5" + runtime_label: "lua5.1.5" + release_suffix: "" + msys_install: gcc make curl zip + lua_include: lua-5.1.5/src + lua_lib: lua-5.1.5/src/liblua.a + - runtime: luajit + runtime_label: "luajit2.1" + release_suffix: "-luajit" + msys_install: make curl zip mingw-w64-x86_64-gcc + lua_include: luajit/src + lua_lib: luajit/src/libluajit.a + + defaults: + run: + shell: msys2 {0} + + steps: + - uses: actions/checkout@master + + - name: Download headers + uses: actions/download-artifact@v4 + with: + name: generated-headers + path: bin/binaries/ + + - uses: msys2/setup-msys2@v2 + with: + install: ${{ matrix.msys_install }} + + - name: Set version suffix + shell: bash + run: | + if [[ "${{ github.ref_type }}" == "tag" ]]; then + echo "VERSION_SUFFIX=${{ github.ref_name }}" >> $GITHUB_ENV + else + echo "VERSION_SUFFIX=$(git rev-parse --short HEAD)" >> $GITHUB_ENV + fi + # commit time, not wall clock, so identical inputs build identical binaries + echo "BUILD_TIME=$(git show -s --format=%cI HEAD)" >> $GITHUB_ENV + + - name: Show GCC + run: gcc -v + + - name: Setup Lua + if: matrix.runtime == 'puc' + run: | + curl -L -O https://www.lua.org/ftp/lua-${{ matrix.lua_version }}.tar.gz + tar -xzf lua-${{ matrix.lua_version }}.tar.gz + cd lua-${{ matrix.lua_version }}/src && make liblua.a + + - name: Setup LuaJIT + if: matrix.runtime == 'luajit' + run: | + curl -L -o luajit.tar.gz https://github.com/LuaJIT/LuaJIT/archive/$LUAJIT_COMMIT.tar.gz + tar -xzf luajit.tar.gz + mv LuaJIT-* luajit + make -C luajit/src -j$(nproc) amalg BUILDMODE=static + + - name: Get LPeg + run: | + curl -L -o lpeg.tar.gz https://www.inf.puc-rio.br/~roberto/lpeg/lpeg-1.0.2.tar.gz + tar -xzf lpeg.tar.gz + + - name: Get Luafilesystem + run: | + curl -L -o luafilesystem.tar.gz https://github.com/keplerproject/luafilesystem/archive/v1_8_0.tar.gz + tar -xzf luafilesystem.tar.gz + + - name: Build + run: | + mkdir -p dist + gcc -static -o dist/moon.exe \ + -I${{ matrix.lua_include }} \ + -Ilpeg-1.0.2/ \ + -Ibin/binaries/ \ + -DMOON_BUILD_COMMIT="\"${{ env.VERSION_SUFFIX }}\"" \ + -DMOON_BUILD_TIME="\"${{ env.BUILD_TIME }}\"" \ + bin/binaries/moon.c \ + bin/binaries/moonscript.c \ + moonscript/parse/native.c \ + lpeg-1.0.2/lpvm.c \ + lpeg-1.0.2/lpcap.c \ + lpeg-1.0.2/lptree.c \ + lpeg-1.0.2/lpcode.c \ + lpeg-1.0.2/lpprint.c \ + ${{ matrix.lua_lib }} \ + -lm + gcc -static -o dist/moonc.exe \ + -I${{ matrix.lua_include }} \ + -Ilpeg-1.0.2/ \ + -Ibin/binaries/ \ + -DMOON_BUILD_COMMIT="\"${{ env.VERSION_SUFFIX }}\"" \ + -DMOON_BUILD_TIME="\"${{ env.BUILD_TIME }}\"" \ + bin/binaries/moonc.c \ + bin/binaries/moonscript.c \ + moonscript/parse/native.c \ + lpeg-1.0.2/lpvm.c \ + lpeg-1.0.2/lpcap.c \ + lpeg-1.0.2/lptree.c \ + lpeg-1.0.2/lpcode.c \ + lpeg-1.0.2/lpprint.c \ + luafilesystem-1_8_0/src/lfs.c \ + ${{ matrix.lua_lib }} \ + -lm + + - name: Test run + run: | + dist/moon.exe -h + dist/moon.exe -e 'print "hello world"' + dist/moonc.exe -h + + - name: Test JIT + if: matrix.runtime == 'luajit' + run: dist/moon.exe -e 'print jit.version' + + - name: Upload artifact + uses: actions/upload-artifact@v6 + with: + name: moonscript-${{ env.VERSION_SUFFIX }}-windows-${{ matrix.runtime_label }} + path: dist/ + + - name: Package for release + if: github.ref_type == 'tag' + run: | + cd dist + zip ../moonscript-${{ env.VERSION_SUFFIX }}-windows-x86_64${{ matrix.release_suffix }}.zip * + + - name: Upload to release + if: github.ref_type == 'tag' + uses: softprops/action-gh-release@v2 + with: + files: moonscript-${{ env.VERSION_SUFFIX }}-windows-x86_64${{ matrix.release_suffix }}.zip diff --git a/.github/workflows/spec.yml b/.github/workflows/spec.yml new file mode 100644 index 00000000..ac6d852d --- /dev/null +++ b/.github/workflows/spec.yml @@ -0,0 +1,32 @@ +name: "spec" + +on: [push, pull_request] + +jobs: + test: + strategy: + fail-fast: false + matrix: + luaVersion: ["5.1", "5.2", "5.3", "5.4", "luajit", "luajit-openresty"] + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@master + + - uses: leafo/gh-actions-lua@master + with: + luaVersion: ${{ matrix.luaVersion }} + + - uses: leafo/gh-actions-luarocks@master + + - name: build + run: | + luarocks install busted + luarocks install loadkit + luarocks make + + - name: test + run: | + busted -o utfTerminal + busted -o utfTerminal --helper=spec/use_slow_parser.moon diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..eea16b9d --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.so +*.o +dist/ +bin/binaries/*.h +bin/binaries/argparse.lua diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..54dfa0fa --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,1020 @@ +# MoonScript dev + +## New parser + +The LPeg parser has been replaced. The grammar is defined in +`moonscript/parse/grammar.moon` and compiled by +[pgen](https://github.com/leafo/pgen) into a native C module, +`moonscript.parse.native`. A pure Lua version of the same grammar is included +as `moonscript.parse.slow`, a drop-in replacement for the native module; it +is not selected automatically. + +Parsing is around 5x faster: + +| Interpreter | LPeg parser | New parser | Speedup | +|-------------|------------:|-----------:|--------:| +| Lua 5.1.5 | 0.947s | 0.180s | 5.3x | +| LuaJIT 2.1 | 0.817s | 0.141s | 5.8x | + +(Parse time for a 405 file, 58,296 line corpus, best of 5 passes.) + +The parser produces the same syntax trees as the LPeg grammar. Other +changes: + +* Parse error messages have a new format: ` at line , column + :` followed by the offending line and a `^` marker, replacing + `Failed to parse: [] >> `. Anything matching on the old error text + will need updating. +* Installing the rock now compiles a C module, so a C compiler is required at + install time. +* `moonscript.parse` no longer exports `build_grammar` and `extract_line`, + and the `moonscript.parse.util`, `moonscript.parse.literals` and + `moonscript.parse.env` modules have been removed. +* The `MOONSCRIPT_PARSER` environment variable is no longer used. +* LPeg is still a dependency: `moonscript.errors` and `bin/moon-tags` use it + for parsing unrelated to MoonScript source. + +# MoonScript v0.7.0 (2026-07-26) + +This update includes a bunch of syntax bug fixes and cleanups, with a few new +features and an enhanced linter. This will be the last release using LPeg, +future versions will use [the new +parser](https://github.com/leafo/moonscript-parser). + +## Destructuring updates + +### Function arguments can be destructured + +A function argument can now be written as a table literal to destructure the +value passed in. The destructuring patterns are the same ones supported by +assignment, including nested patterns, numeric keys, and `@field` targets: + +```moonscript +dist = ({x: x1, y: y1}, {x: x2, y: y2}) -> + math.sqrt (x2 - x1)^2 + (y2 - y1)^2 + +class Point + new: ({x: @x, y: @y}) => + +render = (name, {:width, :height}, scale=1, ...) -> + print name, width, height, scale, ... +``` + +The argument becomes an internal name and the fields are unpacked at the top of +the function body: + +```lua +render = function(name, _arg_0, scale, ...) + local width, height + width, height = _arg_0.width, _arg_0.height + if scale == nil then + scale = 1 + end + return print(name, width, height, scale, ...) +end +``` + +Destructured arguments always declare fresh locals, so a name in the pattern +shadows an enclosing local of the same name instead of assigning to it. + +Argument initialization now happens in the order the arguments are written, so a +default value can reference a name that an earlier argument destructured: + +```moonscript +scaled = ({:base}, amount = base * 2) -> base + amount +``` + +Because the destructured names are bound at the top of the body, a name in a +pattern can not collide with another argument. `({:a}, a) ->` and `({:self}) =>` +are now compile errors. + +### Destructuring in multiple assignment + +Destructuring can now be used mixed in with other names in multiple assignment (#367, #304): + +```moonscript +num, {:message} = two_values! +head, {:content}, tail = mix! +{value: built}, {:status} = builder\build! +``` + +Previously the assignment was split into separate statements, which broke +destructuring output. + +### Better validation of destructuring targets + +The names extracted by a destructuring pattern are now checked with the same +rule the parser uses for assignment targets. `@@class_field`, index expressions, +and slices are now allowed: + +```moonscript +{x: obj.a, y: obj["b"], z: @prop, w: @@cls_prop} = t +``` + +Destructuring into something that can never be assigned to is now a compile +error instead of generating broken code: + +* `{foo!} = thing` reports `Can't destructure into chain ending in call` +* `{...} = thing` reports `Can't destructure into '...'` + +## Linter updates + +### Lint stages + +The linter's checks are now organized into named stages: `global_access`, +`unused`, `constant_assign`, and `import_overwrite`. Every stage runs by +default. `moonc --lint-stage` limits reporting to the named stage, and +`--exclude-lint-stage` reports everything but the named stage. Both options can +be repeated, and they can not be combined. + +```bash +moonc -l --lint-stage global_access --lint-stage unused . +moonc -l --exclude-lint-stage import_overwrite . +``` + +### New checks + +`import_overwrite` reports an `import` that clobbers a name that is already +bound in scope: + +```moonscript +insert = "hello" +f = -> + import insert from table -- import overwrites existing binding `insert` +``` + +`constant_assign` reports writing to a name that is tagged as constant. In this +version, only the names created by `import` are tagged as constant. For this +release, constant names are only checked in the linter. In the future we will +make constant violation a compiler error. + +```moonscript +import insert from table +insert = 5 -- assigning to constant `insert` +``` + +### Compact lint output + +`moonc --lint-format compact` writes one line per issue in the familiar +`file:line:column: message` shape, with the stage name appended, for editors +and tools that parse compiler output (#465): + +```bash +$ moonc -l --lint-format compact lint_example.moon +lint_example.moon:7:5: accessing global `my_nmuber` [global_access] +``` + +## Standard library updates + +### New class introspection functions + +The `moon` module has four new functions, documented in the [standard +library reference](http://moonscript.org/reference/standard_lib.html): + +* `is_class(value)` tests if a value is a MoonScript class table +* `is_instance(value)` tests if a value is an instance of a MoonScript class +* `is_instance_of(value, cls)` tests if a value is an instance of `cls` or any of + its parents. It throws an error if `value` is not an instance +* `is_subclass_of(cls, parent)` tests if `cls` inherits from `parent`. A class is + not a subclass of itself. It throws an error if `cls` is not a class + +Unlike the old `is_object`, these can tell classes, instances, and `__base` +tables apart. + +`is_object` is now deprecated. It returns truthy for instances, classes, and +`__base` tables, so it can not be used to distinguish them. + +### BREAKING: `moon.type` no longer returns the class object for a class + +`moon.type` now returns the string `"class"` when given a class, and it only +returns the class object for actual instances. Previously a class returned +itself, and a `__base` table returned the class it belonged to, which made it +impossible to tell an instance from the class it came from. + +```moonscript +class MyClass + +type MyClass! -- MyClass (unchanged) +type MyClass -- "class" (was MyClass) +type MyClass.__base -- "table" (was MyClass) +``` + +### BREAKING: changes to `moonscript.util` + +These are internal helpers that were never documented, but they were reachable: + +* `util.moon.type` is now `util.mtype`, to avoid the confusion with the `type` + function in the `moon` module +* `util.moon.is_object` removed, replaced by `util.moon.is_class` and + `util.moon.is_instance` +* `util.moon.is_a` removed, replaced by `is_instance_of` in the `moon` module + +## Code Generation + +### Interpolated strings and nested expressions are parenthesized correctly + +The compiler now knows Lua's operator precedence when writing out an +expression, and it adds parentheses when a nested expression would otherwise be +regrouped by the surrounding operators. String interpolation is the most common +way to get a nested expression, and it was silently generating the wrong math +(#320): + +```moonscript +x = 10 / "#{b}.5" +``` + +**Before:** + +```lua +local x = 10 / tostring(b) .. ".5" +``` + +**After:** + +```lua +local x = 10 / (tostring(b) .. ".5") +``` + +Because `..` is right associative in Lua, an interpolated string on the left +hand side of a `..` is grouped as well. This is observable through the +`__concat` metamethod: + +```moonscript +prefix = "id: #{id}, " .. rest +``` + +**Before:** + +```lua +local prefix = "id: " .. tostring(id) .. ", " .. rest +``` + +**After:** + +```lua +local prefix = ("id: " .. tostring(id) .. ", ") .. rest +``` + +### Prefix operators are grouped in update assignments and switches + +The check for whether a value needs to be wrapped in parentheses did not +account for the prefix operators `#`, `-`, `not`, and `~`, which parse greedily. +An update assignment or a `switch` case with one of them on the left would +compile to the wrong grouping (#457): + +```moonscript +count -= #datum + 1 +total *= -x + 1 +flag and= not a or b +``` + +**Before:** + +```lua +local count = count - #datum + 1 +local total = total * -x + 1 +local flag = flag and not a or b +``` + +**After:** + +```lua +local count = count - (#datum + 1) +local total = total * (-x + 1) +local flag = flag and (not a or b) +``` + +### Expression lines no longer use `_` + +When an expression appears where a statement is expected, the compiler assigns +it to a throwaway variable. It used to use the name `_`, which would clobber a +variable of that name in the user's code (#309). It now uses an autogenerated +`_scrap_N` name. + +```moonscript +_ = 5 +tbl.field +print _ +``` + +**Before:** + +```lua +local _ = 5 +_ = tbl.field +return print(_) +``` + +**After:** + +```lua +local _ = 5 +local _scrap_0 = tbl.field +return print(_) +``` + +### `local` in a class body is hoisted + +A `local` declaration inside a class body was written out near the bottom of the +generated `do` block, after the methods had already been compiled. Methods +referencing the name would close over the outer or global name instead of the +class local (#459). The declaration is now hoisted to the top of the class body, +placed after the reference to the parent class so a body local can shadow the +parent's name. + +```moonscript +class Counter + local cache + + -- previously `cache` local would not be lexically available in the method + update: => cache or= "something..." +``` + +## Bug Fixes + +* Updated all compiler thrown errors to ensure they include positional + information so the error messages are more informative +* Trailing `continue` in a loop body generated invalid Lua (`break` was + followed by more statements) +* `a, b = x\some_method` and other assignments of a single complex value to + multiple names silently dropped every name after the first +* A `\stub` without a base crashed the compiler with an internal error. Inside a + `with` block it now uses the `with` value, and outside of one it reports + `Short-colon syntax must be called within a with block` (#428) +* `continue` outside of a loop is now a compile error with a line number instead + of an internal error with a traceback +* Destructuring against the result of an `if`, `switch`, `for`, `while`, `do`, + `with`, or comprehension declared the names in the wrong scope (#449, #411, + #391, #451) +* Destructuring against a receiver that can not be indexed directly, like `...`, + `nil`, `not thing`, `#thing`, or a table literal, generated invalid Lua +* An expression inside a string interpolation that can't be parsed now throws a + parse error instead of putting the malformed code as raw characters in the + string +* Fixed broken `moonc --version` which reported missing argument instead of + printing version (#471) +* Errors raised without a position now report the position of the statement + being compiled, instead of no position at all +* An internal compiler error no longer throws a second error while trying to + report the first one + +## Binaries + +* The static binaries now report build information with `--version`: + + ``` + $ moon --version + MoonScript version 0.7.0 (static build) + Runtime: LuaJIT 2.1.1748459687 + Commit: v0.7.0 + Built: 2026-07-24T09:07:45-07:00 + ``` + +* LuaJIT builds are now included alongside the Lua 5.1 builds +* Linux arm64 builds are now included + +## New Parser in Testing + +The next version of MoonScript will likely include the [new C +parser](https://github.com/leafo/moonscript-parser) generated by +[pgen](https://github.com/leafo/pgen), replacing LPeg. Early testing shows the +new parser is be about 5x faster. + +Setting the `MOONSCRIPT_PARSER=pgen` environment variable will make the +compiler parse with the `moonscript_parser` module instead of building the LPeg +grammar at runtime. This is opt in and experimental, the LPeg grammar is still +the default + +# MoonScript v0.6.0 (2026-01-10) + +## New Features + +### Command Line Improvements + +- **Switched from alt_getopt to argparse** - Both `moon` and `moonc` now use argparse for argument parsing, providing better help messages via `--help` and more robust option handling +- **Added `-e/--execute` flag to `moon`** - Execute MoonScript code directly from the command line: + ``` + moon -e "print 'Hello World'" + ``` +- **New `--transform` option for `moonc`** - Allows custom AST transformations before compilation by specifying a module that receives and returns the syntax tree +- **Improved `-` (stdin) handling** - Now properly enforces that `-` must be the only argument + +### moonc Option Renames +- `-w` is now also available as `--watch` +- `-l` is now also available as `--lint` +- `-t` is now also available as `--output-to` + +### New Tools + +- **moon-tags** - New script for generating ctags-compatible tag files for MoonScript, with support for: + - Class definitions + - Class methods + - Top-level function definitions (exported via `{:func}` pattern) + - Lapis route detection (`--lapis` flag) + - Optional line numbers (`--include-line` flag) + - Optionally skip header (`--no-header` flag) + +### Utility Improvements + +- **`moon.p()` now prints multiple arguments** - Pass multiple values and each will be dumped +- **`util.dump` shows class names** - When dumping objects, the class name is displayed (e.g., `{...}`) + +### Compiler Enhancements + +- **Lua keyword property access on self** - Properties with Lua keyword names (like `@then` or `@@then`) now compile correctly using bracket notation instead of invalid `self.then` + +## Bug Fixes + +- **Fixed ambiguous Lua generation after `import`** - Semicolons are now correctly inserted when the next line starts with `(`, preventing parsing ambiguity +- **Fixed update operators with complex chain indexes** - Expressions like `a[func()].x += 1` now correctly lift the index expression to avoid double evaluation +- **Exit with proper error code** - `moon` now exits with code 1 when the executed script fails +- **Fixed `moonc -`** - Reading from stdin now works correctly +- **Removed accidental debug print** - Removed stray `print file, time` in watcher code +- **`dump.tree` returns string** - Now returns the string instead of printing directly +- **Removed noisy "Built" message** - Single file compilation no longer prints "Built" to stderr + +## Internal Changes + +- Migrated CI from Travis to GitHub Actions +- Added comprehensive compiler and transform specs +- Improved binary building workflow for Windows and Linux +- Better error messages for invalid destructure assignments +- Updated `splat.moon` to use argparse, added `--strip-prefix` option + + +# MoonScript v0.5.0 (2016-9-25) + +## Syntax updates + +### Function calls + +Function calls with parentheses can now have free whitespace around the +arguments. Additionally, a line break may be used in place of a comma: + +```moonscript +my_func( + "first arg" + => + print "some func" + + "third arg", "fourth arg" +) +``` + +### Function argument definitions + +Just like the function all update, function argument definitions have no +whitespace restrictions between arguments, and line breaks can be used to +separate arguments: + + +```moonscript +some_func = ( + name + type + action="print" +) => + print name, type, action +``` + +## Additions + +* `elseif` can be used part of an `unless` block (nymphium) +* `unless` conditional expression can contain an assignment like an `if` statement (#251) +* Lua 5.3 bitwise operator support (nymphium) (Kawahara Satoru) +* Makefile is Lua version agnostic (nymphium) +* Lint flag can be used with `moonc` watch mode (ChickenNuggers) +* Lint exits with status 1 if there was a problem detected (ChickenNuggers) +* Compiler can be used with lulpeg + +## Bug Fixes + +* Slice boundaries can be full expressions (#233) +* Destructure works when used as loop variable in comprehension (#236) +* Proper name local hoisting works for classes again (#287) +* Quoted table key literals can now be parsed when table declaration is in single line (#286) +* Fix an issue where `else` could get attached to wrong `if` statement (#276) +* Loop variables will no longer overwrite variables of the same name in the same scope (egonSchiele) +* A file being deleted will not crash polling watch mode (ChickenNuggers) +* The compiler will not try to compile a directory ending in `.moon` (Gskartwii) +* alt_getopt import works with modern version (Jon Allen) +* Code coverage not being able to find file from chunk name + + +# MoonScript v0.4.0 (2015-12-06) + +## Changes to `super` + +`super` now looks up the parent method via the class reference, instead of a +(fixed) closure to the parent class. + +Given the following code: + +```moonscript +class MyThing extends OtherThing + the_method: => + super! +``` + +In the past `super` would compile to something like this: + +```lua +_parent_0.the_method(self) +``` + +Where `_parent_0` was an internal local variable that contains a reference to +the parent class. Because the reference to parent is an internal local +variable, you could never swap out the parent unless resorting to the debug +library. + +This version will compile to: + +```lua +_class_0.__parent.__base.the_method(self) +``` + +Where `_class_0` is an internal local variable that contains the current class (`MyThing`). + +Another difference is that the instance method is looked up on `__base` instead +of the class. The old variation would trigger the metamethod for looking up on +the instance, but a class method of the same name could conflict, take +precedence, and be retuned instead. By referencing `__base` directly we avoid +this issue. + +### Super on class methods + +`super` can now be used on class methods. It works exactly as you would expect. + +```moonscript +class MyThing extends OtherThing + @static_method: => + print super! +``` + +Calling `super` will compile to: + +```moonscript +_class_0.__parent.static_method(self) +``` + +### Improved scoping for super + +The scoping of super is more intelligent. You can warp your methods in other +code and `super` will still generate correctly. For example, syntax like this +will now work as expected: + +```moonscript +class Sub extends Base + value: if debugging + => super! + 100 + else + => super! + 10 + + other_value: some_decorator { + the_func: => + super! + } +``` + +`super` will refer to the lexically closest class declaration to find the name +of the method it should call on the parent. + +## Bug Fixes + +* Nested `with` blocks used incorrect ref (#214 by @geomaster) +* Lua quote string literals had wrong precedence (#200 by @nonchip) +* Returning from `with` block would generate two `return` statements (#208) +* Including `return` or `break` in a `continue` wrapped block would generate invalid code (#215 #190 #183) + +## Other + +* Refactor transformer out into multiple files +* `moon` command line script rewritten in MoonScript +* `moonscript.parse.build_grammar` function for getting new instance of parser grammar +* Chain AST updated to be simpler + +# MoonScript v0.3.2 (2015-6-01) + +## Bug Fixes + +* `package.moonpath` geneator does not use paths that don't end in `lua` + +# MoonScript v0.3.1 (2015-3-07) + +## Bug Fixes + +* Fixed a bug where an error from a previous compile would prevent the compiler from running again + +# MoonScript v0.3.0 (2015-2-28) + +## New Features + +* New [unused assignment linter](http://moonscript.org/reference/command_line.html#unused_variable_assigns) finds assignments that are never referenced after being defined. + +## Parsing Updates + +Whitespace parsing has been relaxed in a handful of locations: + +* You can put unrestricted whitespace/newlines after operator in a binary operator before writing the right hand side. The following are now valid: + +```moonscript +x = really_long_function! + + 2304 + +big_math = 123 / + 12 - + 43 * 17 + + +bool_exp = nice_shirt and cool_shoes or + skateboard and shades +``` + +* You can put unrestricted whitespace/newlines immediately after an opening parenthesis, and immediately before closing parenthesis. The following are now valid: + +```moonscript +hello = 100 + ( + var * 0.23 +) - 15 + + +funcall( + "height", "age", "weight" +) + + +takes_two_functions (-> + print "hello" +), -> + print "world" +``` + +* You can put unrestricted whitespace/newlines immediately after a `:` when defining a table literal. The following is now valid: + +```moonscript +x = { + hello: + call_a_function "football", "hut" +} +``` + +## Code Generation + +* Single value `import`/`destructure` compiles directly into single assignment + +## Bug Fixes + +* Some `moonc` command line flags were being ignored +* Linter would not report global reference when inside self assign in table +* Fixed an issue where parser would crash in Lpeg 0.12 when compiling hundreds of times per process + +## Misc + +* MoonScript parser now written in MoonScript + +# MoonScript v0.2.6 (2014-6-18) + +## Bug Fixes + +* Fixes to posmap generation for multi-line mappings and variable declarations +* Prefix file name with `@` when loading code so stack traces tread it as file +* Fix bug where `moonc` couldn't work with absolute paths +* Improve target file path generation for `moonc` + +# MoonScript v0.2.5 (2014-3-5) + +## New Things + +* New [code coverage tool](http://moonscript.org/reference/#code_coverage) built into `moonc` +* New [linting tool](http://moonscript.org/reference/#linter) built into `moonc`, identifies global variable references that don't pass whitelist +* Numbers can have `LL` and `ULL` suffixes for LuaJIT + +## Bug Fixes + +* Error messages from `moonc` are written to standard error +* Moonloader correctly throws error when moon file can't be parsed, instead of skipping the module +* Line number rewriting is no longer incorrectly offset due to multiline strings + +## Code Generation + +Bound functions will avoid creating an anonymous function unless necessary. + +```moonscript +x = hello\world +``` + +**Before:** + +```lua +local x = (function() + local _base_0 = hello + local _fn_0 = _base_0.world + return function(...) + return _fn_0(_base_0, ...) + end +end)() +``` + +**After:** + +```lua +local x +do + local _base_0 = hello + local _fn_0 = _base_0.world + x = function(...) + return _fn_0(_base_0, ...) + end +end +``` + +Explicit return statement now avoids creating anonymous function for statements +where return can be cascaded into the body. + +```moon +-> + if test1 + return [x for x in *y] + + if test2 + return if true + "yes" + else + "no" + + false +``` + + +**Before:** + +```lua +local _ +_ = function() + if test1 then + return (function() + local _accum_0 = { } + local _len_0 = 1 + local _list_0 = y + for _index_0 = 1, #_list_0 do + local x = _list_0[_index_0] + _accum_0[_len_0] = x + _len_0 = _len_0 + 1 + end + return _accum_0 + end)() + end + if test2 then + return (function() + if true then + return "yes" + else + return "no" + end + end)() + end + return false +end +``` + +**After:** + +```lua +local _ +_ = function() + if test1 then + local _accum_0 = { } + local _len_0 = 1 + local _list_0 = y + for _index_0 = 1, #_list_0 do + local x = _list_0[_index_0] + _accum_0[_len_0] = x + _len_0 = _len_0 + 1 + end + return _accum_0 + end + if test2 then + if true then + return "yes" + else + return "no" + end + end + return false +end +``` + + + +# MoonScript v0.2.4 (2013-07-02) + +## Changes + +* The way the subtraction operator works has changed. There was always a little confusion as to the rules regarding whitespace around it and it was recommended to always add whitespace around the operator when doing subtraction. Not anymore. Hopefully it now [works how you would expect](http://moonscript.org/reference/#considerations). (`a-b` compiles to `a - b` and not `a(-b)` anymore). +* The `moon` library is no longer sets a global variable and instead returns the module. Your code should now be: + +```moonscript +moon = require "moon" +``` + +* Generated code will reuse local variables when appropriate. Local variables are guaranteed to not have side effects when being accessed as opposed to expressions and global variables. MoonScript will now take advantage of this and reuse those variable without creating and copying to a temporary name. +* Reduced the creation of anonymous functions that are called immediately. + MoonScript uses this technique to convert a series of statements into a single expression. It's inefficient because it allocates a new function object and has to do a function call. It also obfuscates stack traces. MoonScript will flatten these functions into the current scope in a lot of situations now. +* Reduced the amount of code generated for classes. Parent class code it left out if there is no parent. + +## New Things + +* You can now put line breaks inside of string literals. It will be replaced with `\n` in the generated code. + +```moonscript +x = "hello +world" +``` + +* Added `moonscript.base` module. It's a way of including the `moonscript` module without automatically installing the moonloader. +* You are free to use any whitespace around the name list in an import statement. It has the same rules as an array table, meaning you can delimit names with line breaks. + +```moonscript +import a, b + c, d from z +``` + +* Added significantly better tests. Previously the testing suite would only verify that code compiled to an expected string. Now there are unit tests that execute the code as well. This will make it easier to change the generated output while still guaranteeing the semantics are the same. + +## Bug Fixes + +* `b` is not longer treated as self assign in `{ a : b }` +* load functions will return `nil` instead of throwing error, as described in documentation +* fixed an issue with `moon.mixin` where it did not work as described + + +# MoonScript v0.2.3-2 (2013-01-29) + +Fixed bug with moonloader not loading anything + +# MoonScript v0.2.3 (2013-01-24) + +## Changes + +* For loops when used as expressions will no longer discard nil values when accumulating into an array table. **This is a backwards incompatible change**. Instead you should use the `continue` keyword to filter out iterations you don't want to keep. [Read more here](https://github.com/leafo/moonscript/issues/66). +* The `moonscript` module no longer sets a global value for `moonscript` and instead returns it. You should update your code: + +```moonscript +moonscript = require "moonscript" +``` + +## New Things + +* Lua 5.2 Support. The compiler can now run in either Lua 5.2 and 5.1 +* A switch `when` clause [can take multiple values](http://moonscript.org/reference/#switch), comma separated. +* Added [destructuring assignment](http://moonscript.org/reference/#destructuring_assignment). +* Added `local *` (and `local ^`) for [hoisting variable declarations](http://moonscript.org/reference/#local_statement) in the current scope +* List comprehensions and line decorators now support numeric loop syntax + +## Bug Fixes + +* Numbers that start with a dot, like `.03`, are correctly parsed +* Fixed typo in `fold` library function +* Fix declaration hoisting inside of class body, works the same as `local *` now + +## Other Stuff + +MoonScript has [made its way into GitHub](https://github.com/github/linguist/pull/246). `.moon` files should start to be recognized in the near future. + + +# MoonScript v0.2.2 (2012-11-03) + +## Changes + +* Compiled [files will now implicitly return](http://moonscript.org/reference/#implicit_returns_on_files) their last statement. Be careful, this might change what `require` returns. + +## New Things + +### The Language + +* Added [`continue` keyword](http://moonscript.org/reference/#continue) for skipping the current iteration in a loop. +* Added [string interpolation](http://moonscript.org/reference/#string_interpolation). +* Added [`do` expression and block](http://moonscript.org/reference/#do). +* Added `unless` as a block and line decorator. Is the inverse of `if`. +* Assignment can be used in an [`if` statement's expression](http://moonscript.org/reference/#with_assignment). +* Added `or=` and `and=` operators. +* `@@` can be prefixed in front of a name to access that name within `self.__class` +* `@` and `@@` can be [used as values](http://moonscript.org/reference/#_and__values) to reference `self` and `self.__class`. +* In class declarations it's possible to [assign to the class object](http://moonscript.org/reference/#class_variables) instead of the instance metatable by prefixing the key with `@`. +* Class methods can access [locals defined within the body](http://moonscript.org/reference/#class_declaration_statements) of the class declaration. +* Super classes are [notified when they are extended](http://moonscript.org/reference/#inheritance) from with an `__inherited` callback. +* Classes can now [implicitly return and be expressions](http://moonscript.org/reference/#anonymous_classes). +* `local` keyword returns, can be used for forward declaration or shadowing a variable. +* String literals can be used as keys in [table literals](http://moonscript.org/reference/#table_literals). +* Call methods on string literals without wrapping in parentheses: `"hello"\upper!` +* Table comprehensions can return a single value that is unpacked into the key and value. +* The expression in a [`with` statement can now be an assignment](http://moonscript.org/reference/#with_statement), to give a name to the expression that is being operated on. + + +### The API + +* The `load` functions can take an [optional last argument of options](http://moonscript.org/reference/#load_functions). + +### The Tools + +* The [online compiler](http://moonscript.org/compiler/) now runs through a web service instead of emscripten, should work reliably on any computer now. +* [Windows binaries](http://moonscript.org/bin/) have been updated. + +## Bug Fixes + +* Significantly improved the [line number rewriter](http://moonscript.org/reference/#error_rewriting). It should now accurately report all line numbers. +* Generic `for` loops correctly parse for multiple values as defined in Lua. +* Update expressions don't fail with certain combinations of precedence. +* All statements/expressions are allowed in a class body, not just some. +* `x = "hello" if something` will extract the declaration of `x` if it's not in scope yet. Preventing an impossible to access variable from being created. +* varargs, `...`, correctly bubble up through automatically generated anonymous functions. +* Compiler doesn't crash if you try to assign something that isn't assignable. +* Numerous other small fixes. See [commit log](https://github.com/leafo/moonscript/commits/master). + + +# MoonScript v0.2.0 (2011-12-12) + +## Changes + +* `,` is used instead of `:` for delimiting table slice parts. +* Class objects store the metatable of their instances in `__base`. `__base` is also used in inheritance when chaining metatables. + +## New Things + +### The Language + +* Added [key-value table comprehensions][4]. +* Added a [`switch` statement][7]. +* The body of a class can contain arbitrary expressions in addition to assigning properties. `self` in this scope refers to the class itself. +* Class objects themselves support accessing the properties of the superclass they extend (like instances). +* Class objects store their name as a string in the `__name` property. +* [Enhanced the `super` keyword][8] in instance methods. +* Bound methods can be created for an object by using `object\function_name` as a value. Called [function stubs][6]. +* Added `export *` statement to export all assigned names following the statement. +* Added `export ^` statement to export all assigning names that begin with a capital letter following the statement. +* `export` can be used before any assignment or class declaration to export just that assignment (or class declaration). +* Argument lists can be broken up over several lines with trailing comma. +* `:hello` is short hand for `hello: hello` inside of table literal. +* Added `..=` for string concatenation. +* `table.insert` no longer used to build accumlated values in comprehensions. + +### The API + +* Added `loadfile`, `loadstring`, and `dofile` functions to `moonscript` module to load/run MoonScript code. +* Added `to_lua` function to `moonscript` module to convert a MoonScript code string to Lua string. + +### The Tools + +* Created [prebuilt MoonScript Windows executables][2]. +* Wrote a [Textmate/Sublime Text bundle][9]. +* Wrote a [SciTE syntax highlighter with scintillua][10]. +* Created a [SciTE package for Windows][11] that has everything configured. +* Created an [online compiler and snippet site][12] using [emscripten][13]. +* Watch mode works on all platforms now. Uses polling if `inotify` is not + available. + +### Standard Library + +I'm now including a small set of useful functions in a single module called `moon`: + +```moonscript +require "moon" +``` + +Documentation is [available here][3]. + +## Bug Fixes + +* Windows line endings don't break the parser. +* Fixed issues when using `...` within comprehensions when the compiled code uses an intermediate function in the output. +* Names whose first characters happen to be a keyword don't break parser. +* Return statement can have no arguments +* argument names prefixed with `@` in function definitions work outside of classes work with default values. +* Fixed parse issues with the shorthand values within a `with` block. +* Numerous other small fixes. See [commit log][5]. + +## Other Stuff + +Since the first release, I've written one other project in MoonScript (other than the compiler). It's a static site generator called [sitegen][15]. It's what I now use to generate all of my project pages and this blog. + +# MoonScript 0.1.0 (2011-08-12) + +Initial release + + + [1]: http://moonscript.org + [2]: http://moonscript.org/bin/ + [3]: http://moonscript.org/reference/standard_lib.html + [4]: http://moonscript.org/reference/#table_comprehensions + [5]: https://github.com/leafo/moonscript/commits/master + [6]: http://moonscript.org/reference/#function_stubs + [7]: http://moonscript.org/reference/#switch + [8]: http://moonscript.org/reference/#super + [9]: https://github.com/leafo/moonscript-tmbundle + [10]: https://github.com/leafo/moonscript/tree/master/extra/scintillua + [11]: http://moonscript.org/scite/ + [12]: http://moonscript.org/compiler/ + [13]: http://emscripten.org + [14]: http://twitter.com/moonscript + [15]: http://leafo.net/sitegen/ + diff --git a/Makefile b/Makefile index 54dc37b7..f2768176 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,140 @@ +LUA ?= lua5.1 +LUA_VERSION = $(shell $(LUA) -e 'print(_VERSION:match("%d%.%d"))') +LUAROCKS = luarocks --lua-version=$(LUA_VERSION) +LUA_PATH_MAKE = $(shell $(LUAROCKS) path --lr-path);./?.lua;./?/init.lua +LUA_CPATH_MAKE = $(shell $(LUAROCKS) path --lr-cpath);./?.so -test:: - TIME=1 bin/moon test2.moon +LUA_SRC_VERSION ?= 5.1.5 +LPEG_VERSION ?= 1.0.2 +LFS_VERSION ?= 1_8_0 -local: - luarocks make --local moonscript-dev-1.rockspec +.PHONY: test local build watch lint count show test_binary generate clean -global: - sudo luarocks make moonscript-dev-1.rockspec +clean: + rm -f moonscript/parse/native.so moonscript/parse/native.o + rm -rf dist -compile: - bin/moonc moon/ moonscript/ +# regenerate the parser from the pgen grammar (requires the pgen to be installed) +generate: build + pgen moonscript/parse/grammar.lua -n moonscript_parse_native \ + -o moonscript/parse/native.c --vendor-errors moonscript/parse/errors.lua + clang-format -style="{ColumnLimit: 0}" -i moonscript/parse/native.c + pgen moonscript/parse/grammar.lua -n moonscript_parse_slow \ + -o moonscript/parse/slow.lua + +moonscript/parse/native.so: moonscript/parse/native.c + gcc -shared -o $@ -O3 -fPIC $< `pkg-config --cflags --libs lua5.1` + +build: + LUA_PATH='$(LUA_PATH_MAKE)' LUA_CPATH='$(LUA_CPATH_MAKE)' $(LUA) bin/moonc moon/ moonscript/ + echo "#!/usr/bin/env lua" > bin/moon + $(LUA) bin/moonc -p bin/moon.moon >> bin/moon + echo "-- vim: set filetype=lua:" >> bin/moon + + +# This will rebuild MoonScript from the (hopefully working) system installation of moonc +build_from_system: + moonc moon/ moonscript/ + echo "#!/usr/bin/env lua" > bin/moon + moonc -p bin/moon.moon >> bin/moon + echo "-- vim: set filetype=lua:" >> bin/moon + +show: + # LUA $(LUA) + # LUA_VERSION $(LUA_VERSION) + # LUAROCKS $(LUAROCKS) + # LUA_PATH_MAKE $(LUA_PATH_MAKE) + # LUA_CPATH_MAKE $(LUA_CPATH_MAKE) + +test: build moonscript/parse/native.so + LUA_PATH='$(LUA_PATH_MAKE)' LUA_CPATH='./?.so;$(LUA_CPATH_MAKE)' busted + LUA_PATH='$(LUA_PATH_MAKE)' LUA_CPATH='$(LUA_CPATH_MAKE)' busted --helper=spec/use_slow_parser.moon + +build_test_outputs: build + BUILD=1 busted spec/lang_spec.moon + +local: build + LUA_PATH='$(LUA_PATH_MAKE)' LUA_CPATH='$(LUA_CPATH_MAKE)' $(LUAROCKS) make --local moonscript-dev-1.rockspec watch: - moonc -w moon/ moonscript/ + moonc moon/ moonscript/ && moonc -w moon/ moonscript/ + +lint: + moonc -l moonscript moon bin + +count: + wc -l $$(git ls-files | grep 'moon$$') | sort -n | tail + +# Binary build targets for local verification (Linux only) +lua_modules: + luarocks install argparse --tree=lua_modules + +lua-$(LUA_SRC_VERSION)/src/liblua.a: + curl -L -O https://www.lua.org/ftp/lua-$(LUA_SRC_VERSION).tar.gz + tar -xzf lua-$(LUA_SRC_VERSION).tar.gz + cd lua-$(LUA_SRC_VERSION)/src && make liblua.a MYCFLAGS=-DLUA_USE_POSIX + +lpeg-$(LPEG_VERSION)/lptree.c: + curl -L -o lpeg.tar.gz https://www.inf.puc-rio.br/~roberto/lpeg/lpeg-$(LPEG_VERSION).tar.gz + tar -xzf lpeg.tar.gz + +luafilesystem-$(LFS_VERSION)/src/lfs.c: + curl -L -o luafilesystem.tar.gz https://github.com/keplerproject/luafilesystem/archive/v$(LFS_VERSION).tar.gz + tar -xzf luafilesystem.tar.gz + +bin/binaries/moonscript.h: moonscript/*.lua moon/*.lua + bin/splat.moon -x moonscript.parse.slow -x moonscript.parse.grammar moonscript moon > moonscript.lua + xxd -i moonscript.lua > $@ + rm moonscript.lua + +bin/binaries/moon.h: bin/moon + awk 'FNR>1' bin/moon > moon.lua + xxd -i moon.lua > $@ + rm moon.lua + +bin/binaries/argparse.h: lua_modules + bin/splat.moon --strip-prefix -l argparse $$(find lua_modules/share/lua -name "argparse.lua" -exec dirname {} \; | head -1) > bin/binaries/argparse.lua + xxd -i -n argparse_lua bin/binaries/argparse.lua > $@ + +bin/binaries/moonc.h: bin/moonc + awk 'FNR>1' bin/moonc > moonc.lua + xxd -i moonc.lua > $@ + rm moonc.lua + +dist/moon: lua-$(LUA_SRC_VERSION)/src/liblua.a lpeg-$(LPEG_VERSION)/lptree.c bin/binaries/moonscript.h bin/binaries/moon.h bin/binaries/argparse.h bin/binaries/moon.c bin/binaries/moonscript.c + mkdir -p dist + gcc -static -o dist/moon \ + -Ilua-$(LUA_SRC_VERSION)/src/ \ + -Ilpeg-$(LPEG_VERSION)/ \ + -Ibin/binaries/ \ + bin/binaries/moon.c \ + bin/binaries/moonscript.c \ + moonscript/parse/native.c \ + lpeg-$(LPEG_VERSION)/lpvm.c \ + lpeg-$(LPEG_VERSION)/lpcap.c \ + lpeg-$(LPEG_VERSION)/lptree.c \ + lpeg-$(LPEG_VERSION)/lpcode.c \ + lpeg-$(LPEG_VERSION)/lpprint.c \ + lua-$(LUA_SRC_VERSION)/src/liblua.a \ + -lm -ldl + +dist/moonc: lua-$(LUA_SRC_VERSION)/src/liblua.a lpeg-$(LPEG_VERSION)/lptree.c luafilesystem-$(LFS_VERSION)/src/lfs.c bin/binaries/moonscript.h bin/binaries/moonc.h bin/binaries/argparse.h bin/binaries/moonc.c bin/binaries/moonscript.c + mkdir -p dist + gcc -static -o dist/moonc \ + -Ilua-$(LUA_SRC_VERSION)/src/ \ + -Ilpeg-$(LPEG_VERSION)/ \ + -Ibin/binaries/ \ + bin/binaries/moonc.c \ + bin/binaries/moonscript.c \ + moonscript/parse/native.c \ + lpeg-$(LPEG_VERSION)/lpvm.c \ + lpeg-$(LPEG_VERSION)/lpcap.c \ + lpeg-$(LPEG_VERSION)/lptree.c \ + lpeg-$(LPEG_VERSION)/lpcode.c \ + lpeg-$(LPEG_VERSION)/lpprint.c \ + luafilesystem-$(LFS_VERSION)/src/lfs.c \ + lua-$(LUA_SRC_VERSION)/src/liblua.a \ + -lm -ldl + +test_binary: dist/moon + dist/moon diff --git a/README.md b/README.md index c7461be8..251205be 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,83 @@ # MoonScript +[![MoonScript](https://leafo.net/dump/sailormoonscript.png)](https://moonscript.org) + + +[![spec](https://github.com/leafo/moonscript/workflows/spec/badge.svg)](https://github.com/leafo/moonscript/actions?query=workflow%3Aspec) [![Build status](https://ci.appveyor.com/api/projects/status/f5prpi4wvytul290/branch/binaries?svg=true)](https://ci.appveyor.com/project/leafo/moonscript/branch/binaries) + + +[![](https://leafo.net/dump/twitch-banner.svg)](https://www.twitch.tv/moonscript) + MoonScript is a programmer friendly language that compiles into -[Lua](http://www.lua.org/). It gives you the power of the fastest scripting -language combined with a rich set of features. +[Lua](https://www.lua.org/). It gives you the power of the fastest scripting +language combined with a rich set of features. It runs on Lua 5.1 and above, +including alternative runtimes like LuaJIT. + +See . + +Online demo/compiler at . + +## Join Our Community + +We have a Discord for those interested in MoonScript and related projects. You can join us here: + +## Contributing + +MoonScript is a self-hosted compiler, meaning it's written in MoonScript itself. When contributing, please follow the following guidelines: + +1. Edit `.moon` files, never modify the alongside `.lua` files directly +2. After making changes to `.moon` files, run the compiler to regenerate the corresponding `.lua` files +3. Both `.moon` and `.lua` files are included in the repository to ensure that: + - Users can install and use MoonScript without having to compile it themselves + - The compiler bootstrapping process works consistently + +It's helpful to have a separate installation of MoonScript should you break +something and you need to re-build the MoonScript with a working version of +MoonScript. You can check out the repo in another directory, or install it +using LuaRocks to have a separate working version. + +### The parser + +The parser is defined as a [pgen](https://github.com/leafo/pgen) grammar in +`moonscript/parse/grammar.moon`, which is compiled to a native C module +(`moonscript/parse/native.c`) and a pure Lua equivalent +(`moonscript/parse/slow.lua`, unused at runtime, kept for a future Lua-only +distribution). Both generated files are checked in. After changing the +grammar, run `make generate` to rebuild them (requires the pgen and +clang-format). + +## Running Tests + +Tests are written in MoonScript and use [Busted](https://olivinelabs.com/busted/). +In order to run the tests you must have MoonScript and [Loadkit](https://github.com/leafo/loadkit) installed. + +To run tests, execute from the root directory: + +```bash +busted +``` + +Writing specs is a bit more complicated. Check out [the spec writing guide](spec/README.md). + +## Binaries + +Precompiled versions of MoonScript are provided for Windows. You can find them +in the [GitHub releases page](https://github.com/leafo/moonscript/releases). +(Scroll down to the `win32-` tags. + +The build code can be found in the [`binaries` +branch](https://github.com/leafo/moonscript/tree/binaries) -See . +## Editor Support -Online demo/compiler at . +* [Vim](https://github.com/leafo/moonscript-vim) +* [Textadept](https://github.com/leafo/moonscript-textadept) +* [Sublime/Textmate](https://github.com/leafo/moonscript-tmbundle) +* [Emacs](https://github.com/k2052/moonscript-mode) ## License (MIT) -Copyright (C) 2011 by Leaf Corcoran +Copyright (C) 2025 by Leaf Corcoran Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -28,4 +95,4 @@ 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. \ No newline at end of file +THE SOFTWARE. diff --git a/bin/binaries/moon.c b/bin/binaries/moon.c new file mode 100644 index 00000000..911b3695 --- /dev/null +++ b/bin/binaries/moon.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +#include "moon.h" // the CLI script + +// from moonscript.c +extern int luaopen_moonscript(lua_State *l); + +int main(int argc, char **argv) { + lua_State *l = luaL_newstate(); + luaL_openlibs(l); + + // Load moonscript (this also loads lpeg) + luaopen_moonscript(l); + lua_pop(l, 1); + +#ifdef MOON_BUILD_COMMIT + // Build metadata passed in by CI; moonscript.version prints it with -v + lua_newtable(l); + lua_pushstring(l, LUA_RELEASE); + lua_setfield(l, -2, "lua"); + lua_pushstring(l, MOON_BUILD_COMMIT); + lua_setfield(l, -2, "commit"); + lua_pushstring(l, MOON_BUILD_TIME); + lua_setfield(l, -2, "time"); + lua_setglobal(l, "MOON_BUILD_INFO"); +#endif + + // Set up arg table + lua_newtable(l); + lua_pushstring(l, "moon"); + lua_rawseti(l, -2, -1); + for (int i = 0; i < argc; i++) { + lua_pushstring(l, argv[i]); + lua_rawseti(l, -2, i); + } + lua_setglobal(l, "arg"); + + // Load and execute the moon CLI script + if (luaL_loadbuffer(l, (const char *)moon_lua, moon_lua_len, "moon") != 0) { + fprintf(stderr, "Failed to load moon: %s\n", lua_tostring(l, -1)); + return 1; + } + if (lua_pcall(l, 0, 0, 0) != 0) { + fprintf(stderr, "Error: %s\n", lua_tostring(l, -1)); + return 1; + } + + lua_close(l); + return 0; +} diff --git a/bin/binaries/moonc.c b/bin/binaries/moonc.c new file mode 100644 index 00000000..595360d6 --- /dev/null +++ b/bin/binaries/moonc.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include + +#include "moonc.h" // the CLI script + +// from moonscript.c +extern int luaopen_moonscript(lua_State *l); + +// from lfs.c +extern int luaopen_lfs(lua_State *l); + +int main(int argc, char **argv) { + lua_State *l = luaL_newstate(); + luaL_openlibs(l); + + // Load moonscript (this also loads lpeg and argparse) + luaopen_moonscript(l); + lua_pop(l, 1); + + // Load luafilesystem and register it in package.loaded + int nresults = luaopen_lfs(l); + if (nresults > 0) { + lua_getglobal(l, "package"); + lua_getfield(l, -1, "loaded"); + lua_pushvalue(l, -3); // push lfs table + lua_setfield(l, -2, "lfs"); + lua_pop(l, 2); // pop loaded, package + } + lua_pop(l, nresults); + +#ifdef MOON_BUILD_COMMIT + // Build metadata passed in by CI; moonscript.version prints it with -v + lua_newtable(l); + lua_pushstring(l, LUA_RELEASE); + lua_setfield(l, -2, "lua"); + lua_pushstring(l, MOON_BUILD_COMMIT); + lua_setfield(l, -2, "commit"); + lua_pushstring(l, MOON_BUILD_TIME); + lua_setfield(l, -2, "time"); + lua_setglobal(l, "MOON_BUILD_INFO"); +#endif + + // Set up arg table + lua_newtable(l); + lua_pushstring(l, "moonc"); + lua_rawseti(l, -2, -1); + for (int i = 0; i < argc; i++) { + lua_pushstring(l, argv[i]); + lua_rawseti(l, -2, i); + } + lua_setglobal(l, "arg"); + + // Load and execute the moonc CLI script + if (luaL_loadbuffer(l, (const char *)moonc_lua, moonc_lua_len, "moonc") != 0) { + fprintf(stderr, "Failed to load moonc: %s\n", lua_tostring(l, -1)); + return 1; + } + if (lua_pcall(l, 0, 0, 0) != 0) { + fprintf(stderr, "Error: %s\n", lua_tostring(l, -1)); + return 1; + } + + lua_close(l); + return 0; +} diff --git a/bin/binaries/moonscript.c b/bin/binaries/moonscript.c new file mode 100644 index 00000000..53f4fa72 --- /dev/null +++ b/bin/binaries/moonscript.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +#include "moonscript.h" +#include "argparse.h" + +// put whatever is on top of stack into package.loaded under name if something +// is not already there +void setloaded(lua_State* l, const char* name) { + int top = lua_gettop(l); + lua_getglobal(l, "package"); + lua_getfield(l, -1, "loaded"); + lua_getfield(l, -1, name); + if (lua_isnil(l, -1)) { + lua_pop(l, 1); + lua_pushvalue(l, top); + lua_setfield(l, -2, name); + } + + lua_settop(l, top); +} + +extern int luaopen_lpeg(lua_State *l); +extern int luaopen_moonscript_parse_native(lua_State *l); + +LUALIB_API int luaopen_moonscript(lua_State *l) { + luaopen_lpeg(l); + setloaded(l, "lpeg"); + + // Load argparse (splat output sets up package.preload) + if (luaL_loadbuffer(l, (const char *)argparse_lua, argparse_lua_len, "argparse.lua") == 0) { + lua_call(l, 0, 0); + } + + // Register the moonscript modules into package.preload + if (luaL_loadbuffer(l, (const char *)moonscript_lua, moonscript_lua_len, "moonscript.lua") != 0) { + return 0; + } + lua_call(l, 0, 0); + + // The native parser runs its callback chunks at load, and those require + // moonscript.parse.tree: package.preload must be populated first or the + // require escapes to the filesystem + luaopen_moonscript_parse_native(l); + setloaded(l, "moonscript.parse.native"); + + lua_getglobal(l, "require"); + lua_pushstring(l, "moonscript"); + lua_call(l, 1, 1); + return 1; +} diff --git a/bin/moon b/bin/moon index e5b3119a..759cae8a 100755 --- a/bin/moon +++ b/bin/moon @@ -1,86 +1,143 @@ #!/usr/bin/env lua - -require "alt_getopt" -require "moonscript.errors" -require "moonscript" - -local util = require "moonscript.util" - --- moonloader and repl -local opts, ind = alt_getopt.get_opts(arg, "cvhd", { version = "v", help = "h" }) - -local help = [=[Usage: %s [options] [script [args]] - - -h Print this message - -d Disable stack trace rewriting - -v Print version -]=] - -local function print_err(...) - msg = table.concat({...}, "\t") - io.stderr:write(msg .. "\n") +local argparse = require("argparse") +local moonscript = require("moonscript.base") +local util = require("moonscript.util") +local errors = require("moonscript.errors") +local unpack = util.unpack +local argparser = argparse()({ + name = "moon" +}) +argparser:argument("script"):args("?") +argparser:argument("args"):args("*") +argparser:flag("--coverage -c", "Collect and print code coverage") +argparser:flag("-d", "Disable stack trace rewriting") +argparser:option("--execute -e", "Execute MoonScript code string") +argparser:flag("--version -v", "Print version information") +local base = 0 +local _list_0 = arg +for _index_0 = 1, #_list_0 do + local flag = _list_0[_index_0] + base = base + 1 + if flag:sub(1, 1) ~= "-" then + break + end end - -local function print_help(err) - if err then print("Error: "..err) end - print(help:format(arg[0])) - os.exit() -end - -if opts.h then print_help() end - -if opts.v then - local v = require "moonscript.version" - v.print_version() - os.exit() -end - -local script_fname = arg[ind] -if not script_fname then - print_help("repl not yet supported") - return -end - -local new_arg = { - [-1] = arg[0], - [0] = arg[ind], - select(ind + 1, unpack(arg)) +local args = { + unpack(arg, 1, base) } - -local moonscript_chunk, lua_parse_error -local passed, err = pcall(function() - moonscript_chunk, lua_parse_error = moonscript.loadfile(script_fname) -end) - -if not passed then - print_err(err) - os.exit(1) -end - -if not moonscript_chunk then - if lua_parse_error then - print_err(lua_parse_error) - else - print_err("Can't find file: " .. script_fname) - end - os.exit(1) +local opts = argparser:parse(args) +local print_err +print_err = function(...) + local msg = table.concat((function(...) + local _accum_0 = { } + local _len_0 = 1 + local _list_1 = { + ... + } + for _index_0 = 1, #_list_1 do + local v = _list_1[_index_0] + _accum_0[_len_0] = tostring(v) + _len_0 = _len_0 + 1 + end + return _accum_0 + end)(...), "\t") + return io.stderr:write(msg .. "\n") end - -getfenv(moonscript_chunk).arg = new_arg - -if not opts.d then - local err, trace - xpcall(function() moonscript_chunk(unpack(new_arg)) end, function(_err) - err = _err - trace = debug.traceback("", 2) - end) - - if err then - local traceback = util.trim(trace) - traceback = moonscript.errors.truncate_traceback(traceback) - print_err(moonscript.errors.rewrite_traceback(traceback, err)) - os.exit(1) - end -else - moonscript_chunk(unpack(new_arg)) +local run +run = function() + if opts.version then + require("moonscript.version").print_version() + os.exit() + end + args = { + unpack(arg, base + 1) + } + args[-1] = arg[0] + local moonscript_chunk, lua_parse_error + if opts.execute then + args[0] = "-e" + local passed, err = pcall(function() + moonscript_chunk, lua_parse_error = moonscript.loadstring(opts.execute, "=(command line)", { + implicitly_return_root = false + }) + end) + if not (passed) then + print_err(err) + os.exit(1) + end + if not (moonscript_chunk) then + if lua_parse_error then + print_err(lua_parse_error) + else + print_err("Failed to compile: " .. tostring(opts.execute)) + end + os.exit(1) + end + else + local script_fname = opts.script + if not (script_fname) then + print_err("Usage: moon [options] script [args]") + print_err("Use 'moon --help' for more information.") + os.exit(1) + end + args[0] = script_fname + local passed, err = pcall(function() + moonscript_chunk, lua_parse_error = moonscript.loadfile(script_fname, { + implicitly_return_root = false + }) + end) + if not (passed) then + print_err(err) + os.exit(1) + end + if not (moonscript_chunk) then + if lua_parse_error then + print_err(lua_parse_error) + else + print_err("Can't file file: " .. tostring(script_fname)) + end + os.exit(1) + end + end + util.getfenv(moonscript_chunk).arg = args + local run_chunk + run_chunk = function() + moonscript.insert_loader() + moonscript_chunk(unpack(args)) + return moonscript.remove_loader() + end + if opts.d then + return run_chunk() + end + local err, trace, cov + if opts.coverage then + print("starting coverage") + local coverage = require("moonscript.cmd.coverage") + cov = coverage.CodeCoverage() + cov:start() + end + xpcall(run_chunk, function(_err) + err = _err + trace = debug.traceback("", 2) + end) + if err then + local truncated = errors.truncate_traceback(util.trim(trace)) + local rewritten = errors.rewrite_traceback(truncated, err) + if rewritten then + print_err(rewritten) + else + print_err(table.concat({ + err, + util.trim(trace) + }, "\n")) + end + return os.exit(1) + else + if cov then + cov:stop() + return cov:print_results() + end + end end +return run() +-- vim: set filetype=lua: diff --git a/bin/moon-tags b/bin/moon-tags new file mode 100755 index 00000000..451fbe22 --- /dev/null +++ b/bin/moon-tags @@ -0,0 +1,221 @@ +#!/usr/bin/env moon + +HEADER = [[ +!_TAG_FILE_FORMAT 2 /extended format/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_PROGRAM_AUTHOR leaf corcoran /leafot@gmail.com/ +!_TAG_PROGRAM_NAME MoonTags // +!_TAG_PROGRAM_URL https://github.com/leafo/moonscript /GitHub repository/ +!_TAG_PROGRAM_VERSION 0.0.1 // +]] + +-- see `ctags --list-kinds` for examples of kinds +-- see `ctags --list-fields` + +argparse = require "argparse" + +parser = argparse "moon-tags", "Generate ctags style tags file for MoonScript files" +parser\argument("files", "MoonScript files to generate tags for")\args "+" +parser\flag "--include-line", "Include line number field for each tag" +parser\flag "--lapis", "Support extracting lapis routes" +parser\flag "--no-header", "Don't print the header" + +args = parser\parse [v for _, v in ipairs _G.arg] + +TAGS = {} -- the final output of tags + +import P, R, S, C, Cc, Cg, Cb, Ct, Cs, V from require "lpeg" + +Break = P"\r"^-1 * P"\n" +Stop = Break + -1 +AlphaNum = R "az", "AZ", "09", "__" +Name = C R("az", "AZ", "__") * AlphaNum^0 + +-- captures an indentation, returns indent depth +Indent = C(S"\t "^0) / (str) -> + with sum = 0 + for v in str\gmatch "[\t ]" + switch v + when " " + sum += 1 + when "\t" + sum += 4 + +mark = (name) -> (...) -> {name, ...} + +-- quote delimited string, no interpolation parsing since we don't have the +-- full grammar +simple_string = (delim) -> + inner = P("\\#{delim}") + P"\\\\" + (1 - P delim) + C(P delim) * C(inner^0) * P(delim) / mark "string" + +-- consome the rest of the file +until_end = (1 - Stop)^0 +whitespace = S"\t " -- not including newline +ignore_line = Ct until_end -- tag it for empty line + +SingleString = simple_string "'" +DoubleString = simple_string '"' +String = SingleString + DoubleString + +-- we have to do this double Ct to capture both the full line and the grouped captures +Type = (name) -> Cg Cc(name), "type" +Line = (type_name, p) -> Ct C Ct Cg(Indent, "indent") * p * Type type_name + +method = P { P"=>" + P(1 - Stop) * V(1) } +func = P { P"->" + P"=>" + P(1 - Stop) * V(1) } + +self_prefix = Cg(P("@") * Cc(true), "self") + +-- this matches end-of-file return table convention for module files to figure +-- out what names are exported +export_list = Ct P"{" * P { + P"}" + ((P":" * Name) + (P(1) - P"}")) * V(1) +} + +eof_exports = P { export_list * S(" \t\r\n")^0 * P(-1) + P(1) * V(1) } + +-- convert a parsed string to the value the string represents +StringVal = C(String) / (str) -> loadstring("return " .. str)() + +class_line = Line "class", P"class" * whitespace^1 * Cg(Name, "tag") * until_end +class_property = Line "property", self_prefix^-1 * Cg(Name, "tag") * P":" * whitespace^0 * Cg(String, "value")^0 * until_end +class_method = Line("method", P("@")^-1 * Cg(Name, "tag") * P":" * method) * until_end +function_def = Line("function", Cg(Name, "tag") * whitespace^0 * P"=" * func) * until_end +lapis_route = Line "lapis-route", P"[" * Cg(Name + StringVal, "tag") * P":" * whitespace^0 * Cg(String, "route") * whitespace^0 * P("]:") * until_end + +line_types = class_line + class_method + class_property + function_def + +if args.lapis + line_types += lapis_route + +parse_lines = Ct P { + (line_types + ignore_line) * (P(-1) + Break * V(1)) +} + +escape_tagaddress = (line_text) -> + replacements = S([[\/.$^]]) / [[\%0]]+ P("\t") / [[\t]] + P("\r") / [[\r]] + P("\n") / [[\n]] + Cs((replacements + 1)^0)\match line_text + +import types from require "tableshape" + +class_field = types.partial { + "self": true + tag: types.string\tag "name" + value: types.partial { + "string" + types.string + types.string\tag "value" -- TODO: will need to un-escape this + } +} + +for fname in *args.files + file = assert io.open fname + contents = assert file\read "*a" + exports = {e, true for e in *eof_exports\match(contents) or {}} + + lines = assert parse_lines\match contents + + class_stack = {} + + push_class = (cls) -> + assert cls.type == "class", "not a class match" + -- remove classes that are longer in scope due to indentation + for i=#class_stack,1,-1 + top = class_stack[i] + + if cls.indent <= top.indent + table.remove class_stack, i + else + break + + table.insert class_stack, cls + + -- find the class this property is associated with based on change in indent + -- the expeted indent is written to `step` on the first proprety + find_class = (property) -> + for i=#class_stack,1,-1 + top = class_stack[i] + step = property.indent - top.indent + + if step > 0 + if top.step == nil + top.step = step + + if step == top.step + return top + + for line_no, line in ipairs lines + continue unless next line + + {line_text, properties} = line + + fields = {"language:moon"} + if args.include_line + table.insert fields, 1, "line:#{line_no}" + + switch properties.type + when "lapis-route" + if cls = find_class properties + prefix = if cls.fields + cls.fields.name + + table.insert TAGS, { + "#{prefix or ""}#{properties.tag}" + fname + "/^#{escape_tagaddress line_text}/;\"" + "f" + table.concat fields, " " + } + + when "property" + -- this is necessary to register the correct indent level for the class + cls = find_class properties + + -- record the fields into the class object so they can be referenced by + -- other tags. Note this is code-order dependent + if cls and args.lapis + if field = class_field properties + cls.fields or= {} + cls.fields[field.name] = field.value + + when "function" + if exports[properties.tag] and properties.indent == 0 + table.insert TAGS, { + properties.tag + fname + -- note we don't use $ here + "/^#{escape_tagaddress line_text}/;\"" + "f" + table.concat fields, " " + } + + when "method" + if cls = find_class properties + table.insert fields, "class:#{cls.tag}" + + table.insert TAGS, { + properties.tag + fname + -- note we don't use $ here + "/^#{escape_tagaddress line_text}/;\"" + "f" + table.concat fields, " " + } + when "class" + push_class properties + + table.insert TAGS, { + properties.tag + fname + "/^#{escape_tagaddress line_text}$/;\"" + "c" + table.concat fields, " " + } + +unless args.no_header + print HEADER + +tag_lines = [table.concat(t, "\t") for t in *TAGS] +table.sort tag_lines +print table.concat tag_lines, "\n" diff --git a/bin/moon.moon b/bin/moon.moon new file mode 100644 index 00000000..fcb8de89 --- /dev/null +++ b/bin/moon.moon @@ -0,0 +1,124 @@ +argparse = require "argparse" + +moonscript = require "moonscript.base" +util = require "moonscript.util" +errors = require "moonscript.errors" + +unpack = util.unpack + +argparser = argparse! name: "moon" + +argparser\argument("script")\args "?" +argparser\argument("args")\args "*" +argparser\flag "--coverage -c", "Collect and print code coverage" +argparser\flag "-d", "Disable stack trace rewriting" +argparser\option "--execute -e", "Execute MoonScript code string" +argparser\flag "--version -v", "Print version information" + +base = 0 +for flag in *arg + base += 1 + break if flag\sub(1, 1) != "-" +args = {unpack arg, 1, base} +opts = argparser\parse args + +print_err = (...) -> + msg = table.concat [tostring v for v in *{...}], "\t" + io.stderr\write msg .. "\n" + +run = -> + + if opts.version + require("moonscript.version").print_version! + os.exit! + + args = {unpack arg, base + 1} + args[-1] = arg[0] + + local moonscript_chunk, lua_parse_error + + if opts.execute + args[0] = "-e" + + passed, err = pcall -> + moonscript_chunk, lua_parse_error = moonscript.loadstring opts.execute, "=(command line)", { + implicitly_return_root: false + } + + unless passed + print_err err + os.exit 1 + + unless moonscript_chunk + if lua_parse_error + print_err lua_parse_error + else + print_err "Failed to compile: #{opts.execute}" + os.exit 1 + else + script_fname = opts.script + + unless script_fname + print_err "Usage: moon [options] script [args]" + print_err "Use 'moon --help' for more information." + os.exit 1 + + args[0] = script_fname + + passed, err = pcall -> + moonscript_chunk, lua_parse_error = moonscript.loadfile script_fname, { + implicitly_return_root: false + } + + unless passed + print_err err + os.exit 1 + + unless moonscript_chunk + if lua_parse_error + print_err lua_parse_error + else + print_err "Can't file file: #{script_fname}" + os.exit 1 + + util.getfenv(moonscript_chunk).arg = args + + run_chunk = -> + moonscript.insert_loader! + moonscript_chunk unpack args + moonscript.remove_loader! + + if opts.d + return run_chunk! + + local err, trace, cov + + if opts.coverage + print "starting coverage" + coverage = require "moonscript.cmd.coverage" + cov = coverage.CodeCoverage! + cov\start! + + xpcall run_chunk, (_err) -> + err = _err + trace = debug.traceback "", 2 + + if err + truncated = errors.truncate_traceback util.trim trace + rewritten = errors.rewrite_traceback truncated, err + + if rewritten + print_err rewritten + else + -- failed to rewrite, show original + print_err table.concat { + err, + util.trim trace + }, "\n" + os.exit 1 + else + if cov + cov\stop! + cov\print_results! + +run! diff --git a/bin/moonc b/bin/moonc old mode 100755 new mode 100644 index f18d43f5..6cff7443 --- a/bin/moonc +++ b/bin/moonc @@ -1,382 +1,290 @@ #!/usr/bin/env lua -module("moonscript", package.seeall) +local argparse = require "argparse" +local lfs = require "lfs" -require "moonscript.parse" -require "moonscript.compile" -require "moonscript.util" +local parser = argparse() -require "alt_getopt" -require "lfs" +parser:flag("-l --lint", "Perform a lint on the file instead of compiling") -local opts, ind = alt_getopt.get_opts(arg, "vhwt:pTb", { - print = "p", tree = "T", version = "v", help = "h" -}) +-- may be nil when an older moonscript.cmd.lint is loaded from the module path +local all_lint_stages = require("moonscript.cmd.lint").LINT_STAGES +local all_lint_formats = require("moonscript.cmd.lint").LINT_FORMATS -local read_stdin = arg[1] == "--" +local lint_stage_opt = parser:option("--lint-stage", + "Limit lint to the given stage name (repeatable)"):count("*") +local exclude_lint_stage_opt = parser:option("--exclude-lint-stage", + "Lint with all stages except the given stage name (repeatable)"):count("*") +local lint_format_opt = parser:option("--lint-format", + "Set the output format of lint results") -local polling_rate = 1.0 - -local help = [[Usage: %s [options] files... - - -h Print this message - -w Watch file/directory - -t path Specify where to place compiled files - -p Write output to standard out - -T Write parse tree instead of code (to stdout) - -b Dump parse and compile time (doesn't write output) - -v Print version - - -- Read from standard in, print to standard out - (Must be first and only argument) -]] - -if opts.v then - local v = require "moonscript.version" - v.print_version() - os.exit() +if all_lint_stages then + lint_stage_opt:choices(all_lint_stages) + exclude_lint_stage_opt:choices(all_lint_stages) end -function print_help(err) - if err then print("Error: "..err) end - print(help:format(arg[0])) - os.exit() +if all_lint_formats then + lint_format_opt:choices(all_lint_formats) end -function mkdir(path) - local chunks = util.split(path, "/") - local accum +parser:mutex(lint_stage_opt, exclude_lint_stage_opt) - for _, dir in ipairs(chunks) do - accum = accum and accum.."/"..dir or dir - lfs.mkdir(accum) - end - - return lfs.attributes(path, "mode") -end - -function normalize(path) - return path:match("(.-)/*$").."/" -end +parser:flag("-v --version", "Print version"):action(function() + require("moonscript.version").print_version() + os.exit(0) +end) +parser:flag("-w --watch", "Watch file/directory for updates") +parser:option("--transform", "Transform syntax tree with module") -function get_dir(fname) - return fname:match("^(.-)[^/]*$") -end +parser:mutex( + parser:option("-t --output-to", "Specify where to place compiled files"), + parser:option("-o", "Write output to file"), + parser:flag("-p", "Write output to standard output"), + parser:flag("-T", "Write parse tree instead of code (to stdout)"), + parser:flag("-b", "Write parse and compile time instead of code(to stdout)"), + parser:flag("-X", "Write line rewrite map instead of code (to stdout)") +) --- convert .moon to .lua -function convert_path(path) - return (path:gsub("%.moon$", ".lua")) -end +parser:flag("-", + "Read from standard in, print to standard out (Must be only argument)") -function msg(...) - if not opts.p then - print(...) - end -end +local read_stdin = arg[1] == "-" -- luacheck: ignore 113 -local gettime = nil -if opts.b then - pcall(function() - require "socket" - gettime = socket.gettime - end) - - function format_time(time) - return ("%.3fms"):format(time*1000) - end - if not gettime then - print_help"LuaSocket needed for benchmark" - end +if not read_stdin then + parser:argument("file/directory"):args("+") else - gettime = function() return 0 end -end - -function write_file(fname, code) - if opts.p then - if code ~= "" then print(code) end - else - mkdir(get_dir(fname)) - local out_f = io.open(fname, "w") - if not out_f then - return nil, "Failed to write output: "..fname - end - - out_f:write(code.."\n") - out_f:close() - end - return true -end - -function compile_file(text, fname) - local parse_time = gettime() - local tree, err = parse.string(text) - parse_time = gettime() - parse_time - - if not tree then - return nil, err - end - - if opts.T then - opts.p = true - dump.tree(tree) - return "" - else - local compile_time = gettime() - local code, err, pos = compile.tree(tree) - compile_time = gettime() - compile_time - if not code then - return nil, compile.format_error(err, pos, text) - end - - if opts.b then - opts.p = true - return table.concat({ - fname, - "Parse time \t" .. format_time(parse_time), - "Compile time\t" .. format_time(compile_time), - "" - }, "\n") - end - - return code - end + if arg[2] ~= nil then + io.stderr:write("- must be the only argument\n") + os.exit(1) + end end -function compile_and_write(from, to) - local f = io.open(from) - if not f then - return nil, "Can't find file" - end - local text = f:read("*a") - - local code, err = compile_file(text, from) - if not code then - return nil, err - end - - return write_file(to, code) +local opts = read_stdin and {} or parser:parse() + +-- options passed to lint_file, stages: nil runs all of them +local lint_opts +do + local lint_stages + local included, excluded = opts.lint_stage, opts.exclude_lint_stage + if included and #included > 0 then + lint_stages = included + elseif excluded and #excluded > 0 then + local skip = {} + for _, name in ipairs(excluded) do + skip[name] = true + end + + lint_stages = {} + for _, name in ipairs(all_lint_stages or {}) do + if not skip[name] then + table.insert(lint_stages, name) + end + end + end + + lint_opts = { + stages = lint_stages, + format = opts.lint_format, + } end -function scan_directory(root, collected) - root = normalize(root) - collected = collected or {} - - for fname in lfs.dir(root) do - if not fname:match("^%.") then - local full_path = root..fname - - if lfs.attributes(full_path, "mode") == "directory" then - scan_directory(full_path, collected) - end - - if fname:match(".moon") then - table.insert(collected, full_path) - end - end - end - - return collected +function log_msg(...) + if not opts.p then + io.stderr:write(table.concat({...}, " ") .. "\n") + end end -function append(a, b) - for _, v in ipairs(b) do - table.insert(a, v) - end +local moonc = require("moonscript.cmd.moonc") +local util = require "moonscript.util" +local normalize_dir = moonc.normalize_dir +local compile_and_write = moonc.compile_and_write +local path_to_target = moonc.path_to_target + +local function scan_directory(root, collected) + root = normalize_dir(root) + collected = collected or {} + + for fname in lfs.dir(root) do + if not fname:match("^%.") then + local full_path = root..fname + + if lfs.attributes(full_path, "mode") == "directory" then + scan_directory(full_path, collected) + elseif fname:match("%.moon$") then + table.insert(collected, full_path) + end + end + end + + return collected end -function remove_dups(tbl) - local hash = {} - local final = {} +local function remove_dups(tbl, key_fn) + local hash = {} + local final = {} - for _, v in ipairs(tbl) do - if not hash[v] then - table.insert(final, v) - hash[v] = true - end - end + for _, v in ipairs(tbl) do + local dup_key = key_fn and key_fn(v) or v + if not hash[dup_key] then + table.insert(final, v) + hash[dup_key] = true + end + end - return final + return final end -function get_files(fname, files) - files = files or {} - - if lfs.attributes(fname, "mode") == "directory" then - append(files, scan_directory(fname)) - else - table.insert(files, "./"..fname) - end - - return files +-- creates tuples of input and target +local function get_files(fname, files) + files = files or {} + + if lfs.attributes(fname, "mode") == "directory" then + for _, sub_fname in ipairs(scan_directory(fname)) do + table.insert(files, { + sub_fname, + path_to_target(sub_fname, opts.output_to, fname) + }) + end + else + table.insert(files, { + fname, + path_to_target(fname, opts.output_to) + }) + end + + return files end -if opts.h then print_help() end - if read_stdin then - local text = io.stdin:read("*a") - local tree, err = parse.string(text) - if not tree then error(err) end - local code, err, pos = compile.tree(tree) + local parse = require "moonscript.parse" + local compile = require "moonscript.compile" - if not code then - error(compile.format_error(err, pos, text)) - end + local text = io.stdin:read("*a") + local tree, err = parse.string(text) - print(code) - os.exit() -end - -local inputs = {} -for i = ind, #arg do - table.insert(inputs, arg[i]) -end + if not tree then error(err) end + local code, err, pos = compile.tree(tree) -if #inputs == 0 then - print_help("No files specified") -end + if not code then + error(compile.format_error(err, pos, text)) + end -local target_dir = "." -if opts.t then - if mkdir(opts.t) ~= "directory" then - print_help("Invalid target dir") - end - target_dir = opts.t + print(code) + os.exit() end -target_dir = target_dir.."/" +local inputs = opts["file/directory"] local files = {} for _, input in ipairs(inputs) do - get_files(input, files) + get_files(input, files) end -files = remove_dups(files) - -function get_sleep_func() - local sleep - if not pcall(function() - require "socket" - sleep = socket.sleep - end) then - sleep = moonscript._sleep - end - if not sleep then - error("Missing sleep function; install LuaSocket") - end - return sleep -end +files = remove_dups(files, function(f) + return f[2] +end) +-- returns an iterator that returns files that have been updated +local function create_watcher(files) + local watchers = require("moonscript.cmd.watchers") -function plural(count, word) - if count ~= 1 then - word = word .. "s" - end - return table.concat({count, word}, " ") -end + if watchers.InotifyWacher:available() then + return watchers.InotifyWacher(files):each_update() + end --- returns an iterator that returns files that have been updated -function create_watcher(files) - local msg = "Starting watch loop (Ctrl-C to exit)" - - local inotify - pcall(function() - inotify = require "inotify" - end) - - if inotify then - local dirs = {} - for _, fname in ipairs(files) do - table.insert(dirs, get_dir(fname)) - end - dirs = remove_dups(dirs) - - return coroutine.wrap(function() - print(("%s with inotify [%s]"):format(msg, plural(#dirs, "dir"))) - - local wd_table = {} - local handle = inotify.init() - for _, dir in ipairs(dirs) do - local wd = handle:addwatch(dir, inotify.IN_CLOSE_WRITE) - wd_table[wd] = dir - end - - while true do - local events = handle:read() - if events then - for _, ev in ipairs(events) do - local fname = wd_table[ev.wd]..ev.name - if fname:match("%.moon$") then - coroutine.yield(fname) - end - end - else - break - end - end - end) - else - -- poll the filesystem instead - local sleep = get_sleep_func() - return coroutine.wrap(function() - print(("%s with polling [%s]"):format(msg, plural(#files, "file"))) - - local mod_time = {} - while true do - for _, file in ipairs(files) do - local time = lfs.attributes(file, "modification") - if not mod_time[file] then - mod_time[file] = time - else - if time ~= mod_time[file] then - if time > mod_time[file] then - coroutine.yield(file) - mod_time[file] = time - end - end - end - end - sleep(polling_rate) - end - end) - end + return watchers.SleepWatcher(files):each_update() end -if opts.w then - local watcher = create_watcher(files) - -- catches interrupt error for ctl-c - local protected = function() - local status, file = pcall(watcher) - if status then - return file - elseif file ~= "interrupted!" then - error(file) - end - end - - for fname in protected do - local target = target_dir..convert_path(fname) - local success, err = compile_and_write(fname, target) - if not success then - print() - print("Error:", fname) - print(err) - print() - else - msg("Built:", fname, "->", target) - end - end - - print "\nQuitting..." +if opts.watch then + -- build function to check for lint or compile in watch + local handle_file + if opts.lint then + local lint = require "moonscript.cmd.lint" + handle_file = function(fname) + return lint.lint_file(fname, lint_opts) + end + else + handle_file = compile_and_write + end + + local watcher = create_watcher(files) + -- catches interrupt error for ctl-c + local protected = function() + local status, file = true, watcher() + if status then + return file + elseif file ~= "interrupted!" then + error(file) + end + end + + for fname in protected do + local target = path_to_target(fname, opts.t) + + if opts.o then + target = opts.o + end + + local success, err = handle_file(fname, target) + if opts.lint then + if success then + io.stderr:write(success .. "\n\n") + elseif err then + io.stderr:write(fname .. "\n" .. err .. "\n\n") + end + elseif not success then + io.stderr:write(table.concat({ + "", + "Error: " .. fname, + err, + "\n", + }, "\n")) + elseif success == "build" then + log_msg("Built", fname, "->", target) + end + end + + io.stderr:write("\nQuitting...\n") +elseif opts.lint then + local has_linted_with_error; + local lint = require "moonscript.cmd.lint" + for _, tuple in pairs(files) do + local fname = tuple[1] + local res, err = lint.lint_file(fname, lint_opts) + if res then + has_linted_with_error = true + -- compact format lines identify their file, no separator needed + local sep = opts.lint_format == "compact" and "\n" or "\n\n" + io.stderr:write(res .. sep) + elseif err then + has_linted_with_error = true + io.stderr:write(fname .. "\n" .. err.. "\n\n") + end + end + if has_linted_with_error then + os.exit(1) + end else - for _, fname in ipairs(files) do - local success, err = compile_and_write(fname, target_dir..convert_path(fname)) - if not success then - print(fname, err) - os.exit(1) - else - msg("Built", fname) - end - end + for _, tuple in ipairs(files) do + local fname, target = util.unpack(tuple) + if opts.o then + target = opts.o + end + + local success, err = compile_and_write(fname, target, { + print = opts.p, + fname = fname, + benchmark = opts.b, + show_posmap = opts.X, + show_parse_tree = opts.T, + transform_module = opts.transform + }) + + if not success then + io.stderr:write(fname .. "\t" .. err .. "\n") + os.exit(1) + end + end end diff --git a/bin/splat.moon b/bin/splat.moon index 506a54ae..93d8a2f2 100755 --- a/bin/splat.moon +++ b/bin/splat.moon @@ -1,26 +1,24 @@ #!/usr/bin/env moon +argparse = require "argparse" --- concatenate a collection of lua modules into one +-- TODO: it would be cool if you could just point this at a luarocks tree, pass a list of top level module names, and it figures it out for you. +-- Perhaps even merge the header generation into here as well to avoid using xxd -require "lfs" -require "alt_getopt" - -import insert, concat from table -import dump, split from require "moonscript.util" +normalize = (path) -> + path\match("(.-)/*$").."/" -opts, ind = alt_getopt.get_opts arg, "l:", { - load: "l" -} +parser = argparse "splat.moon", "Concatenate a collection of Lua modules into a single file" +parser\option("--load -l", "Module names that will be load on require")\count "*" +parser\option("--exclude -x", "Module names to leave out of the output")\count "*" +parser\flag("--strip-prefix -s", "Strip directory prefix from module names") -if not arg[ind] - print "usage: splat [-l module_names] directory [directories...]" - os.exit! +parser\argument("directories", "Directories to scan for Lua modules")\args "+" -dirs = [a for a in *arg[ind,]] - -normalize = (path) -> - path\match("(.-)/*$").."/" +args = parser\parse [v for _, v in ipairs _G.arg] +dirs = args.directories +strip_prefix = args.strip_prefix +lfs = require "lfs" scan_directory = (root, patt, collected={}) -> root = normalize root for fname in lfs.dir root @@ -31,23 +29,24 @@ scan_directory = (root, patt, collected={}) -> scan_directory full_path, patt, collected else if full_path\match patt - insert collected, full_path + table.insert collected, full_path collected -path_to_module_name = (path) -> +path_to_module_name = (path, prefix) -> + if prefix and path\sub(1, #prefix) == prefix + path = path\sub(#prefix + 1) (path\match("(.-)%.lua")\gsub("/", ".")) each_line = (text) -> - import yield from coroutine coroutine.wrap -> start = 1 while true pos, after = text\find "\n", start, true break if not pos - yield text\sub start, pos - 1 + coroutine.yield text\sub start, pos - 1 start = after + 1 - yield text\sub start, #text + coroutine.yield text\sub start, #text nil write_module = (name, text) -> @@ -56,11 +55,15 @@ write_module = (name, text) -> print " "..line print "end" +exclude = {name, true for name in *args.exclude} + modules = {} for dir in *dirs files = scan_directory dir, "%.lua$" + prefix = strip_prefix and normalize(dir) or nil chunks = for path in *files - module_name = path_to_module_name path + module_name = path_to_module_name path, prefix + continue if exclude[module_name] content = io.open(path)\read"*a" modules[module_name] = true {module_name, content} @@ -73,8 +76,7 @@ for dir in *dirs name = base write_module name, content -if opts.l - for module_name in *split opts.l, "," - if modules[module_name] - print ([[package.preload["%s"]()]])\format module_name +for module_name in *args.load + if modules[module_name] + print ([[package.preload["%s"]()]])\format module_name diff --git a/bin/util/file_to_header.lua b/bin/util/file_to_header.lua new file mode 100644 index 00000000..71378367 --- /dev/null +++ b/bin/util/file_to_header.lua @@ -0,0 +1,42 @@ +-- this script is used to convert a source input file into a C header to embed +-- that file as a string. Works the same as xxd -i + +local input = ... + +local function read_file(file_path) + local file = assert(io.open(file_path, "rb")) + local content = file:read("*a") + file:close() + return content +end + +local function generate_c_header(input_file) + local function byte_to_hex(byte) + return string.format("0x%02x", byte:byte()) + end + + local function sanitize_name(name) + return (name:gsub("[^%w_]", "_")) + end + + local data = read_file(input_file) + local name = sanitize_name(input_file) + local header = {} + + table.insert(header, string.format("unsigned char %s[] = {", name)) + for i = 1, #data do + if i % 16 == 1 then + table.insert(header, "\n ") + end + table.insert(header, byte_to_hex(data:sub(i, i))) + if i ~= #data then + table.insert(header, ", ") + end + end + table.insert(header, "\n};\n") + table.insert(header, string.format("unsigned int %s_len = %d;\n", name, #data)) + + return table.concat(header) +end + +print(generate_c_header(input)) diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 00000000..205556a1 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,136 @@ +{ + target: "reference/api" + template: "reference" + title: "Compiler API" + short_name: "api" +} + +# MoonScript Compiler API + +## Autocompiling with the `moonscript` Module + +After installing MoonScript, you can include the `moonscript` module to make +any Lua script MoonScript aware. + +```lua +require "moonscript" +``` + +After `moonscript` is required, Lua's package loader is updated to search for +`.moon` files on any subsequent calls to `require`. The search path for `.moon` +files is based on the current `package.path` value in Lua when `moonscript` is +required. Any search paths in `package.path` ending in `.lua` are copied, +rewritten to end in `.moon`, and then inserted in `package.moonpath`. + +The `moonloader` is the function that is responsible for searching +`package.moonpath` for a file available to be included. It is inserted in the +second position of the `package.loaders` table. This means that a matching `.moon` file +will be loaded over a matching `.lua` file that has the same base name. + +For more information on Lua's `package.loaders` see [Lua Reference Manual +— +package.loaders](http://www.lua.org/manual/5.1/manual.html#pdf-package.loaders) + +The `moonloader`, when finding a valid path to a `.moon` file, will parse and +compile the file in memory. The code is then turned into a function using the +built in `load` function, which is run as the module. + +If you are executing MoonScript code with the included `moon` command line tool +then it is not required to include this module before including any other +MoonScript modules. + +## `moonscript.base` Module + +```moononly +moonscript = require "moonscript.base" +``` + +This module contains an assortment of functions for loading and compiling +MoonScript code from within Lua. + +The module provides `load`, `loadfile`, `loadstring` functions, which are +analogous to the similarly named Lua functions. The major difference is that +they load MoonScript code instead of Lua code. + + +```moononly +moonscript = require "moonscript.base" + +fn = moonscript.loadstring 'print "hi!"' +fn! +``` + +All of these functions can take an optional last argument, a table of options. +The only option right now is `implicitly_return_root`. Setting this to `false` +makes it so the file does not implicitly return its last statement. + + +```moononly +moonscript = require "moonscript.base" + +fn = moonscript.loadstring "10" +print fn! -- prints "10" + +fn = moonscript.loadstring "10", implicitly_return_root: false +print fn! -- prints nothing +``` + +One more useful function is provided: `to_lua`. This function takes a string of +MoonScript code and returns the compiled Lua result along with the line mapping +table. If there are any errors then `nil` and the error message are returned. + + +```moononly +import to_lua from require "moonscript.base" + +lua_code, line_table = to_lua [[ +x = 124 +print "hello world #{x}" +]] +``` + +Similar to the `load*` functions from above, `to_lua` can take an optional +final argument of a table of options. + +The second return value of `to_lua` is useful if you want to perform line +number reversal. It's a table where the key is a Lua line number and the value +is a character offset from the original MoonScript source. + +## Programmatically Compiling + +If you need finer grained control over the compilation process you can use the +raw parse and compile modules. + +Parsing converts a string of MoonScript into an abstract syntax tree. Compiling +converts an abstract syntax tree into a Lua code string. + +Knowledge of this API may be useful for creating tools to aid the generation of +Lua code from MoonScript code. For example, you could build a macro system by +analyzing and manipulating the abstract syntax tree. Be warned though, the +format of the abstract syntax tree is undocumented and may change in the +future. + +Here is a quick example of how you would compile a MoonScript string to a Lua +String (This is effectively the same as the `to_lua` function described above): + +```moononly +parse = require "moonscript.parse" +compile = require "moonscript.compile" + +moon_code = [[(-> print "hello world")!]] + +tree, err = parse.string moon_code +unless tree + error "Parse error: " .. err + +lua_code, err, pos = compile.tree tree +unless lua_code + error compile.format_error err, pos, moon_code + +-- our code is ready +print lua_code +``` + +On a parse failure `parse.string` returns `nil` and a multi-line error string +of the form ` at line , column :` followed by the offending +source line and a `^` position marker. diff --git a/docs/command_line.md b/docs/command_line.md new file mode 100644 index 00000000..82bb215b --- /dev/null +++ b/docs/command_line.md @@ -0,0 +1,333 @@ +{ + target: "reference/command_line" + template: "reference" + title: "Command Line Tools" + short_name: "command_line" +} + +# Command Line Tools + +Two tools are installed with MoonScript, `moon` and `moonc`. + +`moonc` is for compiling MoonScript code to Lua. +`moon` is for running MoonScript code directly. + +## `moon` + +`moon` can be used to run MoonScript files directly from the command line, +without needing a separate compile step. All MoonScript files are compiled in +memory as they are executed. + +```bash +$ moon my_script.moon +``` + +Any MoonScript files that are required will also be compiled on demand as they +are loaded. + +When an error occurs during runtime, the stack trace is rewritten to give line +numbers from the original `.moon` file. + +If you want to disable [error rewriting](#error_rewriting), you can pass the +`-d` flag. A full list of flags can be seen by passing the `-h` or `--help` +flag. + +### Error Rewriting + +Runtime errors are given special attention when running code using the `moon` +command line tool. Because code is written in MoonScript but executed as Lua, +errors that happen during runtime report Lua line numbers. This can make +debugging less than ideal. + +In order to solve this problem MoonScript builds up a table of line number +mappings, allowing the runtime to calculate what line of MoonScript generated +the line of Lua that triggered the error. + +Consider the following file with a bug (note the invalid `z` variable): + +```moon +add_numbers = (x,y) -> x + z -- 1 +print add_numbers 10,0 -- 2 +``` + +The following error is generated: + + moon: scrap.moon:1(3): attempt to perform arithmetic on global 'z' (a nil value) + stack traceback: + scrap.moon:1(3): in function 'add_numbers' + scrap.moon:2(5): in main chunk + + +Notice how next to the file name there are two numbers. The first number is the +rewritten line number. The number in the parentheses is the original Lua line +number. + +The error in this example is being reported on line 1 of the `moon` file, which +corresponds to line 3 of the generated Lua code. The entire stack trace is rewritten in +addition to the error message. + +### Code Coverage + +`moon` lets you run a MoonScript file while keeping track of which lines +are executed with the `-c` flag. + +For example, consider the following `.moon` file: + +```moononly +-- test.moon +first = -> + print "hello" + +second = -> + print "world" + +first! +``` + +We can execute and get a glance of which lines ran: + +```bash +$ moon -c test.moon +``` + +The following output is produced: + + ------| @cool.moon + 1| -- test.moon + * 2| first = -> + * 3| print "hello" + 4| + * 5| second = -> + 6| print "world" + 7| + * 8| first! + 9| + +The star next to the line means that it was executed. Blank lines are not +considered when running so by default they don't get marked as executed. + +## `moonc` + +`moonc` is used for transforming MoonScript files into Lua files. +It takes a list of files, compiles them all, and creates the associated `.lua` +files in the same directories. + +```bash +$ moonc my_script1.moon my_script2.moon ... +``` + +You can control where the compiled files are put using the `-t` flag, followed +by a directory. + +`moonc` can also take a directory as an argument, and it will recursively scan +for all MoonScript files and compile them. + +`moonc` can write to standard out by passing the `-p` flag. + +The `-w` flag can be used to enable watch mode. `moonc` will stay running, and +watch for changes to the input files. If any of them change then they will be +compiled automatically. + +A full list of flags can be seen by passing the `-h` or `--help` flag. + +### Syntax Transformer + +A syntax transformer is a function that manipulates MoonScript code before +compiling to Lua. It operates on the parsed AST (Abstract Syntax Tree). It can +be used to implement macros, change syntax, optimize code, among other things. +You specify the name of a Lua module. This module must return a single function +(or callable object) that takes AST as an argument, and returns the new AST. + +```bash +moonc --transform my_transfomer my_script.moon +``` + +The transform can fail by returning `nil` and an error message. MoonScript AST +is currently undocumented, so you'll have to experiment by printing out the AST +to see how to make changes. MoonScript AST is made up of standard Lua tables +that are nested. + +### Linter + +`moonc` contains a [lint][1] tool for statically detecting potential problems +with code. If the linter detects any issues with a file, the program will exit +with a status of `1`. + +You can execute the linter with the `-l` flag. When the linting flag is +provided only linting takes place and no compiled code is generated. + +The linter is compatible with the watch mode (see above) for automatic linting. + +```bash +moonc -l file1.moon file2.moon +``` + +The linter's checks are organized into named stages: `global_access`, +`unused`, `constant_assign`, `import_overwrite`. By default every stage is +reported. The `--lint-stage` option limits reporting to the given stage, and +`--exclude-lint-stage` reports every stage except the given one. Both can be +repeated to name multiple stages, and they can not be combined: + +- `global_access` reports references to undeclared global variables. +- `unused` reports local assignments that are never used. +- `constant_assign` reports assignments to constant bindings, such as imports. +- `import_overwrite` reports imports that overwrite existing bindings. + +```bash +moonc -l --lint-stage global_access --lint-stage unused . +moonc -l --exclude-lint-stage import_overwrite . +``` + +The `--lint-format` option changes how results are printed. The `compact` +format writes one `file:line:column: message [stage]` line per issue, +suitable for tools and editors that parse compiler style output: + +```bash +$ moonc -l --lint-format compact lint_example.moon +lint_example.moon:7:5: accessing global `my_nmuber` [global_access] +``` + +Like when compiling, you can also pass a directory as a command line argument +to recursively process all the `.moon` files. + +#### Global Variable Checking + +It's considered good practice to avoid using global variables and create local +variables for all the values referenced. A good case for not using global +variables is that you can analyize the code ahead of time without the need to +execute it to find references to undeclared variables. + +MoonScript makes it difficult to declare global variables by forcing you to be +explicit with the `export` keyword, so it's a good candidate for doing this +kind of linting. + +Consider the following program with a typo: (`my_number` is spelled wrong as +`my_nmuber` in the function) + +```moononly +-- lint_example.moon +my_number = 1234 + +some_function = -> + -- a contrived example with a small chance to pass + if math.random() < 0.01 + my_nmuber + 10 + +some_function! +``` + +Although there is a bug in this code, it rarely happens during execution. It's +more likely to be missed during development and cause problems in the future. + +Running the linter immediately identifies the problem: + +```bash +$ moonc -l lint_example.moon +``` + +Outputs: + + ./lint_example.moon + + line 7: accessing global `my_nmuber` + ================================== + > my_nmuber + 10 + +#### Global Variable Whitelist + +In most circumstances it's impossible to avoid using some global variables. For +example, to access any of the built in modules or functions you typically +access them globally. + +For this reason a global variable whitelist is used. It's a list of global +variables that are allowed to be used. A default whitelist is provided that +contains all of Lua's built in functions and modules. + +You can create your own entires in the whitelist as well. For example, the +testing framework [Busted](http://olivinelabs.com/busted) uses a collection of +global functions (like `describe`, `before_each`, `setup`) to make writing +tests easy. + +It would be nice if we could allow all of those global functions to be called +for `.moon` files located in the `spec/` directory. We can do that by creating +a `lint_config` file. + +`lint_config` is a regular MoonScript or Lua file that provides configuration +for the linter. One of those settings is `whitelist_globals`. + +To create a configuration for Busted we might do something like this: + +```moononly +-- lint_config.moon +{ + whitelist_globals: { + ["spec/"]: { + "it", "describe", "setup", "teardown", + "before_each", "after_each", "pending" + } + } +} +``` + +Compile the file: + +```bash +$ moonc lint_config.moon +``` + +Then run the linter on your entire project: + +```bash +$ moonc -l . +``` + +The whitelisted global references in `spec/` will no longer raise notices. + +The `whitelist_globals` property of the `lint_config` is a table where the keys +are Lua patterns that match file names, and the values are an array of globals +that are allowed. + +Multiple patterns in `whitelist_globals` can match a single file, the union of +the allowed globals will be used when linting that file. + +#### Unused Variable Assigns + +Sometimes when debugging, refactoring, or just developing, you might leave +behind stray assignments that aren't actually necessary for the execution of +your code. It's good practice to clean them up to avoid any potential confusion +they might cause. + +The unused assignment detector keeps track of any variables that are assigned, +and if they aren't accessed in within their available scope, they are reported +as an error. + +Given the following code: + +```moononly +a, b = 1, 2 +print "hello", a +``` + +The linter will identify the problem: + + ./lint_example.moon + + line 1: assigned but unused `b` + =============================== + > a, b = 1, 2 + + +Sometimes you need a name to assign to even though you know it will never be +accessed. The linter will treat `_` as a special name that's allowed to be +written to but never accessed: + +The following code would not produce any lint errors: + +```moononly +item = {123, "shoe", "brown", 123} +_, name, _, count = unpack item +print name, count +``` + + [1]: http://en.wikipedia.org/wiki/Lint_(software) + diff --git a/docs/reference.md b/docs/reference.md index 9aba3071..293165a1 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -1,14 +1,16 @@ - target: reference/index - template: reference - title: MoonScript v0.2.1 - Language Guide - short_name: lang --- +{ + target: "reference/index" + template: "reference" + title: "Language Guide" + short_name: "lang" +} + MoonScript is a programming language that compiles to [Lua](http://www.lua.org). This guide expects the reader to have basic familiarity with Lua. For each code snippet below, the MoonScript is on the left and the compiled Lua is on right right. -This is the offical language reference manual, installation directions and the +This is the official language reference manual, installation directions and the homepage are located at .
@@ -18,126 +20,166 @@ homepage are located at . # The Language +## Whitespace + +MoonScript is a whitespace sensitive language. This means that +instead of using `do` and `end` (or `{` and `}`) to delimit sections of code we +use line-breaks and indentation. + +This means that how you indent your code is important. Luckily MoonScript +doesn't care how you do it but only requires that you be consistent. + +An indent must be at least 1 space or 1 tab, but you can use as many as you +like. All the code snippets on this page will use two spaces. + +> Should you happen to mix tabs and spaces, a tab is equivalent to 4 spaces. I +> shouldn't be telling you this though because you should never do it. + ## Assignment -Unlike Lua, there is no `local` keyword. All assignments to names that are not -already defined will be declared as local to the scope of that declaration. If -you wish to create a global variable it must be done using the `export` -keyword. +Assigning to an undeclared name will cause it to be declared as a new local +variable. The language is dynamically typed so you can assign any value to any +variable. You can assign multiple names and values at once just like Lua: + +```moon +hello = "world" +a,b,c = 1, 2, 3 +hello = 123 -- uses the existing variable +``` - ```moon - hello = "world" - a,b,c = 1, 2, 3 - ``` +If you wish to create a global variable it must be done using the +[`export`](#export_statement) keyword. + +The [`local`](#local_statement) keyword can be used to forward declare a +variable, or shadow an existing one. ## Update Assignment -`+=`, `-=`, `/=`, `*=`, `%=`, `..=` operators have been added for updating a value by -a certain amount. They are aliases for their expanded equivalents. +`+=`, `-=`, `/=`, `*=`, `%=`, `..=`, `or=`, `and=`, `&=`, `|=`, `>>=`, and +`<<=` operators have been added for updating and assigning at the same time. +They are aliases for their expanded equivalents. + +```moon +x = 0 +x += 10 - ```moon - x = 0 - x += 10 +s = "hello " +s ..= "world" - s = "hello " - s ..= "world" - ``` +b = false +b and= true or false -## Comments +p = 50 +p &= 5 +p |= 3 +p >>= 3 +p <<= 3 +``` + +## Comments Like Lua, comments start with `--` and continue to the end of the line. Comments are not written to the output. - ```moon - -- I am a comment - ``` +```moon +-- I am a comment +``` ## Literals & Operators -MoonScript supports all the same primitive literals as Lua and uses the same -syntax. This applies to numbers, strings, booleans, and `nil`. +All of the primitive literals in Lua can be used. This applies to numbers, +strings, booleans, and `nil`. + +All of Lua's binary and unary operators are available. Additionally `!=` is as +an alias for `~=`. + +Unlike Lua, Line breaks are allowed inside of single and double quote strings +without an escape sequence: -MoonScript also supports all the same binary and unary operators. Additionally -`!=` is as an alias for `~=`. +```moon +some_string = "Here is a string + that has a line break in it." +``` ## Function Literals All functions are created using a function expression. A simple function is denoted using the arrow: `->` - ```moon - my_function = -> - my_function() -- call the empty function - ``` - +```moon +my_function = -> +my_function() -- call the empty function +``` The body of the function can either be one statement placed directly after the arrow, or it can be a series of statements indented on the following lines: - ```moon - func_a = -> print "hello world" +```moon +func_a = -> print "hello world" - func_b = -> - value = 100 - print "The value:", value - ``` +func_b = -> + value = 100 + print "The value:", value +``` If a function has no arguments, it can be called using the `!` operator, instead of empty parentheses. The `!` invocation is the preferred way to call functions with no arguments. - ```moon - func_a! - func_b() - ``` +```moon +func_a! +func_b() +``` Functions with arguments can be created by preceding the arrow with a list of argument names in parentheses: - ```moon - sum = (x, y) -> print "sum", x + y - ``` +```moon +sum = (x, y) -> print "sum", x + y +``` -Functions can be called by listing the values of the arguments after the name -of the variable where the function is stored. When chaining together function -calls, the arguments are applied to the closest function to the left. +Functions can be called by listing the arguments after the name of an expression +that evaluates to a function. When chaining together function calls, the +arguments are applied to the closest function to the left. - ```moon - sum 10, 20 - print sum 10, 20 +```moon +sum 10, 20 +print sum 10, 20 - a b c "a", "b", "c" - ``` +a b c "a", "b", "c" +``` In order to avoid ambiguity in when calling functions, parentheses can also be used to surround the arguments. This is required here in order to make sure the right arguments get sent to the right functions. - ```moon - print "x:", sum(10, 20), "y:", sum(30, 40) - ``` +```moon +print "x:", sum(10, 20), "y:", sum(30, 40) +``` + +There must not be any space between the opening parenthesis and the function. Functions will coerce the last statement in their body into a return statement, this is called implicit return: - ```moon - sum = (x, y) -> x + y - print "The sum is ", sum 10, 20 - ``` +```moon +sum = (x, y) -> x + y +print "The sum is ", sum 10, 20 +``` And if you need to explicitly return, you can use the `return` keyword: - ```moon - sum = (x, y) -> return x + y - ``` +```moon +sum = (x, y) -> return x + y +``` Just like in Lua, functions can return multiple values. The last statement must be a list of values separated by commas: - - ```moon - mystery = (x, y) -> x + y, x - y - a,b = mystery 10, 20 - ``` + +```moon +mystery = (x, y) -> x + y, x - y +a,b = mystery 10, 20 +``` ### Fat Arrows @@ -145,30 +187,73 @@ Because it is an idiom in Lua to send an object as the first argument when calling a method, a special syntax is provided for creating functions which automatically includes a `self` argument. - ```moon - func = (num) => self.value + num - ``` +```moon +func = (num) => @value + num +``` ### Argument Defaults It is possible to provide default values for the arguments of a function. An -argument is determined to be empty if it's value is `nil`. Any `nil` arguments -that have a default value will be replace before the body of the function is run. +argument is determined to be empty if its value is `nil`. Any `nil` arguments +that have a default value will be replaced before the body of the function is run. - ```moon - my_function = (name="something", height=100) -> - print "Hello I am", name - print "My height is", height - ``` +```moon +my_function = (name="something", height=100) -> + print "Hello I am", name + print "My height is", height +``` An argument default value expression is evaluated in the body of the function in the order of the argument declarations. For this reason default values have access to previously declared arguments. - ```moon - some_args = (x=100, y=x+1000) -> - print x + y - ``` +```moon +some_args = (x=100, y=x+1000) -> + print x + y +``` + +### Argument Destructuring + +An argument can be written as a table literal to +[destructure](#destructuring_assignment) the value that is passed in. The names +in the table literal become local variables in the body of the function. + +```moon +send_message = ({:sender, :recipient, :body}) -> + print "#{sender} -> #{recipient}: #{body}" + +send_message { + sender: "leaf" + recipient: "world" + body: "hello" +} +``` + +Any pattern that can be used in a destructuring assignment can be used here, +including positional names and nested tables: + +```moon +draw = ({label, pos: {x, y}}) -> + print label, x, y + +draw { "origin", pos: {0, 0} } +``` + +Destructured arguments can be mixed with regular arguments, argument defaults, +and `...`: + +```moon +render = (name, {:width, :height} = {width: 100, height: 50}, ...) -> + print name, width, height, ... +``` + +Using a fat arrow, the pattern can assign directly to properties of the object. +This is convenient for a constructor: + +```moon +class Point + new: ({x: @x, y: @y}) => +``` ### Considerations @@ -177,19 +262,18 @@ restrictions must be put in place to avoid parsing ambiguity involving whitespace. The minus sign plays two roles, a unary negation operator and a binary -subtraction operator. In order to force subtraction a space must be placed -after the `-` operator. In order to force a negation, no space must follow -the `-`. Consider the examples below. +subtraction operator. Consider how the following examples compile: - ```moon - a = x - 10 - b = x-10 - c = x -y - ``` +```moon +a = x - 10 +b = x-10 +c = x -y +d = x- z +``` -The precedence of the first argument of a function call can also be controlled -using whitespace if the argument is a literal string.In Lua, it is common to -leave off parentheses when calling a function with a single string literal. +The precedence of the first argument of a function call can be controlled using +whitespace if the argument is a literal string. In Lua, it is common to leave +off parentheses when calling a function with a single string or table literal. When there is no space between a variable and a string literal, the function call takes precedence over any following expressions. No other @@ -199,10 +283,10 @@ Where there is a space following a variable and a string literal, the function call acts as show above. The string literal belongs to any following expressions (if they exist), which serves as the argument list. - ```moon - x = func"hello" + 100 - y = func "hello" + 100 - ``` +```moon +x = func"hello" + 100 +y = func "hello" + 100 +``` ### Multi-line arguments @@ -216,225 +300,231 @@ must end in a comma. And the following line must be indented more than the current indentation. Once indented, all other argument lines must be at the same level of indentation to be part of the argument list - ```moon - my_func 5,4,3, - 8,9,10 +```moon +my_func 5,4,3, + 8,9,10 - cool_func 1,2, - 3,4, - 5,6, - 7,8 - ``` +cool_func 1,2, + 3,4, + 5,6, + 7,8 +``` This type of invocation can be nested. The level of indentation is used to determine to which function the arguments belong to. - ```moon - my_func 5,6,7, - 6, another_func 6,7,8, - 9,1,2, - 5,4 - ``` +```moon +my_func 5,6,7, + 6, another_func 6,7,8, + 9,1,2, + 5,4 +``` Because [tables](#table_literals) also use the comma as a delimiter, this indentation syntax is helpful for letting values be part of the argument list instead of being part of the table. - ```moon - x = { - 1,2,3,4, a_func 4,5, - 5,6, - 8,9,10 - } - ``` +```moon +x = { + 1,2,3,4, a_func 4,5, + 5,6, + 8,9,10 +} +``` Although uncommon, notice how we can give a deeper indentation for function -arguments if we know we will be using a lower indentation futher on. +arguments if we know we will be using a lower indentation further on. - ```moon - y = { my_func 1,2,3, - 4,5, - 5,6,7 - } - ``` +```moon +y = { my_func 1,2,3, + 4,5, + 5,6,7 +} +``` The same thing can be done with other block level statements like [conditionals](#conditionals). We can use indentation level to determine what statement a value belongs to: - ```moon - if func 1,2,3, - "hello", - "world" - print "hello" - print "I am inside if" +```moon +if func 1,2,3, + "hello", + "world" + print "hello" + print "I am inside if" - if func 1,2,3, - "hello", - "world" - print "hello" - print "I am inside if" - ``` +if func 1,2,3, + "hello", + "world" + print "hello" + print "I am inside if" +``` ## Table Literals Like in Lua, tables are delimited in curly braces. - ```moon - some_values = { 1, 2, 3, 4 } - ``` +```moon +some_values = { 1, 2, 3, 4 } +``` Unlike Lua, assigning a value to a key in a table is done with `:` (instead of `=`). - ```moon - some_values = { - name: "Bill", - age: 200, - ["favorite food"]: "rice" - } - ``` +```moon +some_values = { + name: "Bill", + age: 200, + ["favorite food"]: "rice" +} +``` The curly braces can be left off if a single table of key value pairs is being assigned. - ```moon - profile = - height: "4 feet", - shoe_size: 13, - favorite_foods: {"ice cream", "donuts"} - ``` +```moon +profile = + height: "4 feet", + shoe_size: 13, + favorite_foods: {"ice cream", "donuts"} +``` Newlines can be used to delimit values instead of a comma (or both): - ```moon - values = { - 1,2,3,4 - 5,6,7,8 - name: "superman" - occupation: "crime fighting" - } - ``` +```moon +values = { + 1,2,3,4 + 5,6,7,8 + name: "superman" + occupation: "crime fighting" +} +``` When creating a single line table literal, the curly braces can also be left off: - ```moon - my_function dance: "Tango", partner: "none" +```moon +my_function dance: "Tango", partner: "none" - y = type: "dog", legs: 4, tails: 1 - ``` +y = type: "dog", legs: 4, tails: 1 +``` The keys of a table literal can be language keywords without being escaped: - ```moon - tbl = { - do: "something" - end: "hunger" - } - ``` +```moon +tbl = { + do: "something" + end: "hunger" +} +``` If you are constructing a table out of variables and wish the keys to be the same as the variable names, then the `:` prefix operator can be used: - ```moon - hair = "golden" - height = 200 - person = { :hair, :height, shoe_size: 40 } +```moon +hair = "golden" +height = 200 +person = { :hair, :height, shoe_size: 40 } - print_table :hair, :height - ``` +print_table :hair, :height +``` If you want the key of a field in the table to to be result of an expression, then you can wrap it in `[` `]`, just like in Lua. You can also use a string literal directly as a key, leaving out the square brackets. This is useful if -your key has any special chacters. +your key has any special characters. - ```moon - t = { - [1 + 2]: "hello" - "hello world": true - } +```moon +t = { + [1 + 2]: "hello" + "hello world": true +} - ``` +``` ## Comprehensions -Compiling provide a convenient syntax for constructing a new table by iterating -over some existing object and applying an expression to its values. There are -two kinds of comprehensions: list comprehensions and table comprehensions. They -both produce Lua tables; _list comprehensions_ accumulate values into an -array-like table, and _table comprehensions_ let you set both the key and the -value on each iteration. +Comprehensions provide a convenient syntax for constructing a new table by +iterating over some existing object and applying an expression to its values. +There are two kinds of comprehensions: list comprehensions and table +comprehensions. They both produce Lua tables; _list comprehensions_ accumulate +values into an array-like table, and _table comprehensions_ let you set both +the key and the value on each iteration. ### List Comprehensions The following creates a copy of the `items` table but with all the values doubled. - ```moon - items = { 1, 2, 3, 4 } - doubled = [item * 2 for i, item in ipairs items] - ``` +```moon +items = { 1, 2, 3, 4 } +doubled = [item * 2 for i, item in ipairs items] +``` The items included in the new table can be restricted with a `when` clause: - ```moon - iter = ipairs items - slice = [item for i, item in iter when i > 1 and i < 3] - ``` +```moon +iter = ipairs items +slice = [item for i, item in iter when i > 1 and i < 3] +``` Because it is common to iterate over the values of a numerically indexed table, an `*` operator is introduced. The doubled example can be rewritten as: - ```moon - doubled = [item * 2 for item in *items] - ``` +```moon +doubled = [item * 2 for item in *items] +``` The `for` and `when` clauses can be chained as much as desired. The only requirement is that a comprehension has at least one `for` clause. Using multiple `for` clauses is the same as using nested loops: - ```moon - x_coords = {4, 5, 6, 7} - y_coords = {9, 2, 3} +```moon +x_coords = {4, 5, 6, 7} +y_coords = {9, 2, 3} + +points = [{x,y} for x in *x_coords for y in *y_coords] +``` - points = [{x,y} for x in *x_coords for y in *y_coords] - ``` +Numeric for loops can also be used in comprehensions: + +```moon +evens = [i for i=1,100 when i % 2 == 0] +``` ### Table Comprehensions -The syntax for table comprehensions is very similar, differing by using `{` and +The syntax for table comprehensions is very similar, only differing by using `{` and `}` and taking two values from each iteration. -This example copies the key-value table `thing`: +This example makes a copy of the table`thing`: - ```moon - thing = { - color: "red" - name: "fast" - width: 123 - } +```moon +thing = { + color: "red" + name: "fast" + width: 123 +} - thing_copy = {k,v for k,v in pairs thing} - ``` +thing_copy = {k,v for k,v in pairs thing} +``` Table comprehensions, like list comprehensions, also support multiple `for` and `when` clauses. In this example we use a `when` clause to prevent the value associated with the `color` key from being copied. - ```moon - no_color = {k,v for k,v in pairs thing when k != "color"} - ``` +```moon +no_color = {k,v for k,v in pairs thing when k != "color"} +``` The `*` operator is also supported. Here we create a square root look up table for a few numbers. - ```moon - numbers = {1,2,3,4} - sqrts = {i, math.sqrt i for i in *numbers} - ``` +```moon +numbers = {1,2,3,4} +sqrts = {i, math.sqrt i for i in *numbers} +``` The key-value tuple in a table comprehension can also come from a single expression, in which case the expression should return two values. The @@ -443,10 +533,10 @@ first is used as the key and the second is used as the value: In this example we convert an array of pairs to a table where the first item in the pair is the key and the second is the value. - ```moon - tuples = {{"hello", "world"}, {"foo", "bar"}} - tbl = {unpack tuple for tuple in *tuples} - ``` +```moon +tuples = {{"hello", "world"}, {"foo", "bar"}} +tbl = {unpack tuple for tuple in *tuples} +``` ### Slicing @@ -456,92 +546,96 @@ a step size in a `for` loop. Here we can set the minimum and maximum bounds, taking all items with indexes between 1 and 5 inclusive: - - ```moon - slice = [item for item in *items[1,5]] - ``` + +```moon +slice = [item for item in *items[1,5]] +``` Any of the slice arguments can be left off to use a sensible default. In this example, if the max index is left off it defaults to the length of the table. This will take everything but the first element: - ```moon - slice = [item for item in *items[2,]] - ``` +```moon +slice = [item for item in *items[2,]] +``` If the minimum bound is left out, it defaults to 1. Here we only provide a step size and leave the other bounds blank. This takes all odd indexed items: (1, 3, 5, ...) - ```moon - slice = [item for item in *items[,,2]] - ``` +```moon +slice = [item for item in *items[,,2]] +``` + +## String Interpolation + +You can mix expressions into string literals using `#{}` syntax. + +```moon +print "I am #{math.random! * 100}% sure." +``` +String interpolation is only available in double quoted strings. ## For Loop There are two for loop forms, just like in Lua. A numeric one and a generic one: - ```moon - for i = 10, 20 - print i +```moon +for i = 10, 20 + print i - for k = 1,15,2 -- an optional step provided - print k +for k = 1,15,2 -- an optional step provided + print k + +for key, value in pairs object + print key, value +``` - for key, value in pairs object - print key, value - ``` - The slicing and `*` operators can be used, just like with comprehensions: - ```moon - for item in *items[2,4] - print item - ``` +```moon +for item in *items[2,4] + print item +``` A shorter syntax is also available for all variations when the body is only a single line: - ```moon - for item in *items do print item +```moon +for item in *items do print item - for j = 1,10,3 do print j - ``` +for j = 1,10,3 do print j +``` -A for loop can also be used an expression. The last statement in the body of +A for loop can also be used as an expression. The last statement in the body of the for loop is coerced into an expression and appended to an accumulating -table if the value of that expression is not nil. +array table. Doubling every even number: - ```moon - doubled_evens = for i=1,20 - if i % 2 == 0 - i * 2 - else - i - ``` - -Filtering out odd numbers: - - ```moon - my_numbers = {1,2,3,4,5,6} - odds = for x in *my_numbers - if x % 2 == 1 then x - ``` +```moon +doubled_evens = for i=1,20 + if i % 2 == 0 + i * 2 + else + i +``` + +You can also filter values by combining the for loop expression with the +[`continue`](#continue) statement. For loops at the end of a function body are not accumulated into a table for a -return value (Instead the function will return `nil`). Either an explicit +return value (Instead the function will return `nil`). Either an explicit `return` statement can be used, or the loop can be converted into a list comprehension. - ```moon - func_a = -> for i=1,10 do i - func_b = -> return for i=1,10 do i +```moon +func_a = -> for i=1,10 do i +func_b = -> return for i=1,10 do i - print func_a! -- prints nil - print func_b! -- prints table object - ``` +print func_a! -- prints nil +print func_b! -- prints table object +``` This is done to avoid the needless creation of tables for functions that don't need to return the results of the loop. @@ -550,59 +644,93 @@ need to return the results of the loop. The while loop also comes in two variations: - ```moon - i = 10 - while i > 0 - print i - i -= 1 +```moon +i = 10 +while i > 0 + print i + i -= 1 - while running == true do my_function! - ``` +while running == true do my_function! +``` Like for loops, the while loop can also be used an expression. Additionally, for a function to return the accumulated value of a while loop, the statement must be explicitly returned. +## Continue + +A `continue` statement can be used to skip the current iteration in a loop. + +```moon +i = 0 +while i < 10 + continue if i % 2 == 0 + print i +``` +`continue` can also be used with loop expressions to prevent that iteration +from accumulating into the result. This examples filters the array table into +just even numbers: + +```moon +my_numbers = {1,2,3,4,5,6} +odds = for x in *my_numbers + continue if x % 2 == 1 + x +``` + ## Conditionals - ```moon - have_coins = false - if have_coins - print "Got coins" - else - print "No coins" - ``` +```moon +have_coins = false +if have_coins + print "Got coins" +else + print "No coins" +``` A short syntax for single statements can also be used: - ```moon - have_coins = false - if have_coins then print "Got coins" else print "No coins" - ``` +```moon +have_coins = false +if have_coins then print "Got coins" else print "No coins" +``` -Because if statements can be used as expressions, this can able be written as: +Because if statements can be used as expressions, this can also be written as: - ```moon - have_coins = false - print if have_coins then "Got coins" else "No coins" - ``` +```moon +have_coins = false +print if have_coins then "Got coins" else "No coins" +``` Conditionals can also be used in return statements and assignments: - ```moon - is_tall = (name) -> - if name == "Rob" - true - else - false +```moon +is_tall = (name) -> + if name == "Rob" + true + else + false - message = if is_tall "Rob" - "I am very tall" - else - "I am not so tall" +message = if is_tall "Rob" + "I am very tall" +else + "I am not so tall" + +print message -- prints: I am very tall +``` - print message -- prints: I am very tall - ``` +The opposite of `if` is `unless`: + +```moon +unless os.date("%A") == "Monday" + print "it is not Monday!" +``` + +```moon +print "You're lucky!" unless math.random! > 0.1 +``` + +### With Assignment `if` and `elseif` blocks can take an assignment in place of a conditional expression. Upon evaluating the conditional, the assignment will take place and @@ -610,25 +738,34 @@ the value that was assigned to will be used as the conditional expression. The assigned variable is only in scope for the body of the conditional, meaning it is never available if the value is not truthy. - ```moon - if user = database\find_user "moon" - print user.name - ``` +```moon +if user = database.find_user "moon" + print user.name +``` + +```moon +if hello = os.getenv "hello" + print "You have hello", hello +elseif world = os.getenv "world" + print "you have world", world +else + print "nothing :(" +``` ## Line Decorators For convenience, the for loop and if statement can be applied to single statements at the end of the line: - ```moon - print "hello world" if name == "Rob" - ``` +```moon +print "hello world" if name == "Rob" +``` And with basic loops: - ```moon - print "item: ", item for item in *items - ``` +```moon +print "item: ", item for item in *items +``` ## Switch @@ -637,40 +774,43 @@ check against the same value. Note that the value is only evaluated once. Like if statements, switches can have an else block to handle no matches. Comparison is done with the `==` operator. - ```moon - name = "Dan" - switch name - when "Robert" - print "You are robert" - when "Dan" - print "Your name, it's Dan" - else - print "I don't know about your name" - ``` +```moon +name = "Dan" +switch name + when "Robert" + print "You are Robert" + when "Dan", "Daniel" + print "Your name, it's Dan" + else + print "I don't know about your name" +``` + +A switch `when` clause can match against multiple values by listing them out +comma separated. Switches can be used as expressions as well, here we can assign the result of the switch to a variable: - ```moon - b = 1 - next_number = switch b - when 1 - 2 - when 2 - 3 - else - error "can't count that high!" - ``` +```moon +b = 1 +next_number = switch b + when 1 + 2 + when 2 + 3 + else + error "can't count that high!" +``` We can use the `then` keyword to write a switch's `when` block on a single line. No extra keyword is needed to write the else block on a single line. - ```moon - msg = switch math.random(1, 5) - when 1 then "you are lucky" - when 2 then "you are almost lucky" - else "not so lucky" - ``` +```moon +msg = switch math.random(1, 5) + when 1 then "you are lucky" + when 2 then "you are almost lucky" + else "not so lucky" +``` It is worth noting the order of the case comparison expression. The case's expression is on the left hand side. This can be useful if the case's @@ -685,17 +825,17 @@ code if you wish to know the implementation details. A simple class: - ```moon - class Inventory - new: => - @items = {} +```moon +class Inventory + new: => + @items = {} - add_item: (name) => - if @items[name] - @items[name] += 1 - else - @items[name] = 1 - ``` + add_item: (name) => + if @items[name] + @items[name] += 1 + else + @items[name] = 1 +``` A class is declared with a `class` statement followed by a table-like declaration where all of the methods and properties are listed. @@ -712,11 +852,11 @@ The `@` prefix on a variable name is shorthand for `self.`. `@items` becomes Creating an instance of the class is done by calling the name of the class as a function. - ```moon - inv = Inventory! - inv\add_item "t-shirt" - inv\add_item "pants" - ``` +```moon +inv = Inventory! +inv\add_item "t-shirt" +inv\add_item "pants" +``` Because the instance of the class needs to be sent to the methods when they are called, the \\ operator is used. @@ -727,60 +867,64 @@ functions, but for other types of objects, undesired results may occur. Consider the example below, the `clothes` property is shared amongst all instances, so modifications to it in one instance will show up in another: - ```moon - class Person - clothes: {} - give_item: (name) => - table.insert @clothes, name +```moononly +class Person + clothes: {} + give_item: (name) => + table.insert @clothes, name - a = Person! - b = Person! +a = Person! +b = Person! - a\give_item "pants" - b\give_item "shirt" +a\give_item "pants" +b\give_item "shirt" - -- will print both pants and shirt - print item for item in *a.clothes - ``` +-- will print both pants and shirt +print item for item in *a.clothes +``` The proper way to avoid this problem is to create the mutable state of the object in the constructor: - ```moon - class Person - new: => - @clothes = {} - ``` +```moononly +class Person + new: => + @clothes = {} +``` ### Inheritance The `extends` keyword can be used in a class declaration to inherit the properties and methods from another class. - ```moon - class BackPack extends Inventory - size: 10 - add_item: (name) => - if #@items > size then error "backpack is full" - super name - ``` +```moon +class BackPack extends Inventory + size: 10 + add_item: (name) => + if #@items > size then error "backpack is full" + super name +``` Here we extend our Inventory class, and limit the amount of items it can carry. +In this example, we don't define a constructor on the subclass, so the parent +class' constructor is called when we make a new instance. If we did define a +constructor then we can use the [`super`](#super) method to call the parent +constructor. + Whenever a class inherits from another, it sends a message to the parent class -by calling the method `__inherited` on the parent class if it exists. The -function recieves two arguments, the class that is being inherited and the +by calling the method `__inherited` on the parent class if it exists. The +function receives two arguments, the class that is being inherited and the child class. - ```moon - class Shelf - @__inherited: (child) => - print @__name, "was inherited by", child.__name +```moononly +class Shelf + @__inherited: (child) => + print @__name, "was inherited by", child.__name - -- will print: Shelf was inherited by Cupboard - class Cupboard extends Shelf - nil - ``` +-- will print: Shelf was inherited by Cupboard +class Cupboard extends Shelf +``` ### Super @@ -804,17 +948,17 @@ retrieve a function, the raw function is returned. A few examples of using `super` in different ways: - ```moon - class MyClass extends ParentClass - a_method: => - -- the following have the same effect: - super "hello", "world" - super\a_method "hello", "world" - super.a_method self, "hello", "world" +```moon +class MyClass extends ParentClass + a_method: => + -- the following have the same effect: + super "hello", "world" + super\a_method "hello", "world" + super.a_method self, "hello", "world" - -- super as a value is equal to the parent class: - assert super == ParentClass - ``` + -- super as a value is equal to the parent class: + assert super == ParentClass +``` `super` can also be used on left side of a [Function Stub](#function_stubs). The only major difference is that instead of the resulting function being bound @@ -827,12 +971,12 @@ special `__class` property. This property holds the class object. The class object is what we call to build a new instance. We can also index the class object to retrieve class methods and properties. - ```moon - b = BackPack! - assert b.__class == BackPack +```moon +b = BackPack! +assert b.__class == BackPack - print BackPack.size -- prints 10 - ``` +print BackPack.size -- prints 10 +``` ### Class Objects The class object is what we create when we use a `class` statement. The class @@ -858,9 +1002,9 @@ The class object has a couple special properties: The name of the class as when it was declared is stored as a string in the `__name` field of the class object. - ```moon - print BackPack.__name -- prints Backpack - ``` +```moon +print BackPack.__name -- prints Backpack +``` The *base* object is stored in `__base`. We can modify this table to add functionality to instances that have already been created and ones that are yet @@ -874,39 +1018,39 @@ If the class extends from anything, the parent class object is stored in We can create variables directly in the class object instead of in the *base* by using `@` in the front of the property name in a class declaration. - ```moon - class Things - @some_func: => print "Hello from", @__name +```moononly +class Things + @some_func: => print "Hello from", @__name - Things\some_func! +Things\some_func! - -- class variables not visible in instances - assert Things().some_func == nil +-- class variables not visible in instances +assert Things().some_func == nil - ``` +``` In expressions, we can use `@@` to access a value that is stored in the `__class` of `self`. Thus, `@@hello` is shorthand for `self.__class.hello`. - ```moon - class Counter - @count: 0 +```moononly +class Counter + @count: 0 - new: => - @@count += 1 + new: => + @@count += 1 - Counter! - Counter! +Counter! +Counter! - print Counter.count -- prints 2 - ``` +print Counter.count -- prints 2 +``` The calling semantics of `@@` are similar to `@`. Calling a `@@` name will pass the class in as the first argument using Lua's colon syntax. - ```moon - @@hello 1,2,3,4 - ``` +```moon +@@hello 1,2,3,4 +``` ### Class Declaration Statements In the body of a class declaration, we can have normal expressions in addition @@ -915,15 +1059,29 @@ to key/value pairs. In this context, `self` is equal to the class object. Here is an alternative way to create a class variable compared to what's described above: - ```moon - class Things - @class_var = "hello world" +```moon +class Things + @class_var = "hello world" - ``` +``` These expressions are executed after all the properties have been added to the *base*. +All variables declared in the body of the class are local to the classes +properties. This is convenient for placing private values or helper functions +that only the class methods can access: + + +```moononly +class MoreThings + secret = 123 + log = (msg) -> print "LOG:", msg + + some_method: => + log "hello world: " .. secret + +``` ### `@` and `@@` Values @@ -933,59 +1091,98 @@ that name accessed in `self` and `self.__class`. If they are used all by themselves, they are aliases for `self` and `self.__class`. - assert @ == self - assert @@ == self.__class +```moon +assert @ == self +assert @@ == self.__class +``` For example, a quick way to create a new instance of the same class from an instance method using `@@`: - some_instance_method = (...) => @@ ... +```moon +some_instance_method = (...) => @@ ... +``` + + +### Class Expressions + +The `class` syntax can also be used as an expression which can be assigned to a +variable or explicitly returned. + +```moononly +x = class Bucket + drops: 0 + add_drop: => @drops += 1 +``` + +### Anonymous classes + +The name can be left out when declaring a class. The `__name` attribute will be +`nil`, unless the class expression is in an assignment. The name on the left +hand side of the assignment is used instead of `nil`. + + +```moononly +BigBucket = class extends Bucket + add_drop: => @drops += 10 + +assert Bucket.__name == "BigBucket" +``` + +You can even leave off the body, meaning you can write a blank anonymous class +like this: + +```moononly +x = class +``` ## Export Statement -Because, by default, all assignments to variables that are not lexically visible will +Because all assignments to variables that are not lexically visible will be declared as local, special syntax is required to declare a variable globally. The export keyword makes it so any following assignments to the specified names will not be assigned locally. - ```moon - export var_name, var_name2 - var_name, var_name3 = "hello", "world" - ``` - +```moon +export var_name, var_name2 +var_name, var_name3 = "hello", "world" +``` This is especially useful when declaring what will be externally visible in a module: - ```moon - -- my_module.moon - module "my_module", package.seeall - export print_result +```moon +-- my_module.moon +module "my_module", package.seeall +export print_result - length = (x, y) -> math.sqrt x*x + y*y +length = (x, y) -> math.sqrt x*x + y*y - print_result = (x, y) -> - print "Length is ", length x, y +print_result = (x, y) -> + print "Length is ", length x, y - -- main.moon - require "my_module" +-- main.moon +require "my_module" - my_module.print_result 4, 5 -- prints the result +my_module.print_result 4, 5 -- prints the result - print my_module.length 6, 7 -- errors, `length` not visible - ``` +print my_module.length 6, 7 -- errors, `length` not visible +``` Assignment can be combined with the export keyword to assign to global variables directly: - ```moon - export some_number, message_str = 100, "hello world" - ``` +```moon +export some_number, message_str = 100, "hello world" +``` Additionally, a class declaration can be prefixed with the export keyword in order to export it. +Export will have no effect if there is already a local variable of the same +name in scope. + ### Export All & Export Proper The `export` statement can also take special symbols `*` and `^`. @@ -994,36 +1191,113 @@ The `export` statement can also take special symbols `*` and `^`. current scope. `export ^` will export all proper names, names that begin with a capital letter. +## Local Statement + +Sometimes you want to declare a variable name before the first time you assign +it. The `local` statement can be used to do that. + +In this example we declare the variable `a` in the outer scope so its value +can be accessed after the `if` statement. If there was no `local` statement then +`a` would only be accessible inside the `if` statement. + +```moon +local a +if something + a = 1 +print a +``` + +`local` can also be used to shadow existing variables for the rest of a scope. + +```moon +x = 10 +if something + local x + x = 12 +print x -- prints 10 +``` + +When you have one function that calls another, you typically order them such +that the second function can access the first. If both functions happen to +call each other, then you must forward declare the names: + +```moon +local first, second + +first = -> + second! + +second = -> + first! +``` + +The same problem occurs with declaring classes and regular values too. + +Because forward declaring is often better than manually ordering your assigns, +a special form of `local` is provided: + +```moon +local * + +first = -> + print data + second! + +second = -> + first! + +data = {} +``` + +`local *` will forward declare all names below it in the current scope. + +Similarly to [`export`](#export_all_and_export_proper) one more special form is +provided, `local ^`. This will forward declare all names that begin with a +capital letter. + ## Import Statement Often you want to bring some values from a table into the current scope as local variables by their name. The import statement lets us accomplish this: - ```moon - import insert from table - ``` +```moon +import insert from table +``` The multiple names can be given, each separated by a comma: - ```moon - import C, Ct, Cmt from lpeg - ``` +```moon +import C, Ct, Cmt from lpeg +``` Sometimes a function requires that the table be sent in as the first argument (when using the \\ syntax). As a shortcut, we can prefix the name with a \\ to bind it to that table: - ```moon - -- some object - my_module = - state: 100 - add: (value) => - self.state + value +```moon +-- some object +my_module = + state: 100 + add: (value) => + self.state + value + +import \add from my_module + +print add 22 -- equivalent to calling my_module\add 22 +``` - import \add from my_module +When handing multiple imports you can substitute the comma with a newline and +any amount of whitespace. When working with a lot of imports you might write +something like this: - print add 22 -- equivalent to calling my_module\get 22 - ``` +```moon +import + assert_csrf + assert_timezone + not_found + require_login + from require "helpers" +``` ## With Statement @@ -1041,32 +1315,191 @@ those operations applied to the object we are using `with` on. For example, we work with a newly created object: - ```moon - with Person! - .name = "Oswald" - \add_relative my_dad - \save! - print .name - ``` +```moon +with Person! + .name = "Oswald" + \add_relative my_dad + \save! + print .name +``` The `with` statement can also be used as an expression which returns the value it has been giving access to. - ```moon - file = with File "favorite_foods.txt" - \set_encoding "utf8" - ``` +```moon +file = with File "favorite_foods.txt" + \set_encoding "utf8" +``` Or... - ```moon - create_person = (name, relatives) -> - with Person! - .name = name - \add_relative relative for relative in *relatives +```moon +create_person = (name, relatives) -> + with Person! + .name = name + \add_relative relative for relative in *relatives + +me = create_person "Leaf", {dad, mother, sister} +``` +In this usage, `with` can be seen as a special form of the K combinator. + +The expression in the `with` statement can also be an assignment, if you want +to give a name to the expression. + +```moon +with str = "Hello" + print "original:", str + print "upper:", \upper! +``` + +## Do + +When used as a statement, `do` works just like it does in Lua. + +```moon +do + var = "hello" + print var +print var -- nil here +``` + +MoonScript's `do` can also be used an expression . Allowing you to combine +multiple lines into one. The result of the `do` expression is the last +statement in its body. + + +```moon +counter = do + i = 0 + -> + i += 1 + i + +print counter! +print counter! +``` + +```moon +tbl = { + key: do + print "assigning key!" + 1234 +} +``` + +## Destructuring Assignment + +Destructuring assignment is a way to quickly extract values from a table by +their name or position in array based tables. + +Typically when you see a table literal, `{1,2,3}`, it is on the right hand side +of an assignment because it is a value. Destructuring assignment swaps the role +of the table literal, and puts it on the left hand side of an assign +statement. + +This is best explained with examples. Here is how you would unpack the first +two values from a table: + +```moon +thing = {1,2} + +{a,b} = thing +print a,b +``` + +In the destructuring table literal, the key represents the key to read from the +right hand side, and the value represents the name the read value will be +assigned to. + +```moon +obj = { + hello: "world" + day: "tuesday" + length: 20 +} + +{hello: hello, day: the_day} = obj +print hello, the_day +``` + +This also works with nested data structures as well: + +```moon +obj2 = { + numbers: {1,2,3,4} + properties: { + color: "green" + height: 13.5 + } +} + +{numbers: {first, second}} = obj2 +print first, second, color +``` +If the destructuring statement is complicated, feel free to spread it out over +a few lines. A slightly more complicated example: + +```moon +{ + numbers: { first, second } + properties: { + color: color + } +} = obj2 +``` + +It's common to extract values from at table and assign them the local variables +that have the same name as the key. In order to avoid repetition we can use the +`:` prefix operator: + +```moon +{:concat, :insert} = table +``` + +This is effectively the same as import, but we can rename fields we want to +extract by mixing the syntax: + +```moon +{:mix, :max, random: rand } = math +``` + +The extracted values don't have to be assigned to plain names. Anything that can +go on the left hand side of an assignment works, like properties and indexes: + +```moon +{x: @x, y: obj.y, z: obj["z"]} = point +``` + +### Destructuring In Other Places + +Destructuring can also show up in places where an assignment implicitly takes +place. An example of this is a `for` loop: + + +```moon +tuples = { + {"hello", "world"} + {"egg", "head"} +} + +for {left, right} in *tuples + print left, right +``` + +We know each element in the array table is a two item tuple, so we can unpack +it directly in the names clause of the for statement using a destructure. + +Destructuring can also be mixed with regular names when assigning multiple +values at once: + +```moon +num, {:message} = get_result! +print num, message +``` + +Function arguments can be destructured as well, see [Argument +Destructuring](#argument_destructuring). - me = create_person "Leaf", {dad, mother, sister} - ``` ## Function Stubs @@ -1082,24 +1515,24 @@ function in the correct context of the object. Its syntax is the same as calling an instance method with the \\ operator but with no argument list provided. - ```moon - my_object = { - value: 1000 - write: => print "the value:", @value - } +```moon +my_object = { + value: 1000 + write: => print "the value:", @value +} - run_callback (func) -> - print "running callback..." - func! +run_callback = (func) -> + print "running callback..." + func! - -- this will not work: - -- the function has to no reference to my_object - run_callback my_object.write +-- this will not work: +-- the function has to no reference to my_object +run_callback my_object.write - -- function stub syntax - -- lets us bundle the object into a new function - run_callback my_object\write - ``` +-- function stub syntax +-- lets us bundle the object into a new function +run_callback my_object\write +``` ## The Using Clause; Controlling Destructive Assignment @@ -1107,21 +1540,21 @@ While lexical scoping can be a great help in reducing the complexity of the code we write, things can get unwieldy as the code size increases. Consider the following snippet: - ```moon - i = 100 +```moon +i = 100 - -- many lines of code... +-- many lines of code... - my_func = -> - i = 10 - while i > 0 - print i - i -= 1 +my_func = -> + i = 10 + while i > 0 + print i + i -= 1 - my_func! +my_func! - print i -- will print 0 - ``` +print i -- will print 0 +``` In `my_func`, we've overwritten the value of `i` mistakenly. In this example it @@ -1135,185 +1568,76 @@ The `using` keyword lets us do that. `using nil` makes sure that no closed variables are overwritten in assignment. The `using` clause is placed after the argument list in a function, or in place of it if there are no arguments. - ```moon - i = 100 +```moon +i = 100 - my_func = (using nil) -> - i = "hello" -- a new local variable is created here +my_func = (using nil) -> + i = "hello" -- a new local variable is created here - my_func! - print i -- prints 100, i is unaffected - ``` +my_func! +print i -- prints 100, i is unaffected +``` Multiple names can be separated by commas. Closure values can still be accessed, they just cant be modified: - ```moon - tmp = 1213 - i, k = 100, 50 +```moon +tmp = 1213 +i, k = 100, 50 - my_func = (add using k,i) -> - tmp = tmp + add -- a new local tmp is created - i += tmp - k += tmp +my_func = (add using k,i) -> + tmp = tmp + add -- a new local tmp is created + i += tmp + k += tmp - my_func(22) - print i,k -- these have been updated - ``` +my_func(22) +print i,k -- these have been updated +``` -# MoonScript API +## Misc. -## `moonscript` Module +### Implicit Returns on Files -Upon installing MoonScript, a `moonscript` module is made available. The best -use of this module is making your Lua's require function MoonScript aware. +By default, a file will also implicitly return like a function. This is useful +for writing modules, where you can put your module's table as the last +statement in the file so it is returned when loaded with `require`. - ```moon - require "moonscript" - ``` +### Writing Modules -After `moonscript` is required, Lua's package loader is updated to search for -`.moon` files on any subsequent calls to `require`. The search path for `.moon` -files is based on the current `package.path` value in Lua when `moonscript` is -required. Any search paths in `package.path` ending in `.lua` are copied, -rewritten to end in `.moon`, and then inserted in `package.moonpath`. +Lua 5.2 has removed the `module` function for creating modules. It is +recommended to return a table instead when defining a module. -The `moonloader` is the function that is responsible for searching -`package.moonpath` for a file available to be included. It is inserted in the -second position of the `package.loaders` table. This means that a matching `.moon` file -will be loaded over a matching `.lua` file that has the same base name. +We can cleanly define modules by using the shorthand hash table key/value +syntax: -For more information on Lua's `package.loaders` see [Lua Reference Manual -— -package.loaders](http://www.lua.org/manual/5.1/manual.html#pdf-package.loaders) +```moonret -The `moonloader`, when finding a valid path to a `.moon` file, will parse and -compile the file in memory. The code is then turned into a function using the -built in `load` function, which is run as the module. +MY_CONSTANT = "hello" -## Error Rewriting +my_function = -> print "the function" +my_second_function = -> print "another function" -Runtime errors are given special attention when using the `moonloader`. -Because we start off as MoonScript, but run code as Lua, errors that happen -during runtime report their line numbers as they are in the compiled file. This -can make debugging particularly difficult. +{ :my_function, :my_second_function, :MY_CONSTANT} +``` -Consider the following file with a bug: - - ```moon - add_numbers = (x,y) -> x + z - print add_numbers 10,0 - ``` - -The following error is generated: - - moon:err.moon:1: attempt to perform arithmetic on global 'z' (a nil value) - stack traceback: - err.moon:1: in function 'add_numbers' - err.moon:2: in main chunk - -Instead of the error being reported on line number 3, where it appears in the -Lua file, it is reported on line 1, where the faulty line originated. The -entire stack trace is rewritten in addition to the error. - -## Programmatically Compiling - -The MoonScript module also contains methods for parsing MoonScript text into an -abstract syntax tree, and compiling an instance of a tree into Lua source code. - -Knowledge of this API may be useful for creating tools to aid the generation of -Lua code from MoonScript code. - -Here is a quick example of how you would compile a MoonScript string to a Lua -String: - - ```moon - require "moonscript.parse" - require "moonscript.compile" - - import parse, compile from moonscript - - moon_code = [[(-> print "hello world")!]] - - tree, err = parse.string moon_code - if not tree - error "Parse error: " .. err - - lua_code, err, pos = compile.tree tree - if not lua_code - error compile.format_error err, pos, moon_code - - -- our code is ready - print lua_code - ``` - -# Command Line Use - -Two tools are installed with MoonScript, `moon` and `moonc`. - -`moonc` is for compiling MoonScript code to Lua. -`moon` is for running MoonsScript code directly. - -## `moon` - -`moon` can be used to run MoonsScript files directly from the command line, -without needing a separate compile step. All MoonsScript files are compiled in -memory as they are run. - - ```bash - $ moon my_script.moon - ``` - -Any MoonScript files that are required will also be compiled and run -automatically. - -When an error occurs during runtime, the stack trace is rewritten to give line -numbers from the original `.moon` file. - -If you want to disable [error rewriting](#error_rewriting), you can pass the -`-d` flag. A full list of flags can be seen by passing the `-h` or `--help` -flag. - - -## `moonc` - -`moonc` is used for transforming MoonsScript files into Lua files. -It takes a list of files, compiles them all, and creates the associated `.lua` -files in the same directories. - - ```bash - $ moonc my_script1.moon my_script2.moon ... - ``` - -You can control where the compiled files are put using the `-t` flag, followed -by a directory. - -`moonc` can also take a directory as an argument, and it will recursively scan -for all MoonScript files and compile them. - -`moonc` can write to standard out by passing the `-p` flag. - -Combined with `linotify` on linux, the `-w` flag can be used to watch all files -that match the given search path for changes, and then compile them only when -required. - -A full list of flags can be seen by passing the `-h` or `--help` flag. +If you need to forward declare your values so you can access them regardless of +their written order you can add `local *` to the top of your file. # License (MIT) - Copyright (C) 2011 by Leaf Corcoran - + Copyright (C) 2020 by Leaf Corcoran + 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: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + 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 diff --git a/docs/reference_manual.html b/docs/reference_manual.html deleted file mode 100644 index d768d16b..00000000 --- a/docs/reference_manual.html +++ /dev/null @@ -1,2416 +0,0 @@ - - - - - - MoonScript v0.2.0 - Language Guide - - - - -
-

MoonScript v0.2.0 - Language Guide

-
- - - -
-

MoonScript is a programming language that compiles to -Lua. This guide expects the reader to have basic -familiarity with Lua. For each code snippet below, the MoonScript is on the -left and the compiled Lua is on right right.

- -

The Language

- -

Assignment

- -

Unlike Lua, there is no local keyword. All assignments to names that are not -already defined will be declared as local to the scope of that declaration. If -you wish to create a global variable it must be done using the export -keyword.

- - - - - -
moonscriptlua
hello = "world"
-a,b,c = 1, 2, 3
local hello = "world"
-local a, b, c = 1, 2, 3
- - -

Update Assignment

- -

+=, -=, /=, *=, %=, ..= operators have been added for updating a value by -a certain amount. They are aliases for their expanded equivalents.

- - - - - -
moonscriptlua
x = 0
-x += 10
-
-s = "hello "
-s ..= "world"
local x = 0
-x = x + 10
-local s = "hello "
-s = s .. "world"
- - -

Comments

- -

Like Lua, comments start with -- and continue to the end of the line. -Comments are not written to the output.

- - - - - -
moonscriptlua
-- I am a comment
- - -

Literals & Operators

- -

MoonScript supports all the same primitive literals as Lua and uses the same -syntax. This applies to numbers, strings, booleans, and nil.

- -

MoonScript also supports all the same binary and unary operators. Additionally -!= is as an alias for ~=.

- -

Function Literals

- -

All functions are created using a function expression. A simple function is -denoted using the arrow: ->

- - - - - -
moonscriptlua
my_function = ->
-my_function() -- call the empty function
local my_function
-my_function = function() end
-my_function()
- - -

The body of the function can either be one statement placed directly after the -arrow, or it can be a series of statements indented on the following lines:

- - - - - -
moonscriptlua
func_a = -> print "hello world"
-
-func_b = ->
-  value = 100
-  print "The value:", value
local func_a
-func_a = function()
-  return print("hello world")
-end
-local func_b
-func_b = function()
-  local value = 100
-  return print("The value:", value)
-end
- - -

If a function has no arguments, it can be called using the ! operator, -instead of empty parentheses. The ! invocation is the preferred way to call -functions with no arguments.

- - - - - -
moonscriptlua
func_a!
-func_b()
func_a()
-func_b()
- - -

Functions with arguments can be created by preceding the arrow with a list of -argument names in parentheses:

- - - - - -
moonscriptlua
sum = (x, y) -> print "sum", x + y
local sum
-sum = function(x, y)
-  return print("sum", x + y)
-end
- - -

Functions can be called by listing the values of the arguments after the name -of the variable where the function is stored. When chaining together function -calls, the arguments are applied to the closest function to the left.

- - - - - -
moonscriptlua
sum 10, 20
-print sum 10, 20
-
-a b c "a", "b", "c"
sum(10, 20)
-print(sum(10, 20))
-a(b(c("a", "b", "c")))
- - -

In order to avoid ambiguity in when calling functions, parentheses can also be -used to surround the arguments. This is required here in order to make sure the -right arguments get sent to the right functions.

- - - - - -
moonscriptlua
print "x:", sum(10, 20), "y:", sum(30, 40)
print("x:", sum(10, 20), "y:", sum(30, 40))
- - -

Functions will coerce the last statement in their body into a return statement, -this is called implicit return:

- - - - - -
moonscriptlua
sum = (x, y) -> x + y
-print "The sum is ", sum 10, 20
local sum
-sum = function(x, y)
-  return x + y
-end
-print("The sum is ", sum(10, 20))
- - -

And if you need to explicitly return, you can use the return keyword:

- - - - - -
moonscriptlua
sum = (x, y) -> return x + y
local sum
-sum = function(x, y)
-  return x + y
-end
- - -

Just like in Lua, functions can return multiple values. The last statement must -be a list of values separated by commas:

- - - - - -
moonscriptlua
mystery = (x, y) -> x + y, x - y
-a,b = mystery 10, 20
local mystery
-mystery = function(x, y)
-  return x + y, x - y
-end
-local a, b = mystery(10, 20)
- - -

Fat Arrows

- -

Because it is an idiom in Lua to send an object as the first argument when -calling a method, a special syntax is provided for creating functions which -automatically includes a self argument.

- - - - - -
moonscriptlua
func = (num) => self.value + num
local func
-func = function(self, num)
-  return self.value + num
-end
- - -

Argument Defaults

- -

It is possible to provide default values for the arguments of a function. An -argument is determined to be empty if it’s value is nil. Any nil arguments -that have a default value will be replace before the body of the function is run.

- - - - - -
moonscriptlua
my_function = (name="something", height=100) ->
-  print "Hello I am", name
-  print "My height is", height
local my_function
-my_function = function(name, height)
-  if name == nil then
-    name = "something"
-  end
-  if height == nil then
-    height = 100
-  end
-  print("Hello I am", name)
-  return print("My height is", height)
-end
- - -

An argument default value expression is evaluated in the body of the function -in the order of the argument declarations. For this reason default values have -access to previously declared arguments.

- - - - - -
moonscriptlua
some_args = (x=100, y=x+1000) ->
-  print x + y
local some_args
-some_args = function(x, y)
-  if x == nil then
-    x = 100
-  end
-  if y == nil then
-    y = x + 1000
-  end
-  return print(x + y)
-end
- - -

Considerations

- -

Because of the expressive parentheses-less way of calling functions, some -restrictions must be put in place to avoid parsing ambiguity involving -whitespace.

- -

The minus sign plays two roles, a unary negation operator and a binary -subtraction operator. In order to force subtraction a space must be placed -after the - operator. In order to force a negation, no space must follow -the -. Consider the examples below.

- - - - - -
moonscriptlua
a = x - 10
-b = x-10
-c = x -y
local a = x - 10
-local b = x(-10)
-local c = x(-y)
- - -

The precedence of the first argument of a function call can also be controlled -using whitespace if the argument is a literal string.In Lua, it is common to -leave off parentheses when calling a function with a single string literal.

- -

When there is no space between a variable and a string literal, the -function call takes precedence over any following expressions. No other -arguments can be passed to the function when it is called this way.

- -

Where there is a space following a variable and a string literal, the function -call acts as show above. The string literal belongs to any following -expressions (if they exist), which serves as the argument list.

- - - - - -
moonscriptlua
x = func"hello" + 100
-y = func "hello" + 100
local x = func("hello") + 100
-local y = func("hello" + 100)
- - -

Multi-line arguments

- -

When calling functions that take a large number of arguments, it is convenient -to split the argument list over multiple lines. Because of the white-space -sensitive nature of the language, care must be taken when splitting up the -argument list.

- -

If an argument list is to be continued onto the next line, the current line -must end in a comma. And the following line must be indented more than the -current indentation. Once indented, all other argument lines must be at the -same level of indentation to be part of the argument list

- - - - - -
moonscriptlua
my_func 5,4,3,
-  8,9,10
-
-cool_func 1,2,
-  3,4,
-  5,6,
-  7,8
my_func(5, 4, 3, 8, 9, 10)
-cool_func(1, 2, 3, 4, 5, 6, 7, 8)
- - -

This type of invocation can be nested. The level of indentation is used to -determine to which function the arguments belong to.

- - - - - -
moonscriptlua
my_func 5,6,7,
-  6, another_func 6,7,8,
-    9,1,2,
-  5,4
my_func(5, 6, 7, 6, another_func(6, 7, 8, 9, 1, 2), 5, 4)
- - -

Because tables also use the comma as a delimiter, this -indentation syntax is helpful for letting values be part of the argument list -instead of being part of the table.

- - - - - -
moonscriptlua
x = {
-  1,2,3,4, a_func 4,5,
-    5,6,
-  8,9,10
-}
local x = {
-  1,
-  2,
-  3,
-  4,
-  a_func(4, 5, 5, 6),
-  8,
-  9,
-  10
-}
- - -

Although uncommon, notice how we can give a deeper indentation for function -arguments if we know we will be using a lower indentation futher on.

- - - - - -
moonscriptlua
y = { my_func 1,2,3,
-   4,5,
-  5,6,7
-}
local y = {
-  my_func(1, 2, 3, 4, 5),
-  5,
-  6,
-  7
-}
- - -

The same thing can be done with other block level statements like -conditionals. We can use indentation level to determine what -statement a value belongs to:

- - - - - -
moonscriptlua
if func 1,2,3,
-  "hello",
-  "world"
-    print "hello"
-    print "I am inside if"
-
-if func 1,2,3,
-    "hello",
-    "world"
-  print "hello"
-  print "I am inside if"
if func(1, 2, 3, "hello", "world") then
-  print("hello")
-  print("I am inside if")
-end
-if func(1, 2, 3, "hello", "world") then
-  print("hello")
-  print("I am inside if")
-end
- - -

Table Literals

- -

Like in Lua, tables are delimited in curly braces.

- - - - - -
moonscriptlua
some_values = { 1, 2, 3, 4 }
local some_values = {
-  1,
-  2,
-  3,
-  4
-}
- - -

Unlike Lua, assigning a value to a key in a table is done with : (instead of -=).

- - - - - -
moonscriptlua
some_values = {
-  name: "Bill",
-  age: 200,
-  ["favorite food"]: "rice"
-}
local some_values = {
-  name = "Bill",
-  age = 200,
-  ["favorite food"] = "rice"
-}
- - -

The curly braces can be left off if a single table of key value pairs is being -assigned.

- - - - - -
moonscriptlua
profile = 
-  height: "4 feet",
-  shoe_size: 13,
-  favorite_foods: {"ice cream", "donuts"}
local profile = {
-  height = "4 feet",
-  shoe_size = 13,
-  favorite_foods = {
-    "ice cream",
-    "donuts"
-  }
-}
- - -

Newlines can be used to delimit values instead of a comma (or both):

- - - - - -
moonscriptlua
values = {
-  1,2,3,4
-  5,6,7,8
-  name: "superman"
-  occupation: "crime fighting"
-}
local values = {
-  1,
-  2,
-  3,
-  4,
-  5,
-  6,
-  7,
-  8,
-  name = "superman",
-  occupation = "crime fighting"
-}
- - -

When creating a single line table literal, the curly braces can also be left -off:

- - - - - -
moonscriptlua
my_function dance: "Tango", partner: "none"
-
-y = type: "dog", legs: 4, tails: 1
my_function({
-  dance = "Tango",
-  partner = "none"
-})
-local y = {
-  type = "dog",
-  legs = 4,
-  tails = 1
-}
- - -

The keys of a table literal can be language keywords without being escaped:

- - - - - -
moonscriptlua
tbl = {
-  do: "something"
-  end: "hunger"
-}
local tbl = {
-  ["do"] = "something",
-  ["end"] = "hunger"
-}
- - -

If you are constructing a table out of variables and wish the keys to be the -same as the variable names, then the : prefix operator can be used:

- - - - - -
moonscriptlua
hair = "golden"
-height = 200
-person = { :hair, :height, shoe_size: 40 }
-
-print_table :hair, :height
local hair = "golden"
-local height = 200
-local person = {
-  hair = hair,
-  height = height,
-  shoe_size = 40
-}
-print_table({
-  hair = hair,
-  height = height
-})
- - -

Comprehensions

- -

Compiling provide a convenient syntax for constructing a new table by iterating -over some existing object and applying an expression to its values. There are -two kinds of comprehensions: list comprehensions and table comprehensions. They -both produce Lua tables; list comprehensions accumulate values into an -array-like table, and table comprehensions let you set both the key and the -value on each iteration.

- -

List Comprehensions

- -

The following creates a copy of the items table but with all the values -doubled.

- - - - - -
moonscriptlua
items = { 1, 2, 3, 4 }
-doubled = [item * 2 for i, item in ipairs items]
local items = {
-  1,
-  2,
-  3,
-  4
-}
-local doubled = (function()
-  local _accum_0 = { }
-  local _len_0 = 0
-  for i, item in ipairs(items) do
-    _len_0 = _len_0 + 1
-    _accum_0[_len_0] = item * 2
-  end
-  return _accum_0
-end)()
- - -

The items included in the new table can be restricted with a when clause:

- - - - - -
moonscriptlua
iter = ipairs items
-slice = [item for i, item in iter when i > 1 and i < 3]
local iter = ipairs(items)
-local slice = (function()
-  local _accum_0 = { }
-  local _len_0 = 0
-  for i, item in iter do
-    if i > 1 and i < 3 then
-      _len_0 = _len_0 + 1
-      _accum_0[_len_0] = item
-    end
-  end
-  return _accum_0
-end)()
- - -

Because it is common to iterate over the values of a numerically indexed table, -an * operator is introduced. The doubled example can be rewritten as:

- - - - - -
moonscriptlua
doubled = [item * 2 for item in *items]
local doubled = (function()
-  local _accum_0 = { }
-  local _len_0 = 0
-  local _list_0 = items
-  for _index_0 = 1, #_list_0 do
-    local item = _list_0[_index_0]
-    _len_0 = _len_0 + 1
-    _accum_0[_len_0] = item * 2
-  end
-  return _accum_0
-end)()
- - -

The for and when clauses can be chained as much as desired. The only -requirement is that a comprehension has at least one for clause.

- -

Using multiple for clauses is the same as using nested loops:

- - - - - -
moonscriptlua
x_coords = {4, 5, 6, 7}
-y_coords = {9, 2, 3}
-
-points = [{x,y} for x in *x_coords for y in *y_coords]
local x_coords = {
-  4,
-  5,
-  6,
-  7
-}
-local y_coords = {
-  9,
-  2,
-  3
-}
-local points = (function()
-  local _accum_0 = { }
-  local _len_0 = 0
-  local _list_0 = x_coords
-  for _index_0 = 1, #_list_0 do
-    local x = _list_0[_index_0]
-    local _list_1 = y_coords
-    for _index_0 = 1, #_list_1 do
-      local y = _list_1[_index_0]
-      _len_0 = _len_0 + 1
-      _accum_0[_len_0] = {
-        x,
-        y
-      }
-    end
-  end
-  return _accum_0
-end)()
- - -

Table Comprehensions

- -

The syntax for table comprehensions is very similar, differing by using { and -} and taking two values from each iteration.

- -

This example copies the key-value table thing:

- - - - - -
moonscriptlua
thing = {
-  color: "red"
-  name: "fast"
-  width: 123
-}
-
-thing_copy = {k,v for k,v in pairs thing}
local thing = {
-  color = "red",
-  name = "fast",
-  width = 123
-}
-local thing_copy = (function()
-  local _tbl_0 = { }
-  for k, v in pairs(thing) do
-    _tbl_0[k] = v
-  end
-  return _tbl_0
-end)()
- - -

Table comprehensions, like list comprehensions, also support multiple for and -when clauses. In this example we use a where clause to prevent the value -associated with the color key from being copied.

- - - - - -
moonscriptlua
no_color = {k,v for k,v in pairs thing when k != "color"}
local no_color = (function()
-  local _tbl_0 = { }
-  for k, v in pairs(thing) do
-    if k ~= "color" then
-      _tbl_0[k] = v
-    end
-  end
-  return _tbl_0
-end)()
- - -

The * operator is also supported. Here we create a square root look up table -for a few numbers.

- - - - - -
moonscriptlua
numbers = {1,2,3,4}
-sqrts = {i, math.sqrt i for i in *numbers}
local numbers = {
-  1,
-  2,
-  3,
-  4
-}
-local sqrts = (function()
-  local _tbl_0 = { }
-  local _list_0 = numbers
-  for _index_0 = 1, #_list_0 do
-    local i = _list_0[_index_0]
-    _tbl_0[i] = math.sqrt(i)
-  end
-  return _tbl_0
-end)()
- - -

Slicing

- -

A special syntax is provided to restrict the items that are iterated over when -using the * operator. This is equivalent to setting the iteration bounds and -a step size in a for loop.

- -

Here we can set the minimum and maximum bounds, taking all items with indexes -between 1 and 5 inclusive:

- - - - - -
moonscriptlua
slice = [item for item in *items[1,5]]
local slice = (function()
-  local _accum_0 = { }
-  local _len_0 = 0
-  local _list_0 = items
-  local _max_0 = 5
-  for _index_0 = 1, _max_0 < 0 and #_list_0 + _max_0 or _max_0 do
-    local item = _list_0[_index_0]
-    _len_0 = _len_0 + 1
-    _accum_0[_len_0] = item
-  end
-  return _accum_0
-end)()
- - -

Any of the slice arguments can be left off to use a sensible default. In this -example, if the max index is left off it defaults to the length of the table. -This will take everything but the first element:

- - - - - -
moonscriptlua
slice = [item for item in *items[2,]]
local slice = (function()
-  local _accum_0 = { }
-  local _len_0 = 0
-  local _list_0 = items
-  for _index_0 = 2, #_list_0 do
-    local item = _list_0[_index_0]
-    _len_0 = _len_0 + 1
-    _accum_0[_len_0] = item
-  end
-  return _accum_0
-end)()
- - -

If the minimum bound is left out, it defaults to 1. Here we only provide a step -size and leave the other bounds blank. This takes all odd indexed items: (1, 3, -5, …)

- - - - - -
moonscriptlua
slice = [item for items in *items[,,2]]
local slice = (function()
-  local _accum_0 = { }
-  local _len_0 = 0
-  local _list_0 = items
-  for _index_0 = 1, #_list_0, 2 do
-    local items = _list_0[_index_0]
-    _len_0 = _len_0 + 1
-    _accum_0[_len_0] = item
-  end
-  return _accum_0
-end)()
- - -

For Loop

- -

There are two for loop forms, just like in Lua. A numeric one and a generic one:

- - - - - -
moonscriptlua
for i = 10, 20
-  print i
-
-for k = 1,15,2 -- an optional step provided
-  print k 
-
-for key, value in pairs object
-  print key, value
for i = 10, 20 do
-  print(i)
-end
-for k = 1, 15, 2 do
-  print(k)
-end
-for key, value in pairs(object) do
-  print(key, value)
-end
- - -

The slicing and * operators can be used, just like with table comprehensions:

- - - - - -
moonscriptlua
for item in *items[2,4]
-  print item
local _list_0 = items
-local _max_0 = 4
-for _index_0 = 2, _max_0 < 0 and #_list_0 + _max_0 or _max_0 do
-  local item = _list_0[_index_0]
-  print(item)
-end
- - -

A shorter syntax is also available for all variations when the body is only a -single line:

- - - - - -
moonscriptlua
for item in *items do print item
-
-for j = 1,10,3 do print j
local _list_0 = items
-for _index_0 = 1, #_list_0 do
-  local item = _list_0[_index_0]
-  print(item)
-end
-for j = 1, 10, 3 do
-  print(j)
-end
- - -

A for loop can also be used an expression. The last statement in the body of -the for loop is coerced into an expression and appended to an accumulating -table if the value of that expression is not nil.

- -

Doubling every even number:

- - - - - -
moonscriptlua
doubled_evens = for i=1,20
-  if i % 2 == 0
-    i * 2
-  else
-    i
local doubled_evens = (function()
-  local _accum_0 = { }
-  local _len_0 = 0
-  for i = 1, 20 do
-    local _value_0
-    if i % 2 == 0 then
-      _value_0 = i * 2
-    else
-      _value_0 = i
-    end
-    if _value_0 ~= nil then
-      _len_0 = _len_0 + 1
-      _accum_0[_len_0] = _value_0
-    end
-  end
-  return _accum_0
-end)()
- - -

Filtering out odd numbers:

- - - - - -
moonscriptlua
my_numbers = {1,2,3,4,5,6}
-odds = for x in *my_numbers
-  if x % 2 == 1 then x
local my_numbers = {
-  1,
-  2,
-  3,
-  4,
-  5,
-  6
-}
-local odds = (function()
-  local _accum_0 = { }
-  local _len_0 = 0
-  local _list_0 = my_numbers
-  for _index_0 = 1, #_list_0 do
-    local x = _list_0[_index_0]
-    local _value_0
-    if x % 2 == 1 then
-      _value_0 = x
-    end
-    if _value_0 ~= nil then
-      _len_0 = _len_0 + 1
-      _accum_0[_len_0] = _value_0
-    end
-  end
-  return _accum_0
-end)()
- - -

For loops at the end of a function body are not accumulated into a table for a -return value (Instead the function will return nil). Either an explicit -return statement can be used, or the loop can be converted into a list -comprehension.

- - - - - -
moonscriptlua
func_a = -> for i=1,10 do i
-func_b = -> return for i=1,10 do i
-
-print func_a! -- prints nil
-print func_b! -- prints table object
local func_a
-func_a = function()
-  for i = 1, 10 do
-    local _ = i
-  end
-end
-local func_b
-func_b = function()
-  return (function()
-    local _accum_0 = { }
-    local _len_0 = 0
-    for i = 1, 10 do
-      local _value_0 = i
-      if _value_0 ~= nil then
-        _len_0 = _len_0 + 1
-        _accum_0[_len_0] = _value_0
-      end
-    end
-    return _accum_0
-  end)()
-end
-print(func_a())
-print(func_b())
- - -

This is done to avoid the needless creation of tables for functions that don’t -need to return the results of the loop.

- -

While Loop

- -

The while loop also comes in two variations:

- - - - - -
moonscriptlua
i = 10
-while i > 0
-  print i
-  i -= 1
-
-while running == true do my_function!
local i = 10
-while i > 0 do
-  print(i)
-  i = i - 1
-end
-while running == true do
-  my_function()
-end
- - -

Like for loops, the while loop can also be used an expression. Additionally, -for a function to return the accumulated value of a while loop, the statement -must be explicitly returned.

- -

Conditionals

- - - - - -
moonscriptlua
have_coins = false
-if have_coins
-  print "Got coins"
-else
-  print "No coins"
local have_coins = false
-if have_coins then
-  print("Got coins")
-else
-  print("No coins")
-end
- - -

A short syntax for single statements can also be used:

- - - - - -
moonscriptlua
have_coins = false
-if have_coins then print "Got coins" else print "No coins"
local have_coins = false
-if have_coins then
-  print("Got coins")
-else
-  print("No coins")
-end
- - -

Because if statements can be used as expressions, this can able be written as:

- - - - - -
moonscriptlua
have_coins = false
-print if have_coins then "Got coins" else "No coins"
local have_coins = false
-print((function()
-  if have_coins then
-    return "Got coins"
-  else
-    return "No coins"
-  end
-end)())
- - -

Conditionals can also be used in return statements and assignments:

- - - - - -
moonscriptlua
is_tall = (name) ->
-  if name == "Rob"
-    true
-  else
-    false
-
-message = if is_tall "Rob"
-  "I am very tall"
-else
-  "I am not so tall"
-
-print message -- prints: I am very tall
local is_tall
-is_tall = function(name)
-  if name == "Rob" then
-    return true
-  else
-    return false
-  end
-end
-local message
-if is_tall("Rob") then
-  message = "I am very tall"
-else
-  message = "I am not so tall"
-end
-print(message)
- - -

Line Decorators

- -

For convenience, the for loop and if statement can be applied to single -statements at the end of the line:

- - - - - -
moonscriptlua
print "hello world" if name == "Rob"
if name == "Rob" then
-  print("hello world")
-end
- - -

And with basic loops:

- - - - - -
moonscriptlua
print "item: ", item for item in *items
local _list_0 = items
-for _index_0 = 1, #_list_0 do
-  local item = _list_0[_index_0]
-  print("item: ", item)
-end
- - -

Switch

- -

The switch statement is shorthand for writing a series of if statements that -check against the same value. Note that the value is only evaluated once. Like -if statements, switches can have an else block to handle no matches. Comparison -is done with the == operator.

- - - - - -
moonscriptlua
name = "Dan"
-switch name
-  when "Robert"
-    print "You are robert"
-  when "Dan"
-    print "Your name, it's Dan"
-  else
-    print "I don't know about your name"
local name = "Dan"
-local _exp_0 = name
-if "Robert" == _exp_0 then
-  print("You are robert")
-elseif "Dan" == _exp_0 then
-  print("Your name, it's Dan")
-else
-  print("I don't know about your name")
-end
- - -

Switches can be used as expressions as well, here we can assign the result of -the switch to a variable:

- - - - - -
moonscriptlua
b = 1
-next_number = switch b
-  when 1
-    2
-  when 2
-    3
-  else
-    error "can't count that high!"
local b = 1
-local next_number
-local _exp_0 = b
-if 1 == _exp_0 then
-  next_number = 2
-elseif 2 == _exp_0 then
-  next_number = 3
-else
-  next_number = error("can't count that high!")
-end
- - -

We can use the then keyword to write a switch’s when block on a single line. -No extra keyword is needed to write the else block on a single line.

- - - - - -
moonscriptlua
msg = switch math.random(1, 5)
-  when 1 then "you are lucky"
-  when 2 then "you are almost lucky"
-  else "not so lucky"
local msg
-local _exp_0 = math.random(1, 5)
-if 1 == _exp_0 then
-  msg = "you are lucky"
-elseif 2 == _exp_0 then
-  msg = "you are almost lucky"
-else
-  msg = "not so lucky"
-end
- - -

It is worth noting the order of the case comparison expression. The case’s -expression is on the left hand side. This can be useful if the case’s -expression wants to overwrite how the comparison is done by defining an eq -metamethod.

- -

Object Oriented Programming

- -

In these examples, the generated Lua code may appear overwhelming. It is best -to focus on the meaning of the MoonScript code at first, then look into the Lua -code if you wish to know the implementation details.

- -

A simple class:

- - - - - -
moonscriptlua
class Inventory
-  new: =>
-    @items = {}
-
-  add_item: (name) =>
-    if @items[name]
-      @items[name] += 1
-    else
-      @items[name] = 1
local Inventory
-Inventory = (function()
-  local _parent_0 = nil
-  local _base_0 = {
-    add_item = function(self, name)
-      if self.items[name] then
-        self.items[name] = self.items[name] + 1
-        return self.items[name]
-      else
-        self.items[name] = 1
-      end
-    end
-  }
-  _base_0.__index = _base_0
-  if _parent_0 then
-    setmetatable(_base_0, _parent_0.__base)
-  end
-  local _class_0 = setmetatable({
-    __init = function(self)
-      self.items = { }
-    end,
-    __base = _base_0,
-    __name = "Inventory",
-    __parent = _parent_0
-  }, {
-    __index = function(cls, name)
-      local val = rawget(_base_0, name)
-      if val == nil and _parent_0 then
-        return _parent_0[name]
-      else
-        return val
-      end
-    end,
-    __call = function(cls, ...)
-      local _self_0 = setmetatable({}, _base_0)
-      cls.__init(_self_0, ...)
-      return _self_0
-    end
-  })
-  _base_0.__class = _class_0
-  return _class_0
-end)()
- - -

A class is declared with a class statement followed by a table-like -declaration where all of the methods and properties are listed.

- -

The new property is special in that it will become the constructor.

- -

Notice how all the methods in the class use the fat arrow function syntax. When -calling methods on a instance, the instance itself is sent in as the first -argument. The fat arrow handles the creation of a self argument.

- -

The @ prefix on a variable name is shorthand for self.. @items becomes -self.items.

- -

Creating an instance of the class is done by calling the name of the class as a -function.

- - - - - -
moonscriptlua
inv = Inventory!
-inv\add_item "t-shirt"
-inv\add_item "pants"
local inv = Inventory()
-inv:add_item("t-shirt")
-inv:add_item("pants")
- - -

Because the instance of the class needs to be sent to the methods when they are -called, the ‘' operator is used.

- -

All properties of a class are shared among the instances. This is fine for -functions, but for other types of objects, undesired results may occur.

- -

Consider the example below, the clothes property is shared amongst all -instances, so modifications to it in one instance will show up in another:

- - - - - -
moonscriptlua
class Person
-  clothes: {}
-  give_item: (name) =>
-    table.insert @clothes, name
-
-a = Person!
-b = Person!
-
-a\give_item "pants"
-b\give_item "shirt"
-
--- will print both pants and shirt
-print item for item in *a.clothes
local Person
-Person = (function()
-  local _parent_0 = nil
-  local _base_0 = {
-    clothes = { },
-    give_item = function(self, name)
-      return table.insert(self.clothes, name)
-    end
-  }
-  _base_0.__index = _base_0
-  if _parent_0 then
-    setmetatable(_base_0, _parent_0.__base)
-  end
-  local _class_0 = setmetatable({
-    __init = function(self, ...)
-      if _parent_0 then
-        return _parent_0.__init(self, ...)
-      end
-    end,
-    __base = _base_0,
-    __name = "Person",
-    __parent = _parent_0
-  }, {
-    __index = function(cls, name)
-      local val = rawget(_base_0, name)
-      if val == nil and _parent_0 then
-        return _parent_0[name]
-      else
-        return val
-      end
-    end,
-    __call = function(cls, ...)
-      local _self_0 = setmetatable({}, _base_0)
-      cls.__init(_self_0, ...)
-      return _self_0
-    end
-  })
-  _base_0.__class = _class_0
-  return _class_0
-end)()
-local a = Person()
-local b = Person()
-a:give_item("pants")
-b:give_item("shirt")
-local _list_0 = a.clothes
-for _index_0 = 1, #_list_0 do
-  local item = _list_0[_index_0]
-  print(item)
-end
- - -

The proper way to avoid this problem is to create the mutable state of the -object in the constructor:

- - - - - -
moonscriptlua
class Person
-  new: =>
-    @clothes = {}
local Person
-Person = (function()
-  local _parent_0 = nil
-  local _base_0 = { }
-  _base_0.__index = _base_0
-  if _parent_0 then
-    setmetatable(_base_0, _parent_0.__base)
-  end
-  local _class_0 = setmetatable({
-    __init = function(self)
-      self.clothes = { }
-    end,
-    __base = _base_0,
-    __name = "Person",
-    __parent = _parent_0
-  }, {
-    __index = function(cls, name)
-      local val = rawget(_base_0, name)
-      if val == nil and _parent_0 then
-        return _parent_0[name]
-      else
-        return val
-      end
-    end,
-    __call = function(cls, ...)
-      local _self_0 = setmetatable({}, _base_0)
-      cls.__init(_self_0, ...)
-      return _self_0
-    end
-  })
-  _base_0.__class = _class_0
-  return _class_0
-end)()
- - -

Inheritance

- -

The extends keyword can be used in a class declaration to inherit the -properties and methods from another class.

- - - - - -
moonscriptlua
class BackPack extends Inventory
-  size: 10
-  add_item: (name) =>
-    if #@items > size then error "backpack is full"
-    super name
local BackPack
-BackPack = (function()
-  local _parent_0 = Inventory
-  local _base_0 = {
-    size = 10,
-    add_item = function(self, name)
-      if #self.items > size then
-        error("backpack is full")
-      end
-      return _parent_0.add_item(self, name)
-    end
-  }
-  _base_0.__index = _base_0
-  if _parent_0 then
-    setmetatable(_base_0, _parent_0.__base)
-  end
-  local _class_0 = setmetatable({
-    __init = function(self, ...)
-      if _parent_0 then
-        return _parent_0.__init(self, ...)
-      end
-    end,
-    __base = _base_0,
-    __name = "BackPack",
-    __parent = _parent_0
-  }, {
-    __index = function(cls, name)
-      local val = rawget(_base_0, name)
-      if val == nil and _parent_0 then
-        return _parent_0[name]
-      else
-        return val
-      end
-    end,
-    __call = function(cls, ...)
-      local _self_0 = setmetatable({}, _base_0)
-      cls.__init(_self_0, ...)
-      return _self_0
-    end
-  })
-  _base_0.__class = _class_0
-  return _class_0
-end)()
- - -

Here we extend our Inventory class, and limit the amount of items it can carry.

- -

Super

- -

super is a special keyword that can be used in two different ways: It can be -treated as an object, or it can be called like a function. It only has special -functionality when inside a class.

- -

When called as a function, it will call the function of the same name in the -parent class. The current self will automatically be passed as the first -argument. (As seen in the inheritance example above)

- -

When super is used as a normal value, it is a reference to the parent class -object.

- -

It can be accessed like any of object in order to retrieve values in the -parent class that might have been shadowed by the child class.

- -

When the \ calling operator is used with super, self is inserted as the -first argument instead of the value of super itself. When using . to -retrieve a function, the raw function is returned.

- -

A few examples of using super in different ways:

- - - - - -
moonscriptlua
class MyClass extends ParentClass
-  a_method: =>
-    -- the following have the same effect:
-    super "hello", "world"
-    super\a_method "hello", "world"
-    super.a_method self, "hello", "world"
-
-    -- super as a value is equal to the parent class:
-    assert super == ParentClass
local MyClass
-MyClass = (function()
-  local _parent_0 = ParentClass
-  local _base_0 = {
-    a_method = function(self)
-      _parent_0.a_method(self, "hello", "world")
-      _parent_0.a_method(self, "hello", "world")
-      _parent_0.a_method(self, "hello", "world")
-      return assert(_parent_0 == ParentClass)
-    end
-  }
-  _base_0.__index = _base_0
-  if _parent_0 then
-    setmetatable(_base_0, _parent_0.__base)
-  end
-  local _class_0 = setmetatable({
-    __init = function(self, ...)
-      if _parent_0 then
-        return _parent_0.__init(self, ...)
-      end
-    end,
-    __base = _base_0,
-    __name = "MyClass",
-    __parent = _parent_0
-  }, {
-    __index = function(cls, name)
-      local val = rawget(_base_0, name)
-      if val == nil and _parent_0 then
-        return _parent_0[name]
-      else
-        return val
-      end
-    end,
-    __call = function(cls, ...)
-      local _self_0 = setmetatable({}, _base_0)
-      cls.__init(_self_0, ...)
-      return _self_0
-    end
-  })
-  _base_0.__class = _class_0
-  return _class_0
-end)()
- - -

super can also be used on left side of a Function Stub. -The only major difference is that instead of the resulting function being bound -to the value of super, it is bound to self.

- -

Types

- -

Every instance of a class carries its type with it. This is stored in the -special __class property. This property holds the class object. The class -object is what we call to build a new instance. We can also index the class -object to retrieve class methods and properties.

- - - - - -
moonscriptlua
b = BackPack!
-assert b.__class == BackPack
-
-print BackPack.size -- prints 10
local b = BackPack()
-assert(b.__class == BackPack)
-print(BackPack.size)
- - -

Export Statement

- -

Because, by default, all assignments to variables that are not lexically visible will -be declared as local, special syntax is required to declare a variable globally.

- -

The export keyword makes it so any following assignments to the specified names -will not be assigned locally.

- - - - - -
moonscriptlua
export var_name, var_name2
-var_name, var_name3 = "hello", "world"
local var_name3
-var_name, var_name3 = "hello", "world"
- - -

This is especially useful when declaring what will be externally visible in a -module:

- - - - - -
moonscriptlua
-- my_module.moon
-module "my_module", package.seeall
-export print_result
-
-length = (x, y) -> math.sqrt x*x + y*y
-
-print_result = (x, y) ->
-  print "Length is ", length x, y
-
--- main.moon
-require "my_module"
-
-my_module.print_result 4, 5 -- prints the result
-
-print my_module.length 6, 7 -- errors, `length` not visible
module("my_module", package.seeall)
-local length
-length = function(x, y)
-  return math.sqrt(x * x + y * y)
-end
-print_result = function(x, y)
-  return print("Length is ", length(x, y))
-end
-require("my_module")
-my_module.print_result(4, 5)
-print(my_module.length(6, 7))
- - -

Assignment can be combined with the export keyword to assign to global -variables directly:

- - - - - -
moonscriptlua
export some_number, message_str = 100, "hello world"
some_number, message_str = 100, "hello world"
- - -

Additionally, a class declaration can be prefixed with the export keyword in -order to export it.

- -

Export All & Export Proper

- -

The export statement can also take special symbols * and ^.

- -

export * will cause any name declared after the statement to be exported in the -current scope. export ^ will export all proper names, names that begin with a -capital letter.

- -

Import Statement

- -

Often you want to bring some values from a table into the current scope as -local variables by their name. The import statement lets us accomplish this:

- - - - - -
moonscriptlua
import insert from table
local insert = table.insert
- - -

The multiple names can be given, each separated by a comma:

- - - - - -
moonscriptlua
import C, Ct, Cmt from lpeg
local C, Ct, Cmt = lpeg.C, lpeg.Ct, lpeg.Cmt
- - -

Sometimes a function requires that the table be sent in as the first argument -(when using the \ syntax). As a shortcut, we can prefix the name -with a \ to bind it to that table:

- - - - - -
moonscriptlua
-- some object
-my_module =
-    state: 100
-    add: (value) =>
-        self.state + value
-
-import \add from my_module
-
-print add(22) -- equivalent to calling my_module\get 22
local my_module = {
-  state = 100,
-  add = function(self, value)
-    return self.state + value
-  end
-}
-local add = (function()
-  local _base_0 = my_module
-  local _fn_0 = _base_0.add
-  return function(...)
-    return _fn_0(_base_0, ...)
-  end
-end)()
-print(add(22))
- - -

With Statement

- -

A common pattern involving the creation of an object is calling a series of -functions and setting a series of properties immediately after creating it.

- -

This results in repeating the name of the object multiple times in code, adding -unnecessary noise. A common solution to this is to pass a table in as an -argument which contains a collection of keys and values to overwrite. The -downside to this is that the constructor of this object must support this form.

- -

The with block helps to alleviate this. Within a with block we can use a -special statements that begin with either . or \ which represent -those operations applied to the object we are using with on.

- -

For example, we work with a newly created object:

- - - - - -
moonscriptlua
with Person!
-  .name = "Oswald"
-  \add_relative my_dad
-  \save!
-  print .name
do
-  local _with_0 = Person()
-  _with_0.name = "Oswald"
-  _with_0:add_relative(my_dad)
-  _with_0:save()
-  print(_with_0.name)
-end
- - -

The with statement can also be used as an expression which returns the value -it has been giving access to.

- - - - - -
moonscriptlua
file = with File "favorite_foods.txt"
-  \set_encoding "utf8"
local file
-do
-  local _with_0 = File("favorite_foods.txt")
-  _with_0:set_encoding("utf8")
-  file = _with_0
-end
- - -

Or…

- - - - - -
moonscriptlua
create_person = (name,  relatives) ->
-  with Person!
-    .name = name
-    \add_relative relative for relative in *relatives
-
-me = create_person "Leaf", {dad, mother, sister}
local create_person
-create_person = function(name, relatives)
-  do
-    local _with_0 = Person()
-    _with_0.name = name
-    local _list_0 = relatives
-    for _index_0 = 1, #_list_0 do
-      local relative = _list_0[_index_0]
-      _with_0:add_relative(relative)
-    end
-    return _with_0
-  end
-end
-local me = create_person("Leaf", {
-  dad,
-  mother,
-  sister
-})
- - -

Function Stubs

- -

It is common to pass a function from an object around as a value, for example, -passing an instance method into a function as a callback. If the function -expects the object it is operating on as the first argument then you must -somehow bundle that object with the function so it can be called properly.

- -

The function stub syntax is a shorthand for creating a new closure function -that bundles both the object and function. This new function calls the wrapped -function in the correct context of the object.

- -

Its syntax is the same as calling an instance method with the \ operator but -with no argument list provided.

- - - - - -
moonscriptlua
my_object = {
-  value: 1000
-  write: => print "the value:", @value
-}
-
-run_callback (func) ->
-  print "running callback..."
-  func!
-
--- this will not work:
--- the function has to no reference to my_object
-run_callback my_object.write
-
--- function stub syntax
--- lets us bundle the object into a new function
-run_callback my_object\write
local my_object = {
-  value = 1000,
-  write = function(self)
-    return print("the value:", self.value)
-  end
-}
-run_callback(function(func)
-  print("running callback...")
-  return func()
-end)
-run_callback(my_object.write)
-run_callback((function()
-  local _base_0 = my_object
-  local _fn_0 = _base_0.write
-  return function(...)
-    return _fn_0(_base_0, ...)
-  end
-end)())
- - -

The Using Clause; Controlling Destructive Assignment

- -

While lexical scoping can be a great help in reducing the complexity of the -code we write, things can get unwieldy as the code size increases. Consider -the following snippet:

- - - - - -
moonscriptlua
i = 100
-
--- many lines of code...
-
-my_func = ->
-    i = 10
-    while i > 0
-        print i
-        i -= 1
-
-my_func()
-
-print i -- will print 0
local i = 100
-local my_func
-my_func = function()
-  i = 10
-  while i > 0 do
-    print(i)
-    i = i - 1
-  end
-end
-my_func()
-print(i)
- - -

In my_func, we've overwritten the value of i mistakenly. In this example it -is quite obvious, but consider a large, or foreign code base where it isn’t -clear what names have already been declared.

- -

It would be helpful to say which variables from the enclosing scope we intend -on change, in order to prevent us from changing others by accident.

- -

The using keyword lets us do that. using nil makes sure that no closed -variables are overwritten in assignment. The using clause is placed after the -argument list in a function, or in place of it if there are no arguments.

- - - - - -
moonscriptlua
i = 100
-
-my_func = (using nil) ->
-    i = "hello" -- a new local variable is created here
-
-my_func()
-print i -- prints 100, i is unaffected
local i = 100
-local my_func
-my_func = function()
-  local i = "hello"
-end
-my_func()
-print(i)
- - -

Multiple names can be separated by commas. Closure values can still be -accessed, they just cant be modified:

- - - - - -
moonscriptlua
tmp = 1213
-i, k = 100, 50
-
-my_func = (add using k,i) ->
-    tmp = tmp + add -- a new local tmp is created 
-    i += tmp
-    k += tmp
-
-my_func(22)
-print i,k -- these have been updated
local tmp = 1213
-local i, k = 100, 50
-local my_func
-my_func = function(add)
-  local tmp = tmp + add
-  i = i + tmp
-  k = k + tmp
-  return k
-end
-my_func(22)
-print(i, k)
- - -

MoonScript API

- -

moonscript Module

- -

Upon installing MoonScript, a moonscript module is made available. The best -use of this module is making your Lua’s require function MoonScript aware.

- - - - - -
moonscriptlua
require "moonscript"
require("moonscript")
- - -

After moonscript is required, Lua’s package loader is updated to search for -.moon files on any subsequent calls to require. The search path for .moon -files is based on the current package.path value in Lua when moonscript is -required. Any search paths in package.path ending in .lua are copied, -rewritten to end in .moon, and then inserted in package.moonpath.

- -

The moonloader is the function that is responsible for searching -package.moonpath for a file available to be included. It is inserted in the -second position of the package.loaders table. This means that a matching .moon file -will be loaded over a matching .lua file that has the same base name.

- -

For more information on Lua’s package.loaders see Lua Reference Manual -— -package.loaders

- -

The moonloader, when finding a valid path to a .moon file, will parse and -compile the file in memory. The code is then turned into a function using the -built in load function, which is run as the module.

- -

Error Rewriting

- -

Runtime errors are given special attention when using the moonloader. -Because we start off as MoonScript, but run code as Lua, errors that happen -during runtime report their line numbers as they are in the compiled file. This -can make debugging particularly difficult.

- -

Consider the following file with a bug:

- - - - - -
moonscriptlua
add_numbers = (x,y) -> x + z
-print add_numbers 10,0
local add_numbers
-add_numbers = function(x, y)
-  return x + z
-end
-print(add_numbers(10, 0))
- - -

The following error is generated:

- -
moon:err.moon:1: attempt to perform arithmetic on global 'z' (a nil value)
-stack traceback:
-    err.moon:1: in function 'add_numbers'
-    err.moon:2: in main chunk
-
- -

Instead of the error being reported on line number 3, where it appears in the -Lua file, it is reported on line 1, where the faulty line originated. The -entire stack trace is rewritten in addition to the error.

- -

Programmatically Compiling

- -

The MoonScript module also contains methods for parsing MoonScript text into an -abstract syntax tree, and compiling an instance of a tree into Lua source code.

- -

Knowledge of this API may be useful for creating tools to aid the generation of -Lua code from MoonScript code.

- -

Here is a quick example of how you would compile a MoonScript string to a Lua -String:

- - - - - -
moonscriptlua
require "moonscript.parse"
-require "moonscript.compile"
-
-import parse, compile from moonscript
-
-moon_code = [[(-> print "hello world")!]]
-
-tree, err = parse.string moon_code
-if not tree
-  error "Parse error: " .. err
-
-lua_code, err, pos = compile.tree tree
-if not lua_code
-  error compile.format_error err, pos, moon_code
-
--- our code is ready
-print lua_code
require("moonscript.parse")
-require("moonscript.compile")
-local parse, compile = moonscript.parse, moonscript.compile
-local moon_code = [[(-> print "hello world")!]]
-local tree, err = parse.string(moon_code)
-if not tree then
-  error("Parse error: " .. err)
-end
-local lua_code, pos
-lua_code, err, pos = compile.tree(tree)
-if not lua_code then
-  error(compile.format_error(err, pos, moon_code))
-end
-print(lua_code)
- - -

Command Line Use

- -

Two tools are installed with MoonScript, moon and moonc.

- -

moonc is for compiling MoonScript code to Lua.
-moon is for running MoonsScript code directly.

- -

moon

- -

moon can be used to run MoonsScript files directly from the command line, -without needing a separate compile step. All MoonsScript files are compiled in -memory as they are run.

- -
$ moon my_script.moon
-
- - -

Any MoonScript files that are required will also be compiled and run -automatically.

- -

When an error occurs during runtime, the stack trace is rewritten to give line -numbers from the original .moon file.

- -

If you want to disable error rewriting, you can pass the --d flag. A full list of flags can be seen by passing the -h or --help -flag.

- -

moonc

- -

moonc is used for transforming MoonsScript files into Lua files. -It takes a list of files, compiles them all, and creates the associated .lua -files in the same directories.

- -
$ moonc my_script1.moon my_script2.moon ...
-
- - -

You can control where the compiled files are put using the -t flag, followed -by a directory.

- -

moonc can also take a directory as an argument, and it will recursively scan -for all MoonScript files and compile them.

- -

moonc can write to standard out by passing the -p flag.

- -

Combined with linotify on linux, the -w flag can be used to watch all files -that match the given search path for changes, and then compile them only when -required.

- -

A full list of flags can be seen by passing the -h or --help flag.

- -

License (MIT)

- -
Copyright (C) 2011 by Leaf Corcoran
-
-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:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-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.
-
- -
- - - - - - - - diff --git a/docs/standard_lib.html b/docs/standard_lib.html deleted file mode 100644 index 4c64f016..00000000 --- a/docs/standard_lib.html +++ /dev/null @@ -1,476 +0,0 @@ - - - - - - MoonScript v0.2.0 - Standard Library - - - - -
-

MoonScript v0.2.0 - Standard Library

-
- - - -
-

The MoonScript installation comes with a small kernel of functions that can be -used to perform various common things.

- -

The entire library is currently contained in a single object. We can bring this -moon object into scope by requiring "moon".

- -
require "moon"
--- `moon.p` is the debug printer
-moon.p { hello: "world" }
-
- - -

If you prefer to just inject all of the functions into the current scope, you -can require "moon.all" instead. The following has the same effect as above:

- -
require "moon.all"
-p { hello: "world" }
-
- - -

All of the functions are compatible with Lua in addition to MoonScript, but -some of them only make sense in the context of MoonScript.

- -

MoonScript Standard Library

- -

This is an overview of all the included functions. -All of the examples assume that the standard library has been included with -require "moon.all".

- -

Printing Functions

- -

p(arg)

- -

Prints a formatted version of an object. Excellent for inspecting the contents -of a table.

- -

Table Functions

- -

run_with_scope(fn, scope, [args...])

- -

Mutates the environment of function fn and runs the function with any extra -arguments in args.... Returns the result of the function.

- -

The environment of the function is set to a new table whose metatable will use -scope to look up values. scope must be a table. If scope does not have an -entry for a value, it will fall back on the original environment.

- -
my_env = {
-  secret_function: -> print "shhh this is secret"
-  say_hi: -> print "hi there!"
-}
-
-say_hi = -> print "I am a closure"
-
-fn = ->
-  secret_function!
-  say_hi!
-
-run_with_scope fn, my_env
-
- - -

Note that any closure values will always take precedence against global name -lookups in the environment. In the example above, the say_hi in the -environment has been shadowed by the local variable say_hi.

- -

defaultbl([tbl,] fn)

- -

Sets the __index of table tbl to use the function fn to generate table -values when a missing key is looked up.

- -

extend(arg1, arg2, [rest...])

- -

Chains together a series of tables by their metatable’s __index property. -Overwrites the metatable of all objects exept for the last with a new table -whose __index is set to the next table.

- -

Returns the first argument.

- -
a = { hello: "world" }
-b = { okay: "sure" }
-
-extend a, b
-
-print a.okay
-
- - -

copy(tbl)

- -

Creates a shallow copy of a table, equivalent to:

- -
copy = (arg) -> {k,v for k,v in pairs self}
-
- - -

Class/Object Functions

- -

is_object(value)

- -

Returns true if value is an instance of a MoonScript class, false otherwise.

- -

type(value)

- -

If value is an instance of a MoonScript class, then return it’s class object. -Otherwise, return the result of calling Lua’s type method.

- -
class MyClass
-  nil
-
-x = MyClass!
-assert type(x) == MyClass
-
- - -

bind_methods(obj)

- -

Takes an instance of an object, returns a proxy to the object whose methods can -be called without providing self as the first argument.

- -
obj = SomeClass!
-
-bound_obj = bind_methods obj
-
--- following have the same effect
-obj\hello!
-bound_obj.hello!
-
- - -

It lazily creates and stores in the proxy table the bound methods when they -are first called.

- -

mixin(obj, class, [args...])

- -

Copies the methods of a class cls into the table obj, then calls the -constructor of the class with the obj as the receiver.

- -

In this example we add the functionality of First to an instance of Second -without ever instancing First.

- -
class First
-  new: (@var) =>
-  show_var: => print "var is:", @var
-
-class Second
-  new: =>
-    mixin self, First, "hi"
-
-a = Second!
-a\show_var!
-
- - -

Be weary of name collisions when mixing in other classes, names will be -overwritten.

- -

mixin_object(obj, other_obj, method_names)

- -

Inserts into obj methods from other_obj whose names are listen in -method_names. The inserted methods are bound methods that will run with -other_obj as the receiver.

- -
class List 
-  add: (item) => print "adding to", self
-  remove: (item) => print "removing from", self
-
-class Encapsulation
-  new: =>
-    @list = List!
-    mixin_object self, @list, {"add", "remove"}
-
-e = Encapsulation!
-e.add "something"
-
- - -

mixin_table(a, b, [names])

- -

Copies the elements of table b into table a. If names is provided, then -only those names are copied.

- -

Misc Functions

- -

fold(items, fn)

- -

Calls function fn repeatedly with the accumulated value and the current value -by iterating over items. The accumulated value is the result of the last call -to fn, or, in the base case, the first value. The current value is the value -being iterated over starting with the second item.

- -

items is a normal array table.

- -

For example, to sum all numbers in a list:

- -
numbers = {4,3,5,6,7,2,3}
-sum = fold numbers, (a,b) -> a + b
-
- - -

Debug Functions

- -

debug.upvalue(fn, key[, value])

- -

Gets or sets the value of an upvalue for a function by name.

- -
- - - - - - - - diff --git a/docs/standard_lib.md b/docs/standard_lib.md index 6b3a1c16..4a7c2dfa 100644 --- a/docs/standard_lib.md +++ b/docs/standard_lib.md @@ -1,8 +1,9 @@ - target: reference/standard_lib - template: reference - title: MoonScript v0.2.1 - Standard Library - short_name: stdlib --- +{ + target: "reference/standard_lib" + template: "reference" + title: "Standard Library" + short_name: "stdlib" +} The MoonScript installation comes with a small kernel of functions that can be used to perform various common things. @@ -10,19 +11,19 @@ used to perform various common things. The entire library is currently contained in a single object. We can bring this `moon` object into scope by requiring `"moon"`. - ```moon - require "moon" - -- `moon.p` is the debug printer - moon.p { hello: "world" } - ``` +```moon +moon = require "moon" +-- `moon.p` is the debug printer +moon.p { hello: "world" } +``` If you prefer to just inject all of the functions into the current scope, you can require `"moon.all"` instead. The following has the same effect as above: - ```moon - require "moon.all" - p { hello: "world" } - ``` +```moon +require "moon.all" +p { hello: "world" } +``` All of the functions are compatible with Lua in addition to MoonScript, but some of them only make sense in the context of MoonScript. @@ -53,20 +54,20 @@ The environment of the function is set to a new table whose metatable will use `scope` to look up values. `scope` must be a table. If `scope` does not have an entry for a value, it will fall back on the original environment. - ```moon - my_env = { - secret_function: -> print "shhh this is secret" - say_hi: -> print "hi there!" - } +```moon +my_env = { + secret_function: -> print "shhh this is secret" + say_hi: -> print "hi there!" +} - say_hi = -> print "I am a closure" +say_hi = -> print "I am a closure" - fn = -> - secret_function! - say_hi! +fn = -> + secret_function! + say_hi! - run_with_scope fn, my_env - ``` +run_with_scope fn, my_env +``` Note that any closure values will always take precedence against global name @@ -81,61 +82,141 @@ values when a missing key is looked up. ### `extend(arg1, arg2, [rest...])` Chains together a series of tables by their metatable's `__index` property. -Overwrites the metatable of all objects exept for the last with a new table +Overwrites the metatable of all objects except for the last with a new table whose `__index` is set to the next table. Returns the first argument. - ```moon - a = { hello: "world" } - b = { okay: "sure" } +```moon +a = { hello: "world" } +b = { okay: "sure" } - extend a, b +extend a, b - print a.okay - ``` +print a.okay +``` ### `copy(tbl)` Creates a shallow copy of a table, equivalent to: - ```moon - copy = (arg) -> {k,v for k,v in pairs self} - ``` +```moon +copy = (arg) -> {k,v for k,v in pairs self} +``` ## Class/Object Functions -### `is_object(value)` -Returns true if `value` is an instance of a MoonScript class, false otherwise. +### `is_class(value)` + +Returns `true` if `value` is a MoonScript class table, `false` otherwise. +Returns `false` for instances, `__base` tables, plain tables, and non-table +values. + +Works by checking that the value is a table with a `__base` field set directly +on it, and that its metatable has a `__call` field (the constructor). Both are +properties unique to MoonScript class tables. + +```moon +class MyClass + +is_class MyClass -- true +is_class MyClass! -- false +is_class MyClass.__base -- false +``` + +### `is_instance(value)` + +Returns `true` if `value` is an instance of a MoonScript class, `false` +otherwise. Returns `false` for class tables, `__base` tables, plain tables, and +non-table values. + +Works by checking that the value is a table whose metatable is a `__base` table +(identified by having a self-referencing `__index`), and that the value itself +is not a `__base` table. + +```moon +class MyClass + +is_instance MyClass! -- true +is_instance MyClass -- false +is_instance MyClass.__base -- false +``` + +### `is_instance_of(value, class)` + +Returns `true` if `value` is an instance of `class` or any of its parent +classes, `false` otherwise. Throws an error if `value` is not a MoonScript +instance. First verifies that `value` is a valid instance using `is_instance`, +then walks the `__parent` chain to check if the instance's class matches or +inherits from `class`. + +```moon +class Parent +class Child extends Parent + +is_instance_of Child!, Child -- true +is_instance_of Child!, Parent -- true (checks parent classes) +is_instance_of Parent!, Child -- false +``` + +To check if a value is a direct instance of a specific class without considering +inheritance, use `type(value) == MyClass` instead. + +### `is_subclass_of(cls, parent)` + +Returns `true` if `cls` is a subclass of `parent`, `false` otherwise. Throws an +error if `cls` is not a MoonScript class. Note that a class is not considered a +subclass of itself. Walks the `__parent` chain starting from `cls` to check if +any ancestor matches `parent`. + +```moon +class Parent +class Child extends Parent + +is_subclass_of Child, Parent -- true +is_subclass_of Parent, Child -- false +is_subclass_of Child, Child -- false +``` ### `type(value)` -If `value` is an instance of a MoonScript class, then return it's class object. -Otherwise, return the result of calling Lua's type method. +Returns a class-aware type for a value. If `value` is an instance of a +MoonScript class, returns its class object. If `value` is a class table, returns +the string `"class"`. Returns the result of calling Lua's built-in `type` for +all other values, including `__base` tables and plain tables. + +```moon +class MyClass + +x = MyClass! +assert type(x) == MyClass +assert type(MyClass) == "class" +assert type(MyClass.__base) == "table" +``` - ```moon - class MyClass - nil +### `is_object(value)` + +Legacy method for testing if a value is an instance. - x = MyClass! - assert type(x) == MyClass - ``` +**Deprecated:** Use `is_instance` or `is_class` instead. `is_object` returns +truthy for instances, classes, and `__base` tables, any table with `__class` +accessible. It cannot distinguish between these cases. ### `bind_methods(obj)` Takes an instance of an object, returns a proxy to the object whose methods can be called without providing self as the first argument. - ```moon - obj = SomeClass! +```moon +obj = SomeClass! - bound_obj = bind_methods obj +bound_obj = bind_methods obj - -- following have the same effect - obj\hello! - bound_obj.hello! - ``` +-- following have the same effect +obj\hello! +bound_obj.hello! +``` It lazily creates and stores in the proxy table the bound methods when they are first called. @@ -148,41 +229,41 @@ constructor of the class with the `obj` as the receiver. In this example we add the functionality of `First` to an instance of `Second` without ever instancing `First`. - ```moon - class First - new: (@var) => - show_var: => print "var is:", @var +```moon +class First + new: (@var) => + show_var: => print "var is:", @var - class Second - new: => - mixin self, First, "hi" +class Second + new: => + mixin self, First, "hi" - a = Second! - a\show_var! - ``` +a = Second! +a\show_var! +``` Be weary of name collisions when mixing in other classes, names will be overwritten. ### `mixin_object(obj, other_obj, method_names)` -Inserts into `obj` methods from `other_obj` whose names are listen in +Inserts into `obj` methods from `other_obj` whose names are listed in `method_names`. The inserted methods are bound methods that will run with `other_obj` as the receiver. - ```moon - class List - add: (item) => print "adding to", self - remove: (item) => print "removing from", self +```moon +class List + add: (item) => print "adding to", self + remove: (item) => print "removing from", self - class Encapsulation - new: => - @list = List! - mixin_object self, @list, {"add", "remove"} +class Encapsulation + new: => + @list = List! + mixin_object self, @list, {"add", "remove"} - e = Encapsulation! - e.add "something" - ``` +e = Encapsulation! +e.add "something" +``` ### `mixin_table(a, b, [names])` @@ -202,13 +283,14 @@ being iterated over starting with the second item. For example, to sum all numbers in a list: - ```moon - numbers = {4,3,5,6,7,2,3} - sum = fold numbers, (a,b) -> a + b - ``` +```moon +numbers = {4,3,5,6,7,2,3} +sum = fold numbers, (a,b) -> a + b +``` ## Debug Functions ### `debug.upvalue(fn, key[, value])` Gets or sets the value of an upvalue for a function by name. + diff --git a/extra/scintillua/README.md b/extra/scintillua/README.md deleted file mode 100644 index 7bb850bf..00000000 --- a/extra/scintillua/README.md +++ /dev/null @@ -1,37 +0,0 @@ - -# MoonScript for [scintillua][1] - -MoonScript syntax file for [SciTE][2] written in Lua for [scintillua][1]. - -## Windows Binary - -Windows users can get a all-included package ready for MoonScript Development: - - - -If you already have a ScITE installation, or are on another platform, follow -the directions below. - -## Installation - -Install SciTE, then [install scintillua][3]. - -Put `moonscript.properties` in in your ScITE installation folder or user -properties folder. - -Copy the entire contents of the `lexers` folder in this repository into your -scintillua `lexers` folder. - -In your `lexers` folder edit `lpeg.properties`, add to the end: - - file.patterns.moonscript=*.moon - lexer.$(file.patterns.moonscript)=lpeg_moonscript - -Optionally, enable the Moon theme, find `lexer.peg.color.theme` in the same -file and change it to: - - lexer.lpeg.color.theme=moon - - [1]: http://code.google.com/p/scintillua/ "scintillua" - [2]: http://www.scintilla.org/SciTE.html "SciTE" - [3]: http://code.google.com/p/scintillua/#Using_with_SciTE \ No newline at end of file diff --git a/extra/scintillua/lexers/moonscript.lua b/extra/scintillua/lexers/moonscript.lua deleted file mode 100644 index 72cb66af..00000000 --- a/extra/scintillua/lexers/moonscript.lua +++ /dev/null @@ -1,122 +0,0 @@ --- Copyright 2006-2011 Mitchell mitchellcaladbolg.net. See LICENSE. --- Moonscript lexer by leaf corcoran - -local l = lexer -local token, word_match = l.token, l.word_match -local P, S, R = l.lpeg.P, l.lpeg.S, l.lpeg.R - -module(...) - --- Whitespace. -local ws = token(l.WHITESPACE, l.space^1) - -local longstring = #('[[' + ('[' * P('=')^0 * '[')) -local longstring = longstring * P(function(input, index) - local level = input:match('^%[(=*)%[', index) - if level then - local _, stop = input:find(']'..level..']', index, true) - return stop and stop + 1 or #input + 1 - end -end) - --- Comments. -local line_comment = '--' * l.nonnewline^0 -local block_comment = '--' * longstring -local comment = token(l.COMMENT, block_comment + line_comment) - --- Strings. -local sq_str = l.delimited_range("'", '\\', true) -local dq_str = l.delimited_range('"', '\\', true) -local string = token(l.STRING, sq_str + dq_str + longstring) - --- Numbers. -local number = token(l.NUMBER, l.float + l.integer) - --- Keywords. -local keyword = token(l.KEYWORD, word_match { - 'return', 'break', 'for', 'while', - 'if', 'else', 'elseif', 'then', 'export', - 'import', 'from', 'with', 'in', 'and', - 'or', 'not', 'class', 'extends', 'super', 'do', - 'using', 'switch', 'when', -}) - -local special = token("special", word_match { "true", "false", "nil" }) - --- Functions. -local builtin = token(l.FUNCTION, word_match({ - "_G","_VERSION","assert","collectgarbage","dofile","error","getfenv","getmetatable","ipairs","load", - "loadfile","loadstring","module","next","pairs","pcall","print","rawequal","rawget","rawset","require", - "select","setfenv","setmetatable","tonumber","tostring","type","unpack","xpcall", - - "coroutine.create","coroutine.resume","coroutine.running","coroutine.status","coroutine.wrap","coroutine.yield", - - "debug.debug","debug.getfenv","debug.gethook","debug.getinfo","debug.getlocal","debug.getmetatable", - "debug.getregistry","debug.getupvalue","debug.setfenv","debug.sethook","debug.setlocal","debug.setmetatable", - "debug.setupvalue","debug.traceback", - - "io.close","io.flush","io.input","io.lines","io.open","io.output","io.popen","io.read","io.stderr","io.stdin", - "io.stdout","io.tmpfile","io.type","io.write", - - "math.abs","math.acos","math.asin","math.atan","math.atan2","math.ceil","math.cos","math.cosh","math.deg", - "math.exp","math.floor","math.fmod","math.frexp","math.huge","math.ldexp","math.log","math.log10","math.max", - "math.min","math.modf","math.pi","math.pow","math.rad","math.random","math.randomseed","math.sin","math.sinh", - "math.sqrt","math.tan","math.tanh", - - "os.clock","os.date","os.difftime","os.execute","os.exit","os.getenv","os.remove","os.rename","os.setlocale", - "os.time","os.tmpname", - - "package.cpath","package.loaded","package.loaders","package.loadlib","package.path","package.preload", - "package.seeall", - - "string.byte","string.char","string.dump","string.find","string.format","string.gmatch","string.gsub", - "string.len","string.lower","string.match","string.rep","string.reverse","string.sub","string.upper", - - "table.concat","table.insert","table.maxn","table.remove","table.sort" -}, "%.")) - --- Identifiers. -local identifier = token(l.IDENTIFIER, l.word) - -local fndef = token("fndef", P"->" + P"=>") -local err = token(l.ERROR, word_match { "function", "end" }) - --- Operators. -local symbol = token("symbol", S("(){}[]")) -local operator = token(l.OPERATOR, '~=' + S('+-*!\\/%^#=<>;:,.')) - --- self ref -local self_var = token("self_ref", "@" * l.word + "self") - -local proper_ident = token("proper_ident", R("AZ") * l.word) - -local tbl_key = token("tbl_key", l.word * ":" + ":" * l.word ) - -_rules = { - { 'whitespace', ws }, - { 'error', err }, - { 'self', self_var }, - { 'special', special }, - { 'keyword', keyword }, - { 'builtin', builtin }, - { 'identifier', proper_ident + tbl_key + identifier }, - { 'comment', comment }, - { 'number', number }, - { 'string', string }, - { 'fndef', fndef }, - { 'symbol', symbol }, - { 'operator', operator }, - { 'any_char', l.any_char }, -} - -local style_special = { fore = l.colors.light_blue } -local style_fndef = { fore = l.colors.green } - -_tokenstyles = { - { 'self_ref', style_special }, - { 'proper_ident', l.style_class }, - { 'fndef', style_fndef }, - { 'symbol', style_fndef }, - { 'special', style_special }, - { 'tbl_key', { fore = l.colors.red } }, -} diff --git a/extra/scintillua/lexers/themes/moon.lua b/extra/scintillua/lexers/themes/moon.lua deleted file mode 100644 index db8d7b26..00000000 --- a/extra/scintillua/lexers/themes/moon.lua +++ /dev/null @@ -1,61 +0,0 @@ --- Copyright 2006-2011 Mitchell mitchellcaladbolg.net. See LICENSE. --- moon lexer theme for Scintillua. - -module('lexer', package.seeall) - -colors = { - green = color('9F', 'FF', '98'), -- - blue = color('94', '95', 'FF'), -- - light_blue = color('98', 'D9', 'FF'), -- - red = color('FF', '98', '98'), -- - bright_red = color("F9", "26", "32"), -- - yellow = color('FF', 'E8', '98'), -- - teal = color('4D', '99', '99'), - white = color('FF', 'FF', 'FF'), -- - black = color('2E', '2E', '2E'), -- - grey = color('92', '92', '92'), -- - purple = color('CB', '98', 'FF'), -- - orange = color('FF', '92', '00'), -- - pink = color("ED", "4E", "78"), -- -} - -style_nothing = style { } -style_char = style { fore = colors.red, bold = true } -style_class = style { fore = colors.light_blue, bold = true } -style_comment = style { fore = colors.grey, } -style_constant = style { fore = colors.teal, bold = true } -style_definition = style { fore = colors.red, bold = true } -style_error = style { fore = colors.white, back = colors.bright_red, bold = true} -style_function = style { fore = colors.orange, bold = true } -style_keyword = style { fore = colors.purple, bold = true } -style_number = style { fore = colors.blue } -style_operator = style { fore = colors.red, bold = true } -style_string = style { fore = colors.yellow, bold = true } -style_preproc = style { fore = colors.light_blue } -style_tag = style { fore = colors.teal, bold = true } -style_type = style { fore = colors.green } -style_variable = style { fore = colors.white, italic = true } -style_embedded = style_tag..{ back = color('44', '44', '44') } -style_identifier = style_nothing - --- Default styles. -local font_face = '!Bitstream Vera Sans Mono' -local font_size = 12 -if WIN32 then - font_face = not GTK and 'Courier New' or '!Courier New' -elseif OSX then - font_face = '!Monaco' - font_size = 12 -end -style_default = style{ - font = font_face, - size = font_size, - fore = colors.white, - back = colors.black -} -style_line_number = style { fore = colors.black, back = colors.grey } -style_bracelight = style { fore = color('66', '99', 'FF'), bold = true } -style_bracebad = style { fore = color('FF', '66', '99'), bold = true } -style_controlchar = style_nothing -style_indentguide = style { fore = colors.grey, back = colors.white } -style_calltip = style { fore = colors.white, back = color('44', '44', '44') } diff --git a/extra/scintillua/moonscript.properties b/extra/scintillua/moonscript.properties deleted file mode 100644 index feff9333..00000000 --- a/extra/scintillua/moonscript.properties +++ /dev/null @@ -1,11 +0,0 @@ - -file.patterns.moon=*.moon -shbang.moon=moon -filter.moon=MoonScript (moon)|$(file.patterns.moon)| - -command.compile.*.moon=moonc "$(FileNameExt)" -command.go.*.moon=moon "$(FileNameExt)" - -tabsize=2 -indent.size=2 -use.tabs=0 \ No newline at end of file diff --git a/gen_rockspec.sh b/gen_rockspec.sh new file mode 100755 index 00000000..cf20ce98 --- /dev/null +++ b/gen_rockspec.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +for file in $(find moonscript moon | grep 'lua$'); do + MODULE=$(echo $file | sed -e 's/\.lua$//' -e 's/\//./g' -e 's/\.init$//') + echo "[\"$MODULE\"] = \"$file\"," +done + +echo '["moonscript.parse.native"] = { sources = {"moonscript/parse/native.c"} },' diff --git a/moon/all.lua b/moon/all.lua index 3857b0c0..945576d1 100644 --- a/moon/all.lua +++ b/moon/all.lua @@ -1,3 +1,5 @@ -moon = moon or { } -moon.inject = true -require("moon.init") +local moon = require("moon") +for k, v in pairs(moon) do + _G[k] = v +end +return moon diff --git a/moon/all.moon b/moon/all.moon index 86b33547..cb773c70 100644 --- a/moon/all.moon +++ b/moon/all.moon @@ -1,8 +1,6 @@ - --- insert all moon library functions into requiring scope - -export moon -moon = moon or {} -moon.inject = true -require "moon.init" +-- install moon into global scope +moon = require "moon" +for k,v in pairs moon + _G[k] = v +moon diff --git a/moon/init.lua b/moon/init.lua index dbd9eb30..52c17dfb 100644 --- a/moon/init.lua +++ b/moon/init.lua @@ -1,23 +1,71 @@ -if not moon or not moon.inject then - module("moon", package.seeall) -end -local util = require("moonscript.util") local lua = { debug = debug, type = type } -dump = util.dump -p = function(...) - return print(dump(...)) +local getfenv, setfenv, dump +do + local _obj_0 = require("moonscript.util") + getfenv, setfenv, dump = _obj_0.getfenv, _obj_0.setfenv, _obj_0.dump +end +local p, is_object, is_class, is_instance, is_instance_of, is_subclass_of, type, debug, run_with_scope, bind_methods, defaultbl, extend, copy, mixin, mixin_object, mixin_table, fold +p = function(o, ...) + print(dump(o)) + if select("#", ...) > 0 then + return p(...) + end end is_object = function(value) return lua.type(value) == "table" and value.__class end +is_class = function(value) + if lua.type(value) == "table" and rawget(value, "__base") ~= nil then + local mt = getmetatable(value) + return mt and rawget(mt, "__call") ~= nil + end + return false +end +is_instance = function(value) + if lua.type(value) == "table" then + local mt = getmetatable(value) + return mt and rawget(mt, "__index") == mt and rawget(value, "__index") ~= value + end + return false +end +is_instance_of = function(value, cls) + if not (is_instance(value)) then + error("is_instance_of: expected instance, got " .. tostring(lua.type(value))) + end + local mt = getmetatable(value) + local check = rawget(mt, "__class") + while check do + if check == cls then + return true + end + check = check.__parent + end + return false +end +is_subclass_of = function(cls, parent) + if not (is_class(cls)) then + error("is_subclass_of: expected class, got " .. tostring(lua.type(cls))) + end + local check = cls.__parent + while check do + if check == parent then + return true + end + check = check.__parent + end + return false +end type = function(value) local base_type = lua.type(value) if base_type == "table" then + if is_class(value) then + return "class" + end local cls = value.__class - if cls then + if cls and rawget(value, "__class") == nil then return cls end end @@ -110,17 +158,14 @@ extend = function(...) return tbls[1] end copy = function(self) - return (function() - local _tbl_0 = { } - for key, val in pairs(self) do - _tbl_0[key] = val - end - return _tbl_0 - end)() + local _tbl_0 = { } + for key, val in pairs(self) do + _tbl_0[key] = val + end + return _tbl_0 end mixin = function(self, cls, ...) - local meta = getmetatable(cls) - for key, val in pairs(meta.__index) do + for key, val in pairs(cls.__base) do if not key:match("^__") then self[key] = val end @@ -128,9 +173,8 @@ mixin = function(self, cls, ...) return cls.__init(self, ...) end mixin_object = function(self, object, methods) - local _list_0 = methods - for _index_0 = 1, #_list_0 do - local name = _list_0[_index_0] + for _index_0 = 1, #methods do + local name = methods[_index_0] self[name] = function(parent, ...) return object[name](object, ...) end @@ -138,9 +182,8 @@ mixin_object = function(self, object, methods) end mixin_table = function(self, tbl, keys) if keys then - local _list_0 = keys - for _index_0 = 1, #_list_0 do - local key = _list_0[_index_0] + for _index_0 = 1, #keys do + local key = keys[_index_0] self[key] = tbl[key] end else @@ -154,10 +197,30 @@ fold = function(items, fn) if len > 1 then local accum = fn(items[1], items[2]) for i = 3, len do - accum = fn(acum, items[i]) + accum = fn(accum, items[i]) end return accum else return items[1] end end +return { + dump = dump, + p = p, + is_object = is_object, + is_class = is_class, + is_instance = is_instance, + is_instance_of = is_instance_of, + is_subclass_of = is_subclass_of, + type = type, + debug = debug, + run_with_scope = run_with_scope, + bind_methods = bind_methods, + defaultbl = defaultbl, + extend = extend, + copy = copy, + mixin = mixin, + mixin_object = mixin_object, + mixin_table = mixin_table, + fold = fold +} diff --git a/moon/init.moon b/moon/init.moon index 33ee125e..140194a2 100644 --- a/moon/init.moon +++ b/moon/init.moon @@ -1,26 +1,56 @@ -if not moon or not moon.inject - module "moon", package.seeall - -util = require "moonscript.util" - lua = { :debug, :type } +import getfenv, setfenv, dump from require "moonscript.util" -export * +local * -dump = util.dump +p = (o, ...) -> + print dump o + if select("#", ...) > 0 + p ... -p = (...) -> - print dump ... - -is_object = (value) -> -- is a moonscript object +is_object = (value) -> -- deprecated: use is_instance or is_class instead lua.type(value) == "table" and value.__class +is_class = (value) -> + if lua.type(value) == "table" and rawget(value, "__base") != nil + mt = getmetatable value + return mt and rawget(mt, "__call") != nil + false + +is_instance = (value) -> + if lua.type(value) == "table" + mt = getmetatable value + return mt and rawget(mt, "__index") == mt and rawget(value, "__index") != value + false + +is_instance_of = (value, cls) -> + error "is_instance_of: expected instance, got #{lua.type value}" unless is_instance value + mt = getmetatable value + check = rawget mt, "__class" + while check + if check == cls + return true + check = check.__parent + false + +is_subclass_of = (cls, parent) -> + error "is_subclass_of: expected class, got #{lua.type cls}" unless is_class cls + check = cls.__parent + while check + if check == parent + return true + check = check.__parent + false + type = (value) -> -- class aware type base_type = lua.type value if base_type == "table" + if is_class value + return "class" cls = value.__class - return cls if cls + if cls and rawget(value, "__class") == nil + return cls base_type debug = setmetatable { @@ -104,8 +134,7 @@ copy = => -- mixin class properties into self, call new mixin = (cls, ...) => - meta = getmetatable cls - for key, val in pairs meta.__index + for key, val in pairs cls.__base self[key] = val if not key\match"^__" cls.__init self, ... @@ -129,8 +158,12 @@ fold = (items, fn)-> if len > 1 accum = fn items[1], items[2] for i=3,len - accum = fn acum, items[i] + accum = fn accum, items[i] accum else items[1] +{ + :dump, :p, :is_object, :is_class, :is_instance, :is_instance_of, :is_subclass_of, :type, :debug, :run_with_scope, :bind_methods, + :defaultbl, :extend, :copy, :mixin, :mixin_object, :mixin_table, :fold +} diff --git a/moonscript-dev-1.rockspec b/moonscript-dev-1.rockspec index f1db90b2..3571adb8 100644 --- a/moonscript-dev-1.rockspec +++ b/moonscript-dev-1.rockspec @@ -7,6 +7,7 @@ source = { description = { summary = "A programmer friendly language that compiles to Lua", + detailed = "A programmer friendly language that compiles to Lua", homepage = "http://moonscript.org", maintainer = "Leaf Corcoran ", license = "MIT" @@ -14,29 +15,48 @@ description = { dependencies = { "lua >= 5.1", - "lpeg >= 0.10", - "alt-getopt >= 0.7", + "lpeg >= 0.10, ~= 0.11", + "argparse >= 0.7", "luafilesystem >= 1.5" } build = { type = "builtin", modules = { + ["moon"] = "moon/init.lua", + ["moon.all"] = "moon/all.lua", ["moonscript"] = "moonscript/init.lua", + ["moonscript.base"] = "moonscript/base.lua", + ["moonscript.cmd.coverage"] = "moonscript/cmd/coverage.lua", + ["moonscript.cmd.lint"] = "moonscript/cmd/lint.lua", + ["moonscript.cmd.moonc"] = "moonscript/cmd/moonc.lua", + ["moonscript.cmd.watchers"] = "moonscript/cmd/watchers.lua", ["moonscript.compile"] = "moonscript/compile.lua", ["moonscript.compile.statement"] = "moonscript/compile/statement.lua", ["moonscript.compile.value"] = "moonscript/compile/value.lua", - ["moonscript.compile.format"] = "moonscript/compile/format.lua", + ["moonscript.data"] = "moonscript/data.lua", + ["moonscript.dump"] = "moonscript/dump.lua", + ["moonscript.errors"] = "moonscript/errors.lua", + ["moonscript.line_tables"] = "moonscript/line_tables.lua", + ["moonscript.parse"] = "moonscript/parse.lua", + ["moonscript.parse.errors"] = "moonscript/parse/errors.lua", + ["moonscript.parse.grammar"] = "moonscript/parse/grammar.lua", + ["moonscript.parse.native"] = { sources = {"moonscript/parse/native.c"} }, + ["moonscript.parse.slow"] = "moonscript/parse/slow.lua", + ["moonscript.parse.tree"] = "moonscript/parse/tree.lua", ["moonscript.transform"] = "moonscript/transform.lua", + ["moonscript.transform.accumulator"] = "moonscript/transform/accumulator.lua", + ["moonscript.transform.class"] = "moonscript/transform/class.lua", + ["moonscript.transform.comprehension"] = "moonscript/transform/comprehension.lua", + ["moonscript.transform.destructure"] = "moonscript/transform/destructure.lua", + ["moonscript.transform.names"] = "moonscript/transform/names.lua", + ["moonscript.transform.statement"] = "moonscript/transform/statement.lua", + ["moonscript.transform.statements"] = "moonscript/transform/statements.lua", + ["moonscript.transform.transformer"] = "moonscript/transform/transformer.lua", + ["moonscript.transform.value"] = "moonscript/transform/value.lua", ["moonscript.types"] = "moonscript/types.lua", - ["moonscript.parse"] = "moonscript/parse.lua", - ["moonscript.dump"] = "moonscript/dump.lua", - ["moonscript.data"] = "moonscript/data.lua", ["moonscript.util"] = "moonscript/util.lua", - ["moonscript.errors"] = "moonscript/errors.lua", ["moonscript.version"] = "moonscript/version.lua", - ["moon"] = "moon/init.lua", - ["moon.all"] = "moon/all.lua", }, install = { bin = { "bin/moon", "bin/moonc" } diff --git a/moonscript.lua b/moonscript.lua deleted file mode 100644 index e3a64e5a..00000000 --- a/moonscript.lua +++ /dev/null @@ -1 +0,0 @@ -require "moonscript.init" diff --git a/moonscript/base.lua b/moonscript/base.lua new file mode 100644 index 00000000..6bc0d49e --- /dev/null +++ b/moonscript/base.lua @@ -0,0 +1,153 @@ +local compile = require("moonscript.compile") +local parse = require("moonscript.parse") +local concat, insert, remove +do + local _obj_0 = table + concat, insert, remove = _obj_0.concat, _obj_0.insert, _obj_0.remove +end +local split, dump, get_options, unpack +do + local _obj_0 = require("moonscript.util") + split, dump, get_options, unpack = _obj_0.split, _obj_0.dump, _obj_0.get_options, _obj_0.unpack +end +local lua = { + loadstring = loadstring, + load = load +} +local dirsep, line_tables, create_moonpath, to_lua, moon_loader, loadstring, loadfile, dofile, insert_loader, remove_loader +dirsep = "/" +line_tables = require("moonscript.line_tables") +create_moonpath = function(package_path) + local moonpaths + do + local _accum_0 = { } + local _len_0 = 1 + local _list_0 = split(package_path, ";") + for _index_0 = 1, #_list_0 do + local _continue_0 = false + repeat + local path = _list_0[_index_0] + local prefix = path:match("^(.-)%.lua$") + if not (prefix) then + _continue_0 = true + break + end + local _value_0 = prefix .. ".moon" + _accum_0[_len_0] = _value_0 + _len_0 = _len_0 + 1 + _continue_0 = true + until true + if not _continue_0 then + break + end + end + moonpaths = _accum_0 + end + return concat(moonpaths, ";") +end +to_lua = function(text, options) + if options == nil then + options = { } + end + if "string" ~= type(text) then + local t = type(text) + return nil, "expecting string (got " .. t .. ")" + end + local tree, err = parse.string(text) + if not tree then + return nil, err + end + local code, ltable, pos = compile.tree(tree, options) + if not code then + return nil, compile.format_error(ltable, pos, text) + end + return code, ltable +end +moon_loader = function(name) + local name_path = name:gsub("%.", dirsep) + local file, file_path + for path in package.moonpath:gmatch("[^;]+") do + file_path = path:gsub("?", name_path) + file = io.open(file_path) + if file then + break + end + end + if file then + local text = file:read("*a") + file:close() + local res, err = loadstring(text, "@" .. tostring(file_path)) + if not res then + error(file_path .. ": " .. err) + end + return res + end + return nil, "Could not find moon file" +end +loadstring = function(...) + local options, str, chunk_name, mode, env = get_options(...) + chunk_name = chunk_name or "=(moonscript.loadstring)" + local code, ltable_or_err = to_lua(str, options) + if not (code) then + return nil, ltable_or_err + end + if chunk_name then + line_tables[chunk_name] = ltable_or_err + end + return (lua.loadstring or lua.load)(code, chunk_name, unpack({ + mode, + env + })) +end +loadfile = function(fname, ...) + local file, err = io.open(fname) + if not (file) then + return nil, err + end + local text = assert(file:read("*a")) + file:close() + return loadstring(text, "@" .. tostring(fname), ...) +end +dofile = function(...) + local f = assert(loadfile(...)) + return f() +end +insert_loader = function(pos) + if pos == nil then + pos = 2 + end + if not package.moonpath then + package.moonpath = create_moonpath(package.path) + end + local loaders = package.loaders or package.searchers + for _index_0 = 1, #loaders do + local loader = loaders[_index_0] + if loader == moon_loader then + return false + end + end + insert(loaders, pos, moon_loader) + return true +end +remove_loader = function() + local loaders = package.loaders or package.searchers + for i, loader in ipairs(loaders) do + if loader == moon_loader then + remove(loaders, i) + return true + end + end + return false +end +return { + _NAME = "moonscript", + insert_loader = insert_loader, + remove_loader = remove_loader, + to_lua = to_lua, + moon_loader = moon_loader, + dirsep = dirsep, + dofile = dofile, + loadfile = loadfile, + loadstring = loadstring, + create_moonpath = create_moonpath +} diff --git a/moonscript/base.moon b/moonscript/base.moon new file mode 100644 index 00000000..3a4502d4 --- /dev/null +++ b/moonscript/base.moon @@ -0,0 +1,108 @@ +compile = require "moonscript.compile" +parse = require "moonscript.parse" + +import concat, insert, remove from table +import split, dump, get_options, unpack from require "moonscript.util" + +lua = :loadstring, :load + +local * + +dirsep = "/" +line_tables = require "moonscript.line_tables" + +-- create moon path package from lua package path +create_moonpath = (package_path) -> + moonpaths = for path in *split package_path, ";" + prefix = path\match "^(.-)%.lua$" + continue unless prefix + prefix .. ".moon" + concat moonpaths, ";" + +to_lua = (text, options={}) -> + if "string" != type text + t = type text + return nil, "expecting string (got ".. t ..")" + + tree, err = parse.string text + if not tree + return nil, err + + code, ltable, pos = compile.tree tree, options + if not code + return nil, compile.format_error(ltable, pos, text) + + code, ltable + +moon_loader = (name) -> + name_path = name\gsub "%.", dirsep + + local file, file_path + for path in package.moonpath\gmatch "[^;]+" + file_path = path\gsub "?", name_path + file = io.open file_path + break if file + + if file + text = file\read "*a" + file\close! + res, err = loadstring text, "@#{file_path}" + if not res + error file_path .. ": " .. err + + return res + + return nil, "Could not find moon file" + + +loadstring = (...) -> + options, str, chunk_name, mode, env = get_options ... + chunk_name or= "=(moonscript.loadstring)" + + code, ltable_or_err = to_lua str, options + unless code + return nil, ltable_or_err + + line_tables[chunk_name] = ltable_or_err if chunk_name + -- the unpack prevents us from passing nil + (lua.loadstring or lua.load) code, chunk_name, unpack { mode, env } + +loadfile = (fname, ...) -> + file, err = io.open fname + return nil, err unless file + text = assert file\read "*a" + file\close! + loadstring text, "@#{fname}", ... + +-- throws errros +dofile = (...) -> + f = assert loadfile ... + f! + +insert_loader = (pos=2) -> + if not package.moonpath + package.moonpath = create_moonpath package.path + + loaders = package.loaders or package.searchers + for loader in *loaders + return false if loader == moon_loader + + insert loaders, pos, moon_loader + true + +remove_loader = -> + loaders = package.loaders or package.searchers + + for i, loader in ipairs loaders + if loader == moon_loader + remove loaders, i + return true + + false + +{ + _NAME: "moonscript" + :insert_loader, :remove_loader, :to_lua, :moon_loader, :dirsep, + :dofile, :loadfile, :loadstring, :create_moonpath +} + diff --git a/moonscript/cmd/coverage.lua b/moonscript/cmd/coverage.lua new file mode 100644 index 00000000..43456301 --- /dev/null +++ b/moonscript/cmd/coverage.lua @@ -0,0 +1,146 @@ +local log +log = function(str) + if str == nil then + str = "" + end + return io.stderr:write(str .. "\n") +end +local create_counter +create_counter = function() + return setmetatable({ }, { + __index = function(self, name) + do + local tbl = setmetatable({ }, { + __index = function(self) + return 0 + end + }) + self[name] = tbl + return tbl + end + end + }) +end +local position_to_lines +position_to_lines = function(file_content, positions) + local lines = { } + local current_pos = 0 + local line_no = 1 + for char in file_content:gmatch(".") do + do + local count = rawget(positions, current_pos) + if count then + lines[line_no] = count + end + end + if char == "\n" then + line_no = line_no + 1 + end + current_pos = current_pos + 1 + end + return lines +end +local format_file +format_file = function(fname, positions) + fname = fname:gsub("^@", "") + local file = assert(io.open(fname)) + local content = file:read("*a") + file:close() + local lines = position_to_lines(content, positions) + log("------| @" .. tostring(fname)) + local line_no = 1 + for line in (content .. "\n"):gmatch("(.-)\n") do + local foramtted_no = ("% 5d"):format(line_no) + local sym = lines[line_no] and "*" or " " + log(tostring(sym) .. tostring(foramtted_no) .. "| " .. tostring(line)) + line_no = line_no + 1 + end + return log() +end +local CodeCoverage +do + local _class_0 + local _base_0 = { + reset = function(self) + self.line_counts = create_counter() + end, + start = function(self) + return debug.sethook((function() + local _base_1 = self + local _fn_0 = _base_1.process_line + return function(...) + return _fn_0(_base_1, ...) + end + end)(), "l") + end, + stop = function(self) + return debug.sethook() + end, + print_results = function(self) + return self:format_results() + end, + process_line = function(self, _, line_no) + local debug_data = debug.getinfo(2, "S") + local source = debug_data.source + local _update_0, _update_1 = source, line_no + self.line_counts[_update_0][_update_1] = self.line_counts[_update_0][_update_1] + 1 + end, + format_results = function(self) + local line_table = require("moonscript.line_tables") + local positions = create_counter() + for file, lines in pairs(self.line_counts) do + local _continue_0 = false + repeat + local file_table = line_table[file] + if not (file_table) then + _continue_0 = true + break + end + for line, count in pairs(lines) do + local _continue_1 = false + repeat + local position = file_table[line] + if not (position) then + _continue_1 = true + break + end + local _update_0, _update_1 = file, position + positions[_update_0][_update_1] = positions[_update_0][_update_1] + count + _continue_1 = true + until true + if not _continue_1 then + break + end + end + _continue_0 = true + until true + if not _continue_0 then + break + end + end + for file, ps in pairs(positions) do + format_file(file, ps) + end + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function(self) + return self:reset() + end, + __base = _base_0, + __name = "CodeCoverage" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + CodeCoverage = _class_0 +end +return { + CodeCoverage = CodeCoverage +} diff --git a/moonscript/cmd/coverage.moon b/moonscript/cmd/coverage.moon new file mode 100644 index 00000000..281c12aa --- /dev/null +++ b/moonscript/cmd/coverage.moon @@ -0,0 +1,82 @@ + +log = (str="") -> + io.stderr\write str .. "\n" + +create_counter = -> + setmetatable {}, __index: (name) => + with tbl = setmetatable {}, __index: => 0 + @[name] = tbl + +position_to_lines = (file_content, positions) -> + lines = {} + current_pos = 0 + line_no = 1 + for char in file_content\gmatch "." + if count = rawget positions, current_pos + lines[line_no] = count + + if char == "\n" + line_no += 1 + + current_pos += 1 + + lines + +format_file = (fname, positions) -> + -- sources have @ in front of file names + fname = fname\gsub "^@", "" + + file = assert io.open fname + content = file\read "*a" + file\close! + + lines = position_to_lines content, positions + log "------| @#{fname}" + line_no = 1 + for line in (content .. "\n")\gmatch "(.-)\n" + foramtted_no = "% 5d"\format(line_no) + sym = lines[line_no] and "*" or " " + log "#{sym}#{foramtted_no}| #{line}" + line_no += 1 + + log! + + +class CodeCoverage + new: => + @reset! + + reset: => + @line_counts = create_counter! + + start: => + debug.sethook @\process_line, "l" + + stop: => + debug.sethook! + + print_results: => + @format_results! + + process_line: (_, line_no) => + debug_data = debug.getinfo 2, "S" + source = debug_data.source + @line_counts[source][line_no] += 1 + + format_results: => + line_table = require "moonscript.line_tables" + positions = create_counter! + + for file, lines in pairs @line_counts + file_table = line_table[file] + continue unless file_table + + for line, count in pairs lines + position = file_table[line] + continue unless position + positions[file][position] += count + + for file, ps in pairs positions + format_file file, ps + +{ :CodeCoverage } diff --git a/moonscript/cmd/lint.lua b/moonscript/cmd/lint.lua new file mode 100644 index 00000000..21aedcf8 --- /dev/null +++ b/moonscript/cmd/lint.lua @@ -0,0 +1,426 @@ +local insert +insert = table.insert +local Set +Set = require("moonscript.data").Set +local Block +Block = require("moonscript.compile").Block +local ntype +ntype = require("moonscript.types").ntype +local mtype +mtype = require("moonscript.util").mtype +local default_whitelist = Set({ + '_G', + '_VERSION', + 'assert', + 'bit32', + 'collectgarbage', + 'coroutine', + 'debug', + 'dofile', + 'error', + 'getfenv', + 'getmetatable', + 'io', + 'ipairs', + 'load', + 'loadfile', + 'loadstring', + 'math', + 'module', + 'next', + 'os', + 'package', + 'pairs', + 'pcall', + 'print', + 'rawequal', + 'rawget', + 'rawlen', + 'rawset', + 'require', + 'select', + 'setfenv', + 'setmetatable', + 'string', + 'table', + 'tonumber', + 'tostring', + 'type', + 'unpack', + 'xpcall', + "nil", + "true", + "false" +}) +local LINT_STAGES = { + "global_access", + "unused", + "constant_assign", + "import_overwrite" +} +local LinterBlock +do + local _class_0 + local _parent_0 = Block + local _base_0 = { + lint_report = function(self, stage, msg, pos) + local root = self.root + if root.lint_stages and not root.lint_stages[stage] then + return + end + return insert(root.lint_errors, { + msg, + pos, + stage + }) + end, + lint_mark_used = function(self, name) + if self.lint_unused_names and self.lint_unused_names[name] then + self.lint_unused_names[name] = false + return + end + if self.parent then + return self.parent:lint_mark_used(name) + end + end, + lint_check_unused = function(self) + if not (self.lint_unused_names and next(self.lint_unused_names)) then + return + end + local names_by_position = { } + for name, pos in pairs(self.lint_unused_names) do + local _continue_0 = false + repeat + if not (pos) then + _continue_0 = true + break + end + local _update_0 = pos + names_by_position[_update_0] = names_by_position[_update_0] or { } + insert(names_by_position[pos], name) + _continue_0 = true + until true + if not _continue_0 then + break + end + end + local tuples + do + local _accum_0 = { } + local _len_0 = 1 + for pos, names in pairs(names_by_position) do + _accum_0[_len_0] = { + pos, + names + } + _len_0 = _len_0 + 1 + end + tuples = _accum_0 + end + table.sort(tuples, function(a, b) + return a[1] < b[1] + end) + for _index_0 = 1, #tuples do + local _des_0 = tuples[_index_0] + local pos, names + pos, names = _des_0[1], _des_0[2] + self.root:lint_report("unused", "assigned but unused " .. tostring(table.concat((function() + local _accum_0 = { } + local _len_0 = 1 + for _index_1 = 1, #names do + local n = names[_index_1] + _accum_0[_len_0] = "`" .. tostring(n) .. "`" + _len_0 = _len_0 + 1 + end + return _accum_0 + end)(), ", ")), pos) + end + end, + render = function(self, ...) + self:lint_check_unused() + return _class_0.__parent.__base.render(self, ...) + end, + block = function(self, ...) + do + local child = _class_0.__parent.__base.block(self, ...) + child.block = self.block + child.render = self.render + child.lint_check_unused = self.lint_check_unused + child.lint_mark_used = self.lint_mark_used + child.value_compilers = self.value_compilers + child.statement_compilers = self.statement_compilers + child.lint_wrap_transform = self.lint_wrap_transform + self:lint_wrap_transform(child) + return child + end + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, whitelist_globals, stages, ...) + if whitelist_globals == nil then + whitelist_globals = default_whitelist + end + _class_0.__parent.__init(self, ...) + self.lint_errors = { } + if stages then + self.lint_stages = Set(stages) + end + self.lint_wrap_transform = function(self, block) + local inner = block.transform.statement + local checked_imports = setmetatable({ }, { + __mode = "k" + }) + block.transform.statement = function(node, ...) + local import_node = node + while ntype(import_node) == "transform" do + import_node = import_node[2] + end + if ntype(import_node) == "import" and not checked_imports[import_node] then + checked_imports[import_node] = true + local _list_0 = import_node[2] + for _index_0 = 1, #_list_0 do + local name = _list_0[_index_0] + if ntype(name) == "colon" then + name = name[2] + end + local binding = block:binding_value(name) + if not (type(binding) == "table" and binding.const) then + if binding then + block.root:lint_report("import_overwrite", "import overwrites existing binding `" .. tostring(name) .. "`", import_node[-1]) + end + end + end + end + return inner(node, ...) + end + return block + end + self:lint_wrap_transform(self) + local vc = self.value_compilers + self.value_compilers = setmetatable({ + ref = function(block, val) + local name = val[2] + if not (block:has_name(name) or whitelist_globals[name] or name:match("%.")) then + self:lint_report("global_access", "accessing global `" .. tostring(name) .. "`", val[-1]) + end + block:lint_mark_used(name) + return vc.ref(block, val) + end + }, { + __index = vc + }) + local sc = self.statement_compilers + self.statement_compilers = setmetatable({ + assign = function(block, node) + local names = node[2] + for _index_0 = 1, #names do + local _continue_0 = false + repeat + local name = names[_index_0] + if type(name) == "table" and name[1] == "temp_name" then + _continue_0 = true + break + end + local const_name + if type(name) == "string" then + const_name = name + elseif name[1] == "ref" then + const_name = name[2] + end + if const_name then + local binding = block:binding_value(const_name) + if type(binding) == "table" and binding.const then + self:lint_report("constant_assign", "assigning to constant `" .. tostring(const_name) .. "`", type(name) == "table" and name[-1] or node[-1] or block.root.last_pos) + end + end + local real_name, is_local = block:extract_assign_name(name) + if not (is_local or real_name and not block:has_name(real_name, true)) then + _continue_0 = true + break + end + if real_name == "_" then + _continue_0 = true + break + end + block.lint_unused_names = block.lint_unused_names or { } + block.lint_unused_names[real_name] = node[-1] or 0 + _continue_0 = true + until true + if not _continue_0 then + break + end + end + return sc.assign(block, node) + end + }, { + __index = sc + }) + end, + __base = _base_0, + __name = "LinterBlock", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + LinterBlock = _class_0 +end +local format_lint_compact +format_lint_compact = function(errors, code, header) + if not (next(errors)) then + return nil + end + local pos_to_line_col + pos_to_line_col = require("moonscript.util").pos_to_line_col + local formatted + do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #errors do + local _des_0 = errors[_index_0] + local msg, pos, stage + msg, pos, stage = _des_0[1], _des_0[2], _des_0[3] + local location + if pos then + local line, col = pos_to_line_col(code, pos) + location = tostring(header) .. ":" .. tostring(line) .. ":" .. tostring(col) + else + location = header + end + local stage_suffix = stage and " [" .. tostring(stage) .. "]" or "" + local _value_0 = tostring(location) .. ": " .. tostring(msg) .. tostring(stage_suffix) + _accum_0[_len_0] = _value_0 + _len_0 = _len_0 + 1 + end + formatted = _accum_0 + end + return table.concat(formatted, "\n") +end +local format_lint +format_lint = function(errors, code, header) + if not (next(errors)) then + return + end + local pos_to_line, get_line + do + local _obj_0 = require("moonscript.util") + pos_to_line, get_line = _obj_0.pos_to_line, _obj_0.get_line + end + local formatted + do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #errors do + local _des_0 = errors[_index_0] + local msg, pos + msg, pos = _des_0[1], _des_0[2] + if pos then + local line = pos_to_line(code, pos) + msg = "line " .. tostring(line) .. ": " .. tostring(msg) + local line_text = "> " .. get_line(code, line) + local sep_len = math.max(#msg, #line_text) + _accum_0[_len_0] = table.concat({ + msg, + ("="):rep(sep_len), + line_text + }, "\n") + else + _accum_0[_len_0] = msg + end + _len_0 = _len_0 + 1 + end + formatted = _accum_0 + end + if header then + table.insert(formatted, 1, header) + end + return table.concat(formatted, "\n\n") +end +local whitelist_for_file +do + local lint_config + whitelist_for_file = function(fname) + if not (lint_config) then + lint_config = { } + pcall(function() + lint_config = require("lint_config") + end) + end + if not (lint_config.whitelist_globals) then + return default_whitelist + end + local final_list = { } + for pattern, list in pairs(lint_config.whitelist_globals) do + if fname:match(pattern) then + for _index_0 = 1, #list do + local item = list[_index_0] + insert(final_list, item) + end + end + end + return setmetatable(Set(final_list), { + __index = default_whitelist + }) + end +end +local LINT_FORMATS = { + "default", + "compact" +} +local formatters = { + default = format_lint, + compact = format_lint_compact +} +local lint_code +lint_code = function(code, name, whitelist_globals, opts) + if name == nil then + name = "string input" + end + local parse = require("moonscript.parse") + local tree, err = parse.string(code) + if not (tree) then + return nil, err + end + local scope = LinterBlock(whitelist_globals, opts and opts.stages) + scope:stms(tree) + scope:lint_check_unused() + local formatter = formatters[opts and opts.format or "default"] + if not (formatter) then + error("unknown lint format: " .. tostring(opts.format)) + end + return formatter(scope.lint_errors, code, name) +end +local lint_file +lint_file = function(fname, opts) + local f, err = io.open(fname) + if not (f) then + return nil, err + end + return lint_code(f:read("*a"), fname, whitelist_for_file(fname), opts) +end +return { + lint_code = lint_code, + lint_file = lint_file, + LINT_STAGES = LINT_STAGES, + LINT_FORMATS = LINT_FORMATS +} diff --git a/moonscript/cmd/lint.moon b/moonscript/cmd/lint.moon new file mode 100644 index 00000000..b767e2c4 --- /dev/null +++ b/moonscript/cmd/lint.moon @@ -0,0 +1,284 @@ + +import insert from table +import Set from require "moonscript.data" +import Block from require "moonscript.compile" +import ntype from require "moonscript.types" + +import mtype from require "moonscript.util" + +-- globals allowed to be referenced +default_whitelist = Set { + '_G' + '_VERSION' + 'assert' + 'bit32' + 'collectgarbage' + 'coroutine' + 'debug' + 'dofile' + 'error' + 'getfenv' + 'getmetatable' + 'io' + 'ipairs' + 'load' + 'loadfile' + 'loadstring' + 'math' + 'module' + 'next' + 'os' + 'package' + 'pairs' + 'pcall' + 'print' + 'rawequal' + 'rawget' + 'rawlen' + 'rawset' + 'require' + 'select' + 'setfenv' + 'setmetatable' + 'string' + 'table' + 'tonumber' + 'tostring' + 'type' + 'unpack' + 'xpcall' + + "nil" + "true" + "false" +} + +-- the named checks that can be enabled or disabled when linting +LINT_STAGES = { + "global_access" + "unused" + "constant_assign" + "import_overwrite" +} + +class LinterBlock extends Block + new: (whitelist_globals=default_whitelist, stages, ...) => + super ... + @lint_errors = {} + + if stages + @lint_stages = Set stages + + @lint_wrap_transform = (block) => + inner = block.transform.statement + checked_imports = setmetatable {}, __mode: "k" + + block.transform.statement = (node, ...) -> + import_node = node + while ntype(import_node) == "transform" + import_node = import_node[2] + + if ntype(import_node) == "import" and not checked_imports[import_node] + checked_imports[import_node] = true + + for name in *import_node[2] + name = name[2] if ntype(name) == "colon" + binding = block\binding_value name + + -- repeated imports are reported by the constant assignment + -- check after the import is lowered + unless type(binding) == "table" and binding.const + if binding + block.root\lint_report "import_overwrite", + "import overwrites existing binding `#{name}`", import_node[-1] + + inner node, ... + block + + @lint_wrap_transform @ + + vc = @value_compilers + @value_compilers = setmetatable { + ref: (block, val) -> + name = val[2] + unless block\has_name(name) or whitelist_globals[name] or name\match "%." + @lint_report "global_access", "accessing global `#{name}`", val[-1] + + block\lint_mark_used name + vc.ref block, val + }, __index: vc + + sc = @statement_compilers + @statement_compilers = setmetatable { + assign: (block, node) -> + names = node[2] + + -- extract the names to be declared + for name in *names + -- don't include autogenerated names + if type(name) == "table" and name[1] == "temp_name" + continue + + const_name = if type(name) == "string" + name + elseif name[1] == "ref" + name[2] + + if const_name + binding = block\binding_value const_name + if type(binding) == "table" and binding.const + @lint_report "constant_assign", "assigning to constant `#{const_name}`", + type(name) == "table" and name[-1] or node[-1] or block.root.last_pos + + real_name, is_local = block\extract_assign_name name + -- already defined in some other scope + unless is_local or real_name and not block\has_name real_name, true + continue + + continue if real_name == "_" + + block.lint_unused_names or= {} + block.lint_unused_names[real_name] = node[-1] or 0 + + sc.assign block, node + }, __index: sc + + -- records an error if the stage is enabled + lint_report: (stage, msg, pos) => + root = @root + return if root.lint_stages and not root.lint_stages[stage] + insert root.lint_errors, {msg, pos, stage} + + lint_mark_used: (name) => + if @lint_unused_names and @lint_unused_names[name] + @lint_unused_names[name] = false + return + + if @parent + @parent\lint_mark_used name + + lint_check_unused: => + return unless @lint_unused_names and next @lint_unused_names + + names_by_position = {} + for name, pos in pairs @lint_unused_names + continue unless pos + names_by_position[pos] or= {} + insert names_by_position[pos], name + + tuples = [{pos, names} for pos,names in pairs names_by_position] + table.sort tuples, (a,b) -> a[1] < b[1] + + for {pos, names} in *tuples + @root\lint_report "unused", + "assigned but unused #{table.concat ["`#{n}`" for n in *names], ", "}", pos + + render: (...) => + @lint_check_unused! + super ... + + block: (...) => + + with child = super ... + .block = @block + .render = @render + .lint_check_unused = @lint_check_unused + .lint_mark_used = @lint_mark_used + .value_compilers = @value_compilers + .statement_compilers = @statement_compilers + .lint_wrap_transform = @lint_wrap_transform + @lint_wrap_transform child + +-- one `file:line:col: message [stage]` line per error +format_lint_compact = (errors, code, header) -> + return nil unless next errors + + import pos_to_line_col from require "moonscript.util" + formatted = for {msg, pos, stage} in *errors + location = if pos + line, col = pos_to_line_col code, pos + "#{header}:#{line}:#{col}" + else + header + + stage_suffix = stage and " [#{stage}]" or "" + "#{location}: #{msg}#{stage_suffix}" + + table.concat formatted, "\n" + +format_lint = (errors, code, header) -> + return unless next errors + + import pos_to_line, get_line from require "moonscript.util" + formatted = for {msg, pos} in *errors + if pos + line = pos_to_line code, pos + msg = "line #{line}: #{msg}" + line_text = "> " .. get_line code, line + + sep_len = math.max #msg, #line_text + table.concat { + msg + "="\rep sep_len + line_text + }, "\n" + + else + msg + + table.insert formatted, 1, header if header + table.concat formatted, "\n\n" + + +-- { +-- whitelist_globals: { +-- ["some_file_pattern"]: { +-- "some_var", "another_var" +-- } +-- } +-- } +whitelist_for_file = do + local lint_config + (fname) -> + unless lint_config + lint_config = {} + pcall -> lint_config = require "lint_config" + + return default_whitelist unless lint_config.whitelist_globals + final_list = {} + for pattern, list in pairs lint_config.whitelist_globals + if fname\match(pattern) + for item in *list + insert final_list, item + + setmetatable Set(final_list), __index: default_whitelist + +-- the named output formats for lint results +LINT_FORMATS = {"default", "compact"} + +formatters = { + default: format_lint + compact: format_lint_compact +} + +-- opts: {stages: {stage_name}, format: format_name} +lint_code = (code, name="string input", whitelist_globals, opts) -> + parse = require "moonscript.parse" + tree, err = parse.string code + return nil, err unless tree + + scope = LinterBlock whitelist_globals, opts and opts.stages + scope\stms tree + scope\lint_check_unused! + + formatter = formatters[opts and opts.format or "default"] + error "unknown lint format: #{opts.format}" unless formatter + formatter scope.lint_errors, code, name + +lint_file = (fname, opts) -> + f, err = io.open fname + return nil, err unless f + lint_code f\read("*a"), fname, whitelist_for_file(fname), opts + + +{ :lint_code, :lint_file, :LINT_STAGES, :LINT_FORMATS } diff --git a/moonscript/cmd/moonc.lua b/moonscript/cmd/moonc.lua new file mode 100644 index 00000000..e7618ef8 --- /dev/null +++ b/moonscript/cmd/moonc.lua @@ -0,0 +1,199 @@ +local lfs = require("lfs") +local split +split = require("moonscript.util").split +local dirsep, dirsep_chars, mkdir, normalize_dir, parse_dir, parse_file, convert_path, format_time, gettime, compile_file_text, write_file, compile_and_write, is_abs_path, path_to_target +dirsep = package.config:sub(1, 1) +if dirsep == "\\" then + dirsep_chars = "\\/" +else + dirsep_chars = dirsep +end +mkdir = function(path) + local chunks = split(path, dirsep) + local accum + for _index_0 = 1, #chunks do + local dir = chunks[_index_0] + accum = accum and tostring(accum) .. tostring(dirsep) .. tostring(dir) or dir + lfs.mkdir(accum) + end + return lfs.attributes(path, "mode") +end +normalize_dir = function(path) + return path:match("^(.-)[" .. tostring(dirsep_chars) .. "]*$") .. dirsep +end +parse_dir = function(path) + return (path:match("^(.-)[^" .. tostring(dirsep_chars) .. "]*$")) +end +parse_file = function(path) + return (path:match("^.-([^" .. tostring(dirsep_chars) .. "]*)$")) +end +convert_path = function(path) + local new_path = path:gsub("%.moon$", ".lua") + if new_path == path then + new_path = path .. ".lua" + end + return new_path +end +format_time = function(time) + return ("%.3fms"):format(time * 1000) +end +do + local socket + gettime = function() + if socket == nil then + pcall(function() + socket = require("socket") + end) + if not (socket) then + socket = false + end + end + if socket then + return socket.gettime() + else + return nil, "LuaSocket needed for benchmark" + end + end +end +compile_file_text = function(text, opts) + if opts == nil then + opts = { } + end + local parse = require("moonscript.parse") + local compile = require("moonscript.compile") + local parse_time + if opts.benchmark then + parse_time = assert(gettime()) + end + local tree, err = parse.string(text) + if not (tree) then + return nil, err + end + if parse_time then + parse_time = gettime() - parse_time + end + if opts.show_parse_tree then + local dump = require("moonscript.dump") + print(dump.tree(tree)) + return true + end + local compile_time + if opts.benchmark then + compile_time = gettime() + end + do + local mod = opts.transform_module + if mod then + local file = assert(loadfile(mod)) + local fn = assert(file()) + tree = assert(fn(tree)) + end + end + local code, posmap_or_err, err_pos = compile.tree(tree) + if not (code) then + return nil, compile.format_error(posmap_or_err, err_pos, text) + end + if compile_time then + compile_time = gettime() - compile_time + end + if opts.show_posmap then + local debug_posmap + debug_posmap = require("moonscript.util").debug_posmap + print("Pos", "Lua", ">>", "Moon") + print(debug_posmap(posmap_or_err, text, code)) + return true + end + if opts.benchmark then + print(table.concat({ + opts.fname or "stdin", + "Parse time \t" .. format_time(parse_time), + "Compile time\t" .. format_time(compile_time), + "" + }, "\n")) + return true + end + return code +end +write_file = function(fname, code) + mkdir(parse_dir(fname)) + local f, err = io.open(fname, "w") + if not (f) then + return nil, err + end + assert(f:write(code)) + assert(f:write("\n")) + f:close() + return "build" +end +compile_and_write = function(src, dest, opts) + if opts == nil then + opts = { } + end + local f = io.open(src) + if not (f) then + return nil, "Can't find file" + end + local text = assert(f:read("*a")) + f:close() + local code, err = compile_file_text(text, opts) + if not code then + return nil, err + end + if code == true then + return true + end + if opts.print then + print(code) + return true + end + return write_file(dest, code) +end +is_abs_path = function(path) + local first = path:sub(1, 1) + if dirsep == "\\" then + return first == "/" or first == "\\" or path:sub(2, 1) == ":" + else + return first == dirsep + end +end +path_to_target = function(path, target_dir, base_dir) + if target_dir == nil then + target_dir = nil + end + if base_dir == nil then + base_dir = nil + end + local target = convert_path(path) + if target_dir then + target_dir = normalize_dir(target_dir) + end + if base_dir and target_dir then + local head = base_dir:match("^(.-)[^" .. tostring(dirsep_chars) .. "]*[" .. tostring(dirsep_chars) .. "]?$") + if head then + local start, stop = target:find(head, 1, true) + if start == 1 then + target = target:sub(stop + 1) + end + end + end + if target_dir then + if is_abs_path(target) then + target = parse_file(target) + end + target = target_dir .. target + end + return target +end +return { + dirsep = dirsep, + mkdir = mkdir, + normalize_dir = normalize_dir, + parse_dir = parse_dir, + parse_file = parse_file, + convert_path = convert_path, + gettime = gettime, + format_time = format_time, + path_to_target = path_to_target, + compile_file_text = compile_file_text, + compile_and_write = compile_and_write +} diff --git a/moonscript/cmd/moonc.moon b/moonscript/cmd/moonc.moon new file mode 100644 index 00000000..3e05bb07 --- /dev/null +++ b/moonscript/cmd/moonc.moon @@ -0,0 +1,197 @@ +-- assorted utilities for moonc command line tool + +lfs = require "lfs" + +import split from require "moonscript.util" + +local * + +dirsep = package.config\sub 1,1 +dirsep_chars = if dirsep == "\\" + "\\/" -- windows +else + dirsep + +-- similar to mkdir -p +mkdir = (path) -> + chunks = split path, dirsep + + local accum + for dir in *chunks + accum = accum and "#{accum}#{dirsep}#{dir}" or dir + lfs.mkdir accum + + lfs.attributes path, "mode" + +-- strips excess / and ensures path ends with / +normalize_dir = (path) -> + path\match("^(.-)[#{dirsep_chars}]*$") .. dirsep + +-- parse the directory out of a path +parse_dir = (path) -> + (path\match "^(.-)[^#{dirsep_chars}]*$") + +-- parse the filename out of a path +parse_file = (path) -> + (path\match "^.-([^#{dirsep_chars}]*)$") + +-- converts .moon to a .lua path for calcuating compile target +convert_path = (path) -> + new_path = path\gsub "%.moon$", ".lua" + if new_path == path + new_path = path .. ".lua" + new_path + +format_time = (time) -> + "%.3fms"\format time*1000 + +gettime = do + local socket + -> + if socket == nil + pcall -> + socket = require "socket" + + unless socket + socket = false + + if socket + socket.gettime() + else + nil, "LuaSocket needed for benchmark" + +-- compiles file to lua, returns lua code +-- returns nil, error on error +-- returns true if some option handled the output instead +compile_file_text = (text, opts={}) -> + parse = require "moonscript.parse" + compile = require "moonscript.compile" + + parse_time = if opts.benchmark + assert gettime! + + tree, err = parse.string text + return nil, err unless tree + + if parse_time + parse_time = gettime! - parse_time + + if opts.show_parse_tree + dump = require "moonscript.dump" + print dump.tree tree + return true + + compile_time = if opts.benchmark + gettime! + + if mod = opts.transform_module + file = assert loadfile mod + fn = assert file! + tree = assert fn tree + + code, posmap_or_err, err_pos = compile.tree tree + + unless code + return nil, compile.format_error posmap_or_err, err_pos, text + + if compile_time + compile_time = gettime() - compile_time + + if opts.show_posmap + import debug_posmap from require "moonscript.util" + print "Pos", "Lua", ">>", "Moon" + print debug_posmap posmap_or_err, text, code + return true + + if opts.benchmark + print table.concat { + opts.fname or "stdin", + "Parse time \t" .. format_time(parse_time), + "Compile time\t" .. format_time(compile_time), + "" + }, "\n" + return true + + code + +write_file = (fname, code) -> + mkdir parse_dir fname + f, err = io.open fname, "w" + unless f + return nil, err + + assert f\write code + assert f\write "\n" + f\close! + "build" + +compile_and_write = (src, dest, opts={}) -> + f = io.open src + unless f + return nil, "Can't find file" + + text = assert f\read("*a") + f\close! + + code, err = compile_file_text text, opts + + if not code + return nil, err + + if code == true + return true + + if opts.print + print code + return true + + write_file dest, code + +is_abs_path = (path) -> + first = path\sub 1, 1 + if dirsep == "\\" + first == "/" or first == "\\" or path\sub(2,1) == ":" + else + first == dirsep + + +-- calcuate where a path should be compiled to +-- target_dir: the directory to place the file (optional, from -t flag) +-- base_dir: the directory where the file came from when globbing recursively +path_to_target = (path, target_dir=nil, base_dir=nil) -> + target = convert_path path + + if target_dir + target_dir = normalize_dir target_dir + + if base_dir and target_dir + -- one directory back + head = base_dir\match("^(.-)[^#{dirsep_chars}]*[#{dirsep_chars}]?$") + + if head + start, stop = target\find head, 1, true + if start == 1 + target = target\sub(stop + 1) + + if target_dir + if is_abs_path target + target = parse_file target + + target = target_dir .. target + + target + +{ + :dirsep + :mkdir + :normalize_dir + :parse_dir + :parse_file + :convert_path + :gettime + :format_time + :path_to_target + + :compile_file_text + :compile_and_write +} diff --git a/moonscript/cmd/watchers.lua b/moonscript/cmd/watchers.lua new file mode 100644 index 00000000..7e266b8d --- /dev/null +++ b/moonscript/cmd/watchers.lua @@ -0,0 +1,268 @@ +local remove_dupes +remove_dupes = function(list, key_fn) + local seen = { } + return (function() + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #list do + local _continue_0 = false + repeat + local item = list[_index_0] + local key + if key_fn then + key = key_fn(item) + else + key = item + end + if seen[key] then + _continue_0 = true + break + end + seen[key] = true + local _value_0 = item + _accum_0[_len_0] = _value_0 + _len_0 = _len_0 + 1 + _continue_0 = true + until true + if not _continue_0 then + break + end + end + return _accum_0 + end)() +end +local plural +plural = function(count, word) + return tostring(count) .. " " .. tostring(word) .. tostring(count == 1 and "" or "s") +end +local Watcher +do + local _class_0 + local _base_0 = { + start_msg = "Starting watch loop (Ctrl-C to exit)", + print_start = function(self, mode, misc) + return io.stderr:write(tostring(self.start_msg) .. " with " .. tostring(mode) .. " [" .. tostring(misc) .. "]\n") + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function(self, file_list) + self.file_list = file_list + end, + __base = _base_0, + __name = "Watcher" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + Watcher = _class_0 +end +local InotifyWacher +do + local _class_0 + local _parent_0 = Watcher + local _base_0 = { + get_dirs = function(self) + local parse_dir + parse_dir = require("moonscript.cmd.moonc").parse_dir + local dirs + do + local _accum_0 = { } + local _len_0 = 1 + local _list_0 = self.file_list + for _index_0 = 1, #_list_0 do + local _des_0 = _list_0[_index_0] + local file_path + file_path = _des_0[1] + local dir = parse_dir(file_path) + if dir == "" then + dir = "./" + end + local _value_0 = dir + _accum_0[_len_0] = _value_0 + _len_0 = _len_0 + 1 + end + dirs = _accum_0 + end + return remove_dupes(dirs) + end, + each_update = function(self) + return coroutine.wrap(function() + local dirs = self:get_dirs() + self:print_start("inotify", plural(#dirs, "dir")) + local wd_table = { } + local inotify = require("inotify") + local handle = inotify.init() + for _index_0 = 1, #dirs do + local dir = dirs[_index_0] + local wd = handle:addwatch(dir, inotify.IN_CLOSE_WRITE, inotify.IN_MOVED_TO) + wd_table[wd] = dir + end + while true do + local events = handle:read() + if not (events) then + break + end + for _index_0 = 1, #events do + local _continue_0 = false + repeat + local ev = events[_index_0] + local fname = ev.name + if not (fname:match("%.moon$")) then + _continue_0 = true + break + end + local dir = wd_table[ev.wd] + if dir ~= "./" then + fname = dir .. fname + end + coroutine.yield(fname) + _continue_0 = true + until true + if not _continue_0 then + break + end + end + end + end) + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, ...) + return _class_0.__parent.__init(self, ...) + end, + __base = _base_0, + __name = "InotifyWacher", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + local self = _class_0 + self.available = function(self) + return pcall(function() + return require("inotify") + end) + end + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + InotifyWacher = _class_0 +end +local SleepWatcher +do + local _class_0 + local _parent_0 = Watcher + local _base_0 = { + polling_rate = 1.0, + get_sleep_func = function(self) + local sleep + pcall(function() + sleep = require("socket").sleep + end) + sleep = sleep or require("moonscript")._sleep + if not (sleep) then + error("Missing sleep function; install LuaSocket") + end + return sleep + end, + each_update = function(self) + return coroutine.wrap(function() + local lfs = require("lfs") + local sleep = self:get_sleep_func() + self:print_start("polling", plural(#self.file_list, "files")) + local mod_time = { } + while true do + local _list_0 = self.file_list + for _index_0 = 1, #_list_0 do + local _continue_0 = false + repeat + local _des_0 = _list_0[_index_0] + local file + file = _des_0[1] + local time = lfs.attributes(file, "modification") + if not (time) then + mod_time[file] = nil + _continue_0 = true + break + end + if not (mod_time[file]) then + mod_time[file] = time + _continue_0 = true + break + end + if time > mod_time[file] then + mod_time[file] = time + coroutine.yield(file) + end + _continue_0 = true + until true + if not _continue_0 then + break + end + end + sleep(self.polling_rate) + end + end) + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, ...) + return _class_0.__parent.__init(self, ...) + end, + __base = _base_0, + __name = "SleepWatcher", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + SleepWatcher = _class_0 +end +return { + Watcher = Watcher, + SleepWatcher = SleepWatcher, + InotifyWacher = InotifyWacher +} diff --git a/moonscript/cmd/watchers.moon b/moonscript/cmd/watchers.moon new file mode 100644 index 00000000..c147fc5e --- /dev/null +++ b/moonscript/cmd/watchers.moon @@ -0,0 +1,103 @@ +remove_dupes = (list, key_fn) -> + seen = {} + return for item in *list + key = if key_fn then key_fn item else item + continue if seen[key] + seen[key] = true + item + +plural = (count, word) -> + "#{count} #{word}#{count == 1 and "" or "s"}" + +-- files is a list of tuples, {source, target} +class Watcher + start_msg: "Starting watch loop (Ctrl-C to exit)" + new: (@file_list) => + + print_start: (mode, misc) => + io.stderr\write "#{@start_msg} with #{mode} [#{misc}]\n" + +class InotifyWacher extends Watcher + @available: => + pcall -> require "inotify" + + get_dirs: => + import parse_dir from require "moonscript.cmd.moonc" + dirs = for {file_path} in *@file_list + dir = parse_dir file_path + dir = "./" if dir == "" + dir + + remove_dupes dirs + + -- creates an iterator that yields a file every time it's updated + -- TODO: detect when new files are added to directories + each_update: => + coroutine.wrap -> + dirs = @get_dirs! + + @print_start "inotify", plural #dirs, "dir" + + wd_table = {} + + inotify = require "inotify" + handle = inotify.init! + + for dir in *dirs + wd = handle\addwatch dir, inotify.IN_CLOSE_WRITE, inotify.IN_MOVED_TO + wd_table[wd] = dir + + while true + events = handle\read! + break unless events -- error? + + for ev in *events + fname = ev.name + continue unless fname\match "%.moon$" + dir = wd_table[ev.wd] + fname = dir .. fname if dir != "./" + + -- TODO: check to make sure the file was in the original set + coroutine.yield fname + +class SleepWatcher extends Watcher + polling_rate: 1.0 + + -- the windows mooonscript binaries provide their own sleep function + get_sleep_func: => + local sleep + + pcall -> + sleep = require("socket").sleep + + -- TODO: this is also loading moonloader, which isn't intentional + sleep or= require("moonscript")._sleep + error "Missing sleep function; install LuaSocket" unless sleep + sleep + + each_update: => + coroutine.wrap -> + lfs = require "lfs" + sleep = @get_sleep_func! + + @print_start "polling", plural #@file_list, "files" + mod_time = {} + + while true + for {file} in *@file_list + time = lfs.attributes file, "modification" + unless time -- file no longer exists + mod_time[file] = nil + continue + + unless mod_time[file] -- file time scanned + mod_time[file] = time + continue + + if time > mod_time[file] + mod_time[file] = time + coroutine.yield file + + sleep @polling_rate + +{:Watcher, :SleepWatcher, :InotifyWacher} diff --git a/moonscript/compile.lua b/moonscript/compile.lua index 102ac162..d8d3a127 100644 --- a/moonscript/compile.lua +++ b/moonscript/compile.lua @@ -1,77 +1,196 @@ -module("moonscript.compile", package.seeall) local util = require("moonscript.util") local dump = require("moonscript.dump") -require("moonscript.compile.format") -require("moonscript.compile.statement") -require("moonscript.compile.value") local transform = require("moonscript.transform") -local NameProxy, LocalName = transform.NameProxy, transform.LocalName +local NameProxy, LocalName +do + local _obj_0 = require("moonscript.transform.names") + NameProxy, LocalName = _obj_0.NameProxy, _obj_0.LocalName +end local Set +Set = require("moonscript.data").Set +local ntype, value_can_be_statement do - local _table_0 = require("moonscript.data") - Set = _table_0.Set + local _obj_0 = require("moonscript.types") + ntype, value_can_be_statement = _obj_0.ntype, _obj_0.value_can_be_statement end -local ntype +local statement_compilers = require("moonscript.compile.statement") +local value_compilers = require("moonscript.compile.value") +local concat, insert do - local _table_0 = require("moonscript.types") - ntype = _table_0.ntype + local _obj_0 = table + concat, insert = _obj_0.concat, _obj_0.insert end -local concat, insert = table.concat, table.insert -local pos_to_line, get_closest_line, trim = util.pos_to_line, util.get_closest_line, util.trim -local mtype = util.moon.type -local Line -Line = (function() - local _parent_0 = nil +local pos_to_line, get_closest_line, trim, unpack, mtype +pos_to_line, get_closest_line, trim, unpack, mtype = util.pos_to_line, util.get_closest_line, util.trim, util.unpack, util.mtype +local indent_char = " " +local Line, DelayedLine, Lines, Block, RootBlock +do + local _class_0 local _base_0 = { - _append_single = function(self, item) - if util.moon.type(item) == Line then - local _list_0 = item - for _index_0 = 1, #_list_0 do - value = _list_0[_index_0] - self:_append_single(value) - end + mark_pos = function(self, pos, line) + if line == nil then + line = #self + end + if not (self.posmap[line]) then + self.posmap[line] = pos + end + end, + add = function(self, item) + local _exp_0 = mtype(item) + if Line == _exp_0 then + item:render(self) + elseif Block == _exp_0 then + item:render(self) else - insert(self, item) + self[#self + 1] = item end - return nil + return self end, + flatten_posmap = function(self, line_no, out) + if line_no == nil then + line_no = 0 + end + if out == nil then + out = { } + end + local posmap = self.posmap + for i, l in ipairs(self) do + local _exp_0 = mtype(l) + if "string" == _exp_0 or DelayedLine == _exp_0 then + line_no = line_no + 1 + out[line_no] = posmap[i] + for _ in l:gmatch("\n") do + line_no = line_no + 1 + end + out[line_no] = posmap[i] + elseif Lines == _exp_0 then + local _ + _, line_no = l:flatten_posmap(line_no, out) + else + error("Unknown item in Lines: " .. tostring(l)) + end + end + return out, line_no + end, + flatten = function(self, indent, buffer) + if indent == nil then + indent = nil + end + if buffer == nil then + buffer = { } + end + for i = 1, #self do + local l = self[i] + local t = mtype(l) + if t == DelayedLine then + l = l:render() + t = "string" + end + local _exp_0 = t + if "string" == _exp_0 then + if indent then + insert(buffer, indent) + end + insert(buffer, l) + if "string" == type(self[i + 1]) then + if l:sub(-1) ~= ',' and l:sub(-3) ~= 'end' and self[i + 1]:sub(1, 1) == "(" then + insert(buffer, ";") + end + end + insert(buffer, "\n") + elseif Lines == _exp_0 then + l:flatten(indent and indent .. indent_char or indent_char, buffer) + else + error("Unknown item in Lines: " .. tostring(l)) + end + end + return buffer + end, + __tostring = function(self) + local strip + strip = function(t) + if "table" == type(t) then + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #t do + local v = t[_index_0] + _accum_0[_len_0] = strip(v) + _len_0 = _len_0 + 1 + end + return _accum_0 + else + return t + end + end + return "Lines<" .. tostring(util.dump(strip(self)):sub(1, -2)) .. ">" + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function(self) + self.posmap = { } + end, + __base = _base_0, + __name = "Lines" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + Lines = _class_0 +end +do + local _class_0 + local _base_0 = { + pos = nil, append_list = function(self, items, delim) for i = 1, #items do - self:_append_single(items[i]) + self:append(items[i]) if i < #items then insert(self, delim) end end + return nil end, - append = function(self, ...) - local _list_0 = { - ... - } - for _index_0 = 1, #_list_0 do - local item = _list_0[_index_0] - self:_append_single(item) + append = function(self, first, ...) + if Line == mtype(first) then + if not (self.pos) then + self.pos = first.pos + end + for _index_0 = 1, #first do + local value = first[_index_0] + self:append(value) + end + else + insert(self, first) + end + if ... then + return self:append(...) end - return nil end, render = function(self, buffer) local current = { } local add_current add_current = function() - return insert(buffer, concat(current)) + buffer:add(concat(current)) + return buffer:mark_pos(self.pos) end - local _list_0 = self - for _index_0 = 1, #_list_0 do - local chunk = _list_0[_index_0] + for _index_0 = 1, #self do + local chunk = self[_index_0] local _exp_0 = mtype(chunk) if Block == _exp_0 then - local _list_1 = chunk:render({ }) - for _index_1 = 1, #_list_1 do - local block_chunk = _list_1[_index_1] + local _list_0 = chunk:render(Lines()) + for _index_1 = 1, #_list_0 do + local block_chunk = _list_0[_index_1] if "string" == type(block_chunk) then insert(current, block_chunk) else add_current() - insert(buffer, block_chunk) + buffer:add(block_chunk) current = { } end end @@ -79,37 +198,49 @@ Line = (function() insert(current, chunk) end end - if #current > 0 then + if current[1] then add_current() end return buffer end, __tostring = function(self) - return "Line<" .. tostring(self:render()) .. ">" + return "Line<" .. tostring(util.dump(self):sub(1, -2)) .. ">" end } _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self, ...) - if _parent_0 then - return _parent_0.__init(self, ...) - end - end, + _class_0 = setmetatable({ + __init = function() end, __base = _base_0, - __name = "Line", - __parent = _parent_0 + __name = "Line" }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + Line = _class_0 +end +do + local _class_0 + local _base_0 = { + prepare = function() end, + render = function(self) + self:prepare() + return concat(self) + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function(self, fn) + self.prepare = fn end, + __base = _base_0, + __name = "DelayedLine" + }, { + __index = _base_0, __call = function(cls, ...) local _self_0 = setmetatable({}, _base_0) cls.__init(_self_0, ...) @@ -117,19 +248,17 @@ Line = (function() end }) _base_0.__class = _class_0 - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -Block = (function() - local add_to_buffer - local _parent_0 = nil + DelayedLine = _class_0 +end +do + local _class_0 local _base_0 = { header = "do", footer = "end", export_all = false, export_proper = false, + value_compilers = value_compilers, + statement_compilers = statement_compilers, __tostring = function(self) local h if "string" == type(self.header) then @@ -137,10 +266,7 @@ Block = (function() else h = unpack(self.header:render({ })) end - return "Block<" .. tostring(h) .. "> <- " .. tostring(self.parent) - end, - line_table = function(self) - return self._posmap + return ("Block<" .. tostring(h) .. "> <- ") .. tostring(self.parent) end, set = function(self, name, value) self._state[name] = value @@ -148,78 +274,100 @@ Block = (function() get = function(self, name) return self._state[name] end, - listen = function(self, name, fn) - self._listeners[name] = fn - end, - unlisten = function(self, name) - self._listeners[name] = nil - end, - send = function(self, name, ...) - do - local fn = self._listeners[name] - if fn then - return fn(self, ...) - end - end + get_current = function(self, name) + return rawget(self._state, name) + end, + extract_assign_name = function(self, node) + local is_local = false + local real_name + local _exp_0 = mtype(node) + if LocalName == _exp_0 then + is_local = true + real_name = node:get_name(self) + elseif NameProxy == _exp_0 then + real_name = node:get_name(self) + elseif "table" == _exp_0 then + real_name = node[1] == "ref" and node[2] + elseif "string" == _exp_0 then + real_name = node + end + return real_name, is_local end, declare = function(self, names) - local undeclared = (function() + local undeclared + do local _accum_0 = { } - local _len_0 = 0 - local _list_0 = names - for _index_0 = 1, #_list_0 do - local name = _list_0[_index_0] - local is_local = false - local real_name - local _exp_0 = util.moon.type(name) - if LocalName == _exp_0 then - is_local = true - real_name = name:get_name(self) - elseif NameProxy == _exp_0 then - real_name = name:get_name(self) - elseif "string" == _exp_0 then - real_name = name - end - local _value_0 - if is_local or real_name and not self:has_name(real_name) then - _value_0 = real_name - end - if _value_0 ~= nil then - _len_0 = _len_0 + 1 + local _len_0 = 1 + for _index_0 = 1, #names do + local _continue_0 = false + repeat + local name = names[_index_0] + local real_name, is_local = self:extract_assign_name(name) + if not (is_local or real_name and not self:has_name(real_name, true)) then + _continue_0 = true + break + end + self:put_name(real_name) + if self:name_exported(real_name) then + _continue_0 = true + break + end + local _value_0 = real_name _accum_0[_len_0] = _value_0 + _len_0 = _len_0 + 1 + _continue_0 = true + until true + if not _continue_0 then + break end end - return _accum_0 - end)() - local _list_0 = undeclared - for _index_0 = 1, #_list_0 do - local name = _list_0[_index_0] - self:put_name(name) + undeclared = _accum_0 end return undeclared end, whitelist_names = function(self, names) self._name_whitelist = Set(names) end, + name_exported = function(self, name) + if self.export_all then + return true + end + if self.export_proper and name:match("^%u") then + return true + end + end, put_name = function(self, name, ...) - value = ... + local value = ... if select("#", ...) == 0 then value = true end - if util.moon.type(name) == NameProxy then + if NameProxy == mtype(name) then name = name:get_name(self) end self._names[name] = value end, - has_name = function(self, name, skip_exports) - if not skip_exports then - if self.export_all then - return true + put_fresh_names = function(self, names) + for _index_0 = 1, #names do + local name = names[_index_0] + local real_name = self:extract_assign_name(name) + if real_name then + self:put_name(real_name) end - if self.export_proper and name:match("^[A-Z]") then - return true + end + end, + binding_value = function(self, name) + local val = self._names[name] + if val == nil and self.parent then + if not self._name_whitelist or self._name_whitelist[name] then + return self.parent:binding_value(name) end end + return val + end, + has_name = function(self, name, skip_exports) + if not skip_exports and self:name_exported(name) then + return true + end local yes = self._names[name] if yes == nil and self.parent then if not self._name_whitelist or self._name_whitelist[name] then @@ -229,6 +377,21 @@ Block = (function() return yes end end, + is_local = function(self, node) + local t = mtype(node) + if t == "string" then + return self:has_name(node, false) + end + if t == NameProxy or t == LocalName then + return true + end + if t == "table" then + if node[1] == "ref" or (node[1] == "chain" and #node == 2) then + return self:is_local(node[2]) + end + end + return false + end, free_name = function(self, prefix, dont_put) prefix = prefix or "moon" local searching = true @@ -260,77 +423,34 @@ Block = (function() }) return name end, - mark_pos = function(self, node) - if node[-1] then - self.last_pos = node[-1] - if not self._posmap[self.current_line] then - self._posmap[self.current_line] = self.last_pos - end - end - end, - add_raw = function(self, item) - return insert(self._lines, item) - end, - append_line_table = function(self, sub_table, offset) - offset = offset + self.current_line - for line, source in pairs(sub_table) do - line = line + offset - if not self._posmap[line] then - self._posmap[line] = source - end - end + discard_name = function(self) + self._discard_name = self._discard_name or NameProxy("scrap") + return self._discard_name end, - add_line_tables = function(self, line) - local _list_0 = line - for _index_0 = 1, #_list_0 do - local chunk = _list_0[_index_0] - if util.moon.type(chunk) == Block then - local current = chunk - while current do - if util.moon.type(current.header) == Line then - self:add_line_tables(current.header) - end - self:append_line_table(current:line_table(), 0) - self.current_line = self.current_line + current.current_line - current = current.next - end + add = function(self, item, pos) + do + local _with_0 = self._lines + _with_0:add(item) + if pos then + _with_0:mark_pos(pos) end end - end, - add = function(self, line) - local _exp_0 = util.moon.type(line) - if "string" == _exp_0 then - return insert(self._lines, line) - elseif Block == _exp_0 then - return line:render(self._lines) - elseif Line == _exp_0 then - return line:render(self._lines) - else - return error("Adding unknown item") - end + return item end, render = function(self, buffer) - add_to_buffer(buffer, self.header) - local lines = (function() - local _accum_0 = { } - local _len_0 = 0 - local _list_0 = self._lines - for _index_0 = 1, #_list_0 do - local l = _list_0[_index_0] - _len_0 = _len_0 + 1 - _accum_0[_len_0] = l - end - return _accum_0 - end)() + buffer:add(self.header) + buffer:mark_pos(self.pos) if self.next then - insert(buffer, lines) + buffer:add(self._lines) self.next:render(buffer) else - if #lines == 0 and "string" == type(buffer[#buffer]) then - buffer[#buffer] = buffer[#buffer] .. (" " .. (unpack(add_to_buffer({ }, self.footer)))) + if #self._lines == 0 and "string" == type(buffer[#buffer]) then + local _update_0 = #buffer + buffer[_update_0] = buffer[_update_0] .. (" " .. (unpack(Lines():add(self.footer)))) else - insert(buffer, lines) - add_to_buffer(buffer, self.footer) + buffer:add(self._lines) + buffer:add(self.footer) + buffer:mark_pos(self.pos) end end return buffer @@ -346,14 +466,18 @@ Block = (function() end end, is_stm = function(self, node) - return line_compile[ntype(node)] ~= nil + return self.statement_compilers[ntype(node)] ~= nil end, is_value = function(self, node) local t = ntype(node) - return value_compile[t] ~= nil or t == "value" + return self.value_compilers[t] ~= nil or t == "value" end, - name = function(self, node) - return self:value(node) + name = function(self, node, ...) + if type(node) == "string" then + return node + else + return self:value(node, ...) + end end, value = function(self, node, ...) node = self.transform.value(node) @@ -361,14 +485,28 @@ Block = (function() if type(node) ~= "table" then action = "raw_value" else - self:mark_pos(node) action = node[1] end - local fn = value_compile[action] - if not fn then - error("Failed to compile value: " .. dump.value(node)) + local fn = self.value_compilers[action] + if not (fn) then + error({ + "compile-error", + "Failed to find value compiler for: " .. dump.value(node), + node[-1] + }) + end + local out = fn(self, node, ...) + if type(node) == "table" and node[-1] then + if type(out) == "string" then + do + local _with_0 = Line() + _with_0:append(out) + out = _with_0 + end + end + out.pos = node[-1] end - return fn(self, node, ...) + return out end, values = function(self, values, delim) delim = delim or ', ' @@ -376,12 +514,11 @@ Block = (function() local _with_0 = Line() _with_0:append_list((function() local _accum_0 = { } - local _len_0 = 0 - local _list_0 = values - for _index_0 = 1, #_list_0 do - local v = _list_0[_index_0] - _len_0 = _len_0 + 1 + local _len_0 = 1 + for _index_0 = 1, #values do + local v = values[_index_0] _accum_0[_len_0] = self:value(v) + _len_0 = _len_0 + 1 end return _accum_0 end)(), delim) @@ -392,28 +529,36 @@ Block = (function() if not node then return end + if type(node) == "table" and node[-1] then + self.root.last_pos = node[-1] + end node = self.transform.statement(node) - local fn = line_compile[ntype(node)] - if not fn then - if has_value(node) then - self:stm({ - "assign", - { - "_" - }, - { - node - } - }) + local result + do + local fn = self.statement_compilers[ntype(node)] + if fn then + result = fn(self, node, ...) else - self:add(self:value(node)) + if value_can_be_statement(node) then + result = self:value(node) + else + result = self:stm({ + "assign", + { + self:discard_name() + }, + { + node + } + }) + end end - else - self:mark_pos(node) - local out = fn(self, node, ...) - if out then - self:add(out) + end + if result then + if type(node) == "table" and type(result) == "table" and node[-1] then + result.pos = node[-1] end + self:add(result) end return nil end, @@ -421,67 +566,46 @@ Block = (function() if ret then error("deprecated stms call, use transformer") end - local _list_0 = stms - for _index_0 = 1, #_list_0 do - local stm = _list_0[_index_0] - self:stm(stm) + local current_stms, current_stm_i + current_stms, current_stm_i = self.current_stms, self.current_stm_i + self.current_stms = stms + for i = 1, #stms do + self.current_stm_i = i + self:stm(stms[i]) end + self.current_stms = current_stms + self.current_stm_i = current_stm_i return nil - end, - splice = function(self, fn) - local lines = { - "lines", - self._lines - } - self._lines = { } - return self:stms(fn(lines)) end } _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ + _class_0 = setmetatable({ __init = function(self, parent, header, footer) self.parent, self.header, self.footer = parent, header, footer - self.current_line = 1 - self._lines = { } - self._posmap = { } + self._lines = Lines() self._names = { } self._state = { } - self._listeners = { } do - local _with_0 = transform self.transform = { - value = _with_0.Value:bind(self), - statement = _with_0.Statement:bind(self) + value = transform.Value:bind(self), + statement = transform.Statement:bind(self) } end if self.parent then self.root = self.parent.root self.indent = self.parent.indent + 1 - setmetatable(self._state, { + return setmetatable(self._state, { __index = self.parent._state }) - return setmetatable(self._listeners, { - __index = self.parent._listeners - }) else + self.root = self self.indent = 0 end end, __base = _base_0, - __name = "Block", - __parent = _parent_0 + __name = "Block" }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, + __index = _base_0, __call = function(cls, ...) local _self_0 = setmetatable({}, _base_0) cls.__init(_self_0, ...) @@ -489,59 +613,23 @@ Block = (function() end }) _base_0.__class = _class_0 - local self = _class_0 - add_to_buffer = function(buffer, line) - local _exp_0 = mtype(line) - if Line == _exp_0 then - line:render(buffer) - else - insert(buffer, line) - end - return buffer - end - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -local flatten_lines -flatten_lines = function(lines, indent, buffer) - if indent == nil then - indent = nil - end - if buffer == nil then - buffer = { } - end - for i = 1, #lines do - local l = lines[i] - local _exp_0 = type(l) - if "string" == _exp_0 then - if indent then - insert(buffer, indent) - end - insert(buffer, l) - if "string" == type(lines[i + 1]) then - local lc = l:sub(-1) - if (lc == ")" or lc == "]") and lines[i + 1]:sub(1, 1) == "(" then - insert(buffer, ";") - end - end - insert(buffer, "\n") - local last = l - elseif "table" == _exp_0 then - flatten_lines(l, indent and indent .. indent_char or indent_char, buffer) - end - end - return buffer + Block = _class_0 end -RootBlock = (function() +do + local _class_0 local _parent_0 = Block local _base_0 = { __tostring = function(self) return "RootBlock<>" end, + root_stms = function(self, stms) + if not (self.options.implicitly_return_root == false) then + stms = transform.Statement.transformers.root_stms(self, stms) + end + return self:stms(stms) + end, render = function(self) - local buffer = flatten_lines(self._lines) + local buffer = self._lines:flatten() if buffer[#buffer] == "\n" then buffer[#buffer] = nil end @@ -549,13 +637,11 @@ RootBlock = (function() end } _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self, ...) - self.root = self - return _parent_0.__init(self, ...) + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, options) + self.options = options + return _class_0.__parent.__init(self) end, __base = _base_0, __name = "RootBlock", @@ -563,8 +649,11 @@ RootBlock = (function() }, { __index = function(cls, name) local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end else return val end @@ -576,21 +665,28 @@ RootBlock = (function() end }) _base_0.__class = _class_0 - if _parent_0 and _parent_0.__inherited then + if _parent_0.__inherited then _parent_0.__inherited(_parent_0, _class_0) end - return _class_0 -end)() + RootBlock = _class_0 +end +local format_error format_error = function(msg, pos, file_str) - local line = pos_to_line(file_str, pos) - local line_str - line_str, line = get_closest_line(file_str, line) - line_str = line_str or "" + msg = tostring(msg) + local line_message + if pos then + local line = pos_to_line(file_str, pos) + local line_str + line_str, line = get_closest_line(file_str, line) + line_str = line_str or "" + line_message = (" [%d] >> %s"):format(line, trim(line_str)) + end return concat({ "Compile error: " .. msg, - (" [%d] >> %s"):format(line, trim(line_str)) + line_message }, "\n") end +local value value = function(value) local out = nil do @@ -600,38 +696,55 @@ value = function(value) end return out end -tree = function(tree, scope) - if scope == nil then - scope = RootBlock() +local tree +tree = function(tree, options) + if options == nil then + options = { } end assert(tree, "missing tree") + local scope = (options.scope or RootBlock)(options) local runner = coroutine.create(function() - local _list_0 = tree - for _index_0 = 1, #_list_0 do - local line = _list_0[_index_0] - scope:stm(line) - end - return scope:render() + return scope:root_stms(tree) end) - local success, result = coroutine.resume(runner) - if not success then - local error_msg - if type(result) == "table" then - local error_type = result[1] - if error_type == "user-error" then - error_msg = result[2] + local success, err = coroutine.resume(runner) + if not (success) then + local error_msg, error_pos + if type(err) == "table" then + local _exp_0 = err[1] + if "user-error" == _exp_0 or "compile-error" == _exp_0 then + error_msg, error_pos = unpack(err, 2) else - error_msg = error("Unknown error thrown", util.dump(error_msg)) + error_msg, error_pos = concat({ + "Unknown error thrown: " .. tostring(util.dump(err)), + debug.traceback(runner) + }, "\n") end else - error_msg = concat({ - result, + error_msg, error_pos = concat({ + err, debug.traceback(runner) }, "\n") end - return nil, error_msg, scope.last_pos - else - local tbl = scope:line_table() - return result, tbl + return nil, error_msg, error_pos or scope.last_pos + end + local lua_code = scope:render() + local posmap = scope._lines:flatten_posmap() + return lua_code, posmap +end +do + local data = require("moonscript.data") + for name, cls in pairs({ + Line = Line, + Lines = Lines, + DelayedLine = DelayedLine + }) do + data[name] = cls end end +return { + tree = tree, + value = value, + format_error = format_error, + Block = Block, + RootBlock = RootBlock +} diff --git a/moonscript/compile.moon b/moonscript/compile.moon index 4705960c..9e5ab210 100644 --- a/moonscript/compile.moon +++ b/moonscript/compile.moon @@ -1,70 +1,155 @@ -module "moonscript.compile", package.seeall util = require "moonscript.util" dump = require "moonscript.dump" - -require "moonscript.compile.format" -require "moonscript.compile.statement" -require "moonscript.compile.value" - transform = require "moonscript.transform" -import NameProxy, LocalName from transform +import NameProxy, LocalName from require "moonscript.transform.names" import Set from require "moonscript.data" -import ntype from require "moonscript.types" +import ntype, value_can_be_statement from require "moonscript.types" + +statement_compilers = require "moonscript.compile.statement" +value_compilers = require "moonscript.compile.value" import concat, insert from table -import pos_to_line, get_closest_line, trim from util +import pos_to_line, get_closest_line, trim, unpack, mtype from util + +indent_char = " " + +local Line, DelayedLine, Lines, Block, RootBlock + +-- a buffer for building up lines +class Lines + new: => + @posmap = {} + + mark_pos: (pos, line=#@) => + @posmap[line] = pos unless @posmap[line] + + -- append a line or lines to the buffer + add: (item) => + switch mtype item + when Line + item\render self + when Block + item\render self + else -- also captures DelayedLine + @[#@ + 1] = item + @ + + flatten_posmap: (line_no=0, out={}) => + posmap = @posmap + for i, l in ipairs @ + switch mtype l + when "string", DelayedLine + line_no += 1 + out[line_no] = posmap[i] + + line_no += 1 for _ in l\gmatch"\n" + out[line_no] = posmap[i] + when Lines + _, line_no = l\flatten_posmap line_no, out + else + error "Unknown item in Lines: #{l}" -mtype = util.moon.type + out, line_no + + flatten: (indent=nil, buffer={}) => + for i = 1, #@ + l = @[i] + t = mtype l + + if t == DelayedLine + l = l\render! + t = "string" + + switch t + when "string" + insert buffer, indent if indent + insert buffer, l + + -- insert breaks between ambiguous statements + if "string" == type @[i + 1] + if l\sub(-1)!=',' and l\sub(-3)!='end' and @[i + 1]\sub(1,1) == "(" + insert buffer, ";" + + insert buffer, "\n" + when Lines + l\flatten indent and indent .. indent_char or indent_char, buffer + else + error "Unknown item in Lines: #{l}" + buffer + + __tostring: => + -- strip non-array elements + strip = (t) -> + if "table" == type t + [strip v for v in *t] + else + t -export tree, value, format_error -export Block, RootBlock + "Lines<#{util.dump(strip @)\sub 1, -2}>" --- buffer for building up a line +-- Buffer for building up a line +-- A plain old table holding either strings or Block objects. +-- Adding a line to a line will cause that line to be merged in. class Line - _append_single: (item) => - if util.moon.type(item) == Line - @_append_single value for value in *item - else - insert self, item - nil + pos: nil append_list: (items, delim) => for i = 1,#items - @_append_single items[i] + @append items[i] if i < #items then insert self, delim - - append: (...) => - @_append_single item for item in *{...} nil - -- todo: remove concats from here + append: (first, ...) => + if Line == mtype first + -- print "appending line to line", first.pos, first + @pos = first.pos unless @pos -- bubble pos if there isn't one + @append value for value in *first + else + insert self, first + + if ... + @append ... + + -- todo: try to remove concats from here render: (buffer) => current = {} add_current = -> - insert buffer, concat current + buffer\add concat current + buffer\mark_pos @pos for chunk in *@ switch mtype chunk when Block - for block_chunk in *chunk\render{} + for block_chunk in *chunk\render Lines! if "string" == type block_chunk insert current, block_chunk else add_current! - insert buffer, block_chunk + buffer\add block_chunk current = {} else insert current, chunk - if #current > 0 + if current[1] add_current! buffer - __tostring: => "Line<#{@render!}>" + __tostring: => + "Line<#{util.dump(@)\sub 1, -2}>" + +class DelayedLine + new: (fn) => + @prepare = fn + + prepare: -> + + render: => + @prepare! + concat @ class Block header: "do" @@ -73,6 +158,9 @@ class Block export_all: false export_proper: false + value_compilers: value_compilers + statement_compilers: statement_compilers + __tostring: => h = if "string" == type @header @header @@ -82,13 +170,10 @@ class Block "Block<#{h}> <- " .. tostring @parent new: (@parent, @header, @footer) => - @current_line = 1 + @_lines = Lines! - @_lines = {} - @_posmap = {} @_names = {} @_state = {} - @_listeners = {} with transform @transform = { @@ -100,58 +185,81 @@ class Block @root = @parent.root @indent = @parent.indent + 1 setmetatable @_state, { __index: @parent._state } - setmetatable @_listeners, { __index: @parent._listeners } else + @root = self @indent = 0 - line_table: => - @_posmap - set: (name, value) => @_state[name] = value get: (name) => @_state[name] - listen: (name, fn) => - @_listeners[name] = fn - - unlisten: (name) => - @_listeners[name] = nil + get_current: (name) => + rawget @_state, name + + extract_assign_name: (node) => + is_local = false + real_name = switch mtype node + when LocalName + is_local = true + node\get_name self + when NameProxy + node\get_name self + when "table" + node[1] == "ref" and node[2] + when "string" + -- TOOD: some legacy transfomers might use string for ref + node - send: (name, ...) => - if fn = @_listeners[name] - fn self, ... + real_name, is_local declare: (names) => undeclared = for name in *names - is_local = false - real_name = switch util.moon.type name - when LocalName - is_local = true - name\get_name self - when NameProxy then name\get_name self - when "string" then name + real_name, is_local = @extract_assign_name name + continue unless is_local or real_name and not @has_name real_name, true + -- this also puts exported names so they can be assigned a new value in + -- deeper scope + @put_name real_name + continue if @name_exported real_name + real_name - real_name if is_local or real_name and not @has_name real_name - - @put_name name for name in *undeclared undeclared whitelist_names: (names) => @_name_whitelist = Set names + name_exported: (name) => + return true if @export_all + return true if @export_proper and name\match"^%u" + put_name: (name, ...) => value = ... value = true if select("#", ...) == 0 - name = name\get_name self if util.moon.type(name) == NameProxy + name = name\get_name self if NameProxy == mtype name @_names[name] = value + -- record names the current construct always binds fresh (loop variables, + -- local statements), so they shadow any binding in an enclosing scope + put_fresh_names: (names) => + for name in *names + real_name = @extract_assign_name name + @put_name real_name if real_name + + -- the value stored for a binding by put_name, following the same scope + -- visibility as has_name. constant bindings store a descriptor table + binding_value: (name) => + val = @_names[name] + if val == nil and @parent + if not @_name_whitelist or @_name_whitelist[name] + return @parent\binding_value name + val + + -- Check if a name is defined in the current or any enclosing scope + -- skip_exports: ignore names that have been exported using `export` has_name: (name, skip_exports) => - if not skip_exports - return true if @export_all - return true if @export_proper and name\match"^[A-Z]" + return true if not skip_exports and @name_exported name yes = @_names[name] if yes == nil and @parent @@ -160,6 +268,18 @@ class Block else yes + is_local: (node) => + t = mtype node + + return @has_name(node, false) if t == "string" + return true if t == NameProxy or t == LocalName + + if t == "table" + if node[1] == "ref" or (node[1] == "chain" and #node == 2) + return @is_local node[2] + + false + free_name: (prefix, dont_put) => prefix = prefix or "moon" searching = true @@ -177,74 +297,34 @@ class Block @stm {"assign", {name}, {value}} name - mark_pos: (node) => - if node[-1] - @last_pos = node[-1] - if not @_posmap[@current_line] - @_posmap[@current_line] = @last_pos - - -- add raw text as new line - add_raw: (item) => - insert @_lines, item - - append_line_table: (sub_table, offset) => - offset = offset + @current_line - - for line, source in pairs sub_table - line += offset - if not @_posmap[line] - @_posmap[line] = source - - add_line_tables: (line) => - for chunk in *line - if util.moon.type(chunk) == Block - current = chunk - while current - if util.moon.type(current.header) == Line - @add_line_tables current.header - - @append_line_table current\line_table!, 0 - @current_line += current.current_line - current = current.next - - -- add a line object - add: (line) => - -- print "adding", line - switch util.moon.type line - when "string" - insert @_lines, line - when Block - line\render @_lines - when Line - line\render @_lines - else - error "Adding unknown item" + -- expressions that need to be coerced into statements are assigned to this name + discard_name: => + @_discard_name or= NameProxy "scrap" + @_discard_name - add_to_buffer = (buffer, line) -> - switch mtype line - when Line - line\render buffer - else - insert buffer, line - buffer + -- add something to the line buffer + add: (item, pos) => + with @_lines + \add item + \mark_pos pos if pos + item -- todo: pass in buffer as argument render: (buffer) => - add_to_buffer buffer, @header - - -- copy lines - lines = [l for l in *@_lines] + buffer\add @header + buffer\mark_pos @pos if @next - insert buffer, lines + buffer\add @_lines @next\render buffer else -- join an empty block into a single line - if #lines == 0 and "string" == type buffer[#buffer] - buffer[#buffer] ..= " " .. (unpack add_to_buffer {}, @footer) + if #@_lines == 0 and "string" == type buffer[#buffer] + buffer[#buffer] ..= " " .. (unpack Lines!\add @footer) else - insert buffer, lines - add_to_buffer buffer, @footer + buffer\add @_lines + buffer\add @footer + buffer\mark_pos @pos buffer @@ -256,25 +336,43 @@ class Block \append ... is_stm: (node) => - line_compile[ntype node] != nil + @statement_compilers[ntype node] != nil is_value: (node) => t = ntype node - value_compile[t] != nil or t == "value" + @value_compilers[t] != nil or t == "value" + + -- compile name for assign + name: (node, ...) => + if type(node) == "string" + node + else + @value node, ... - -- line wise compile functions - name: (node) => @value node value: (node, ...) => node = @transform.value node action = if type(node) != "table" "raw_value" else - @mark_pos node node[1] - fn = value_compile[action] - error "Failed to compile value: "..dump.value node if not fn - fn self, node, ... + fn = @value_compilers[action] + unless fn + error { + "compile-error" + "Failed to find value compiler for: " .. dump.value node + node[-1] + } + + out = fn self, node, ... + + -- store the pos, creating a line if necessary + if type(node) == "table" and node[-1] + if type(out) == "string" + out = with Line! do \append out + out.pos = node[-1] + + out values: (values, delim) => delim = delim or ', ' @@ -283,72 +381,71 @@ class Block stm: (node, ...) => return if not node -- skip blank statements + + -- track the most recent position for errors raised without one + if type(node) == "table" and node[-1] + @root.last_pos = node[-1] + node = @transform.statement node - fn = line_compile[ntype(node)] - if not fn - -- coerce value into statement - if has_value node - @stm {"assign", {"_"}, {node}} - else - @add @value node + + result = if fn = @statement_compilers[ntype(node)] + fn @, node, ... else - @mark_pos node - out = fn self, node, ... - @add out if out + if value_can_be_statement node + @value node + else + -- coerce value into statement + @stm {"assign", {@discard_name!}, {node}} + + if result + if type(node) == "table" and type(result) == "table" and node[-1] + result.pos = node[-1] + @add result + nil stms: (stms, ret) => error "deprecated stms call, use transformer" if ret - @stm stm for stm in *stms - nil - - splice: (fn) => - lines = {"lines", @_lines} - @_lines = {} - @stms fn lines + {:current_stms, :current_stm_i} = @ + @current_stms = stms + for i=1,#stms + @current_stm_i = i + @stm stms[i] -flatten_lines = (lines, indent=nil, buffer={}) -> - for i = 1, #lines - l = lines[i] - switch type l - when "string" - insert buffer, indent if indent - insert buffer, l + @current_stms = current_stms + @current_stm_i = current_stm_i - -- insert breaks between ambiguous statements - if "string" == type lines[i + 1] - lc = l\sub(-1) - if (lc == ")" or lc == "]") and lines[i + 1]\sub(1,1) == "(" - insert buffer, ";" - - insert buffer, "\n" - last = l - when "table" - flatten_lines l, indent and indent .. indent_char or indent_char, buffer - - buffer + nil class RootBlock extends Block - new: (...) => - @root = self - super ... + new: (@options) => + super! __tostring: => "RootBlock<>" + root_stms: (stms) => + unless @options.implicitly_return_root == false + stms = transform.Statement.transformers.root_stms self, stms + @stms stms + render: => - -- print util.dump @_lines - buffer = flatten_lines @_lines + -- print @_lines + buffer = @_lines\flatten! buffer[#buffer] = nil if buffer[#buffer] == "\n" table.concat buffer format_error = (msg, pos, file_str) -> - line = pos_to_line file_str, pos - line_str, line = get_closest_line file_str, line - line_str = line_str or "" + msg = tostring msg + line_message = if pos + line = pos_to_line file_str, pos + line_str, line = get_closest_line file_str, line + line_str = line_str or "" + (" [%d] >> %s")\format line, trim line_str + concat { "Compile error: "..msg - (" [%d] >> %s")\format line, trim line_str + line_message }, "\n" value = (value) -> @@ -358,26 +455,35 @@ value = (value) -> out = \render! out -tree = (tree, scope=RootBlock!) -> +tree = (tree, options={}) -> assert tree, "missing tree" + scope = (options.scope or RootBlock) options + runner = coroutine.create -> - scope\stm line for line in *tree - scope\render! - - success, result = coroutine.resume runner - if not success - error_msg = if type(result) == "table" - error_type = result[1] - if error_type == "user-error" - result[2] - else - error "Unknown error thrown", util.dump error_msg + scope\root_stms tree + + success, err = coroutine.resume runner + + unless success + error_msg, error_pos = if type(err) == "table" + switch err[1] + when "user-error", "compile-error" + unpack err, 2 + else + concat {"Unknown error thrown: #{util.dump err}", debug.traceback runner}, "\n" else - concat {result, debug.traceback runner}, "\n" + concat {err, debug.traceback runner}, "\n" + + return nil, error_msg, error_pos or scope.last_pos + + lua_code = scope\render! + posmap = scope._lines\flatten_posmap! + lua_code, posmap - nil, error_msg, scope.last_pos - else - tbl = scope\line_table! - result, tbl +-- mmmm +with data = require "moonscript.data" + for name, cls in pairs {:Line, :Lines, :DelayedLine} + data[name] = cls +{ :tree, :value, :format_error, :Block, :RootBlock } diff --git a/moonscript/compile/format.lua b/moonscript/compile/format.lua deleted file mode 100644 index db30ef78..00000000 --- a/moonscript/compile/format.lua +++ /dev/null @@ -1,55 +0,0 @@ -module("moonscript.compile", package.seeall) -local util = require("moonscript.util") -local data = require("moonscript.data") -local Set -do - local _table_0 = require("moonscript.data") - Set = _table_0.Set -end -local ntype -do - local _table_0 = require("moonscript.types") - ntype = _table_0.ntype -end -local concat, insert = table.concat, table.insert -indent_char = " " -user_error = function(...) - return error({ - "user-error", - ... - }) -end -moonlib = { - bind = function(tbl, name) - return concat({ - "moon.bind(", - tbl, - ".", - name, - ", ", - tbl, - ")" - }) - end -} -non_atomic = Set({ - "update" -}) -has_value = function(node) - if ntype(node) == "chain" then - local ctype = ntype(node[#node]) - return ctype ~= "call" and ctype ~= "colon" - else - return true - end -end -is_non_atomic = function(node) - return non_atomic[ntype(node)] -end -count_lines = function(str) - local count = 1 - for _ in str:gmatch("\n") do - count = count + 1 - end - return count -end diff --git a/moonscript/compile/format.moon b/moonscript/compile/format.moon deleted file mode 100644 index 218aa675..00000000 --- a/moonscript/compile/format.moon +++ /dev/null @@ -1,44 +0,0 @@ -module "moonscript.compile", package.seeall - -util = require "moonscript.util" -data = require "moonscript.data" - -import Set from require "moonscript.data" -import ntype from require "moonscript.types" -import concat, insert from table - -export indent_char, default_return, moonlib, cascading, non_atomic, has_value, is_non_atomic -export count_lines, user_error - -indent_char = " " - -user_error = (...) -> - error {"user-error", ...} - --- manual_return = Set{"foreach", "for", "while"} --- cascading = Set{ "if", "with" } - --- TODO get RID OF THIAS -moonlib = - bind: (tbl, name) -> - concat {"moon.bind(", tbl, ".", name, ", ", tbl, ")"} - --- an action that can't be completed in a single line -non_atomic = Set{ "update" } - --- does this always return a value -has_value = (node) -> - if ntype(node) == "chain" - ctype = ntype(node[#node]) - ctype != "call" and ctype != "colon" - else - true - -is_non_atomic = (node) -> - non_atomic[ntype(node)] - -count_lines = (str) -> - count = 1 - count += 1 for _ in str\gmatch "\n" - count - diff --git a/moonscript/compile/statement.lua b/moonscript/compile/statement.lua index 45277581..66907ce7 100644 --- a/moonscript/compile/statement.lua +++ b/moonscript/compile/statement.lua @@ -1,25 +1,29 @@ -module("moonscript.compile", package.seeall) -local util = require("moonscript.util") -require("moonscript.compile.format") -local dump = require("moonscript.dump") -local transform = require("moonscript.transform") -local reversed = util.reversed local ntype +ntype = require("moonscript.types").ntype +local concat, insert do - local _table_0 = require("moonscript.types") - ntype = _table_0.ntype + local _obj_0 = table + concat, insert = _obj_0.concat, _obj_0.insert end -local concat, insert = table.concat, table.insert -line_compile = { +local unpack +unpack = require("moonscript.util").unpack +return { raw = function(self, node) return self:add(node[2]) end, - lines = function(self, node) + declare_constants = function(self, node) local _list_0 = node[2] for _index_0 = 1, #_list_0 do - local line = _list_0[_index_0] - self:add_raw(line) + local name = _list_0[_index_0] + if ntype(name) == "ref" then + name = name[2] + end + self:put_name(name, { + const = true, + pos = node[-1] + }) end + return nil end, declare = function(self, node) local names = node[2] @@ -29,12 +33,11 @@ line_compile = { local _with_0 = self:line("local ") _with_0:append_list((function() local _accum_0 = { } - local _len_0 = 0 - local _list_0 = undeclared - for _index_0 = 1, #_list_0 do - local name = _list_0[_index_0] - _len_0 = _len_0 + 1 + local _len_0 = 1 + for _index_0 = 1, #undeclared do + local name = undeclared[_index_0] _accum_0[_len_0] = self:name(name) + _len_0 = _len_0 + 1 end return _accum_0 end)(), ", ") @@ -45,16 +48,16 @@ line_compile = { declare_with_shadows = function(self, node) local names = node[2] self:declare(names) + self:put_fresh_names(names) do local _with_0 = self:line("local ") _with_0:append_list((function() local _accum_0 = { } - local _len_0 = 0 - local _list_0 = names - for _index_0 = 1, #_list_0 do - local name = _list_0[_index_0] - _len_0 = _len_0 + 1 + local _len_0 = 1 + for _index_0 = 1, #names do + local name = names[_index_0] _accum_0[_len_0] = self:name(name) + _len_0 = _len_0 + 1 end return _accum_0 end)(), ", ") @@ -62,7 +65,7 @@ line_compile = { end end, assign = function(self, node) - local _, names, values = unpack(node) + local names, values = unpack(node, 2) local undeclared = self:declare(names) local declare = "local " .. concat(undeclared, ", ") local has_fndef = false @@ -79,16 +82,15 @@ line_compile = { _with_0:append(declare) else if #undeclared > 0 then - self:add(declare) + self:add(declare, node[-1]) end _with_0:append_list((function() local _accum_0 = { } - local _len_0 = 0 - local _list_0 = names - for _index_0 = 1, #_list_0 do - local name = _list_0[_index_0] - _len_0 = _len_0 + 1 + local _len_0 = 1 + for _index_0 = 1, #names do + local name = names[_index_0] _accum_0[_len_0] = self:value(name) + _len_0 = _len_0 + 1 end return _accum_0 end)(), ", ") @@ -96,12 +98,11 @@ line_compile = { _with_0:append(" = ") _with_0:append_list((function() local _accum_0 = { } - local _len_0 = 0 - local _list_0 = values - for _index_0 = 1, #_list_0 do - local v = _list_0[_index_0] - _len_0 = _len_0 + 1 + local _len_0 = 1 + for _index_0 = 1, #values do + local v = values[_index_0] _accum_0[_len_0] = self:value(v) + _len_0 = _len_0 + 1 end return _accum_0 end)(), ", ") @@ -142,9 +143,8 @@ line_compile = { current.next = next current = next end - local _list_0 = node - for _index_0 = 4, #_list_0 do - cond = _list_0[_index_0] + for _index_0 = 4, #node do + local cond = node[_index_0] add_clause(cond) end return root @@ -158,40 +158,22 @@ line_compile = { end end, ["while"] = function(self, node) - local _, cond, block = unpack(node) - local out - if is_non_atomic(cond) then - do - local _with_0 = self:block("while true do") - _with_0:stm({ - "if", - { - "not", - cond - }, - { - { - "break" - } - } - }) - out = _with_0 - end - else - out = self:block(self:line("while ", self:value(cond), " do")) + local cond, block = unpack(node, 2) + do + local _with_0 = self:block(self:line("while ", self:value(cond), " do")) + _with_0:stms(block) + return _with_0 end - out:stms(block) - return out end, ["for"] = function(self, node) - local _, name, bounds, block = unpack(node) + local name, bounds, block = unpack(node, 2) local loop = self:line("for ", self:name(name), " = ", self:value({ "explist", unpack(bounds) }), " do") do local _with_0 = self:block(loop) - _with_0:declare({ + _with_0:put_fresh_names({ name }) _with_0:stms(block) @@ -199,46 +181,44 @@ line_compile = { end end, foreach = function(self, node) - local _, names, exps, block = unpack(node) + local names, exps, block = unpack(node, 2) local loop do local _with_0 = self:line() _with_0:append("for ") - _with_0:append_list((function() + loop = _with_0 + end + do + local _with_0 = self:block(loop) + loop:append_list((function() local _accum_0 = { } - local _len_0 = 0 - local _list_0 = names - for _index_0 = 1, #_list_0 do - local name = _list_0[_index_0] + local _len_0 = 1 + for _index_0 = 1, #names do + local name = names[_index_0] + _accum_0[_len_0] = _with_0:name(name, false) _len_0 = _len_0 + 1 - _accum_0[_len_0] = self:name(name) end return _accum_0 end)(), ", ") - _with_0:append(" in ") - _with_0:append_list((function() + loop:append(" in ") + loop:append_list((function() local _accum_0 = { } - local _len_0 = 0 - local _list_0 = exps - for _index_0 = 1, #_list_0 do - local exp = _list_0[_index_0] - _len_0 = _len_0 + 1 + local _len_0 = 1 + for _index_0 = 1, #exps do + local exp = exps[_index_0] _accum_0[_len_0] = self:value(exp) + _len_0 = _len_0 + 1 end return _accum_0 end)(), ",") - _with_0:append(" do") - loop = _with_0 - end - do - local _with_0 = self:block(loop) - _with_0:declare(names) + loop:append(" do") + _with_0:put_fresh_names(names) _with_0:stms(block) return _with_0 end end, export = function(self, node) - local _, names = unpack(node) + local names = unpack(node, 2) if type(names) == "string" then if names == "*" then self.export_all = true @@ -263,5 +243,6 @@ line_compile = { _with_0:stms(node[2]) return _with_0 end - end + end, + noop = function(self) end } diff --git a/moonscript/compile/statement.moon b/moonscript/compile/statement.moon index 3c20bd1f..8624f9a7 100644 --- a/moonscript/compile/statement.moon +++ b/moonscript/compile/statement.moon @@ -1,23 +1,19 @@ -module "moonscript.compile", package.seeall -util = require "moonscript.util" - -require "moonscript.compile.format" -dump = require "moonscript.dump" -transform = require "moonscript.transform" - -import reversed from util import ntype from require "moonscript.types" import concat, insert from table -export line_compile +import unpack from require "moonscript.util" -line_compile = +{ raw: (node) => @add node[2] - lines: (node) => - for line in *node[2] - @add_raw line + -- tags existing bindings as constant, has no output. only generated by + -- transformations, there is no syntax that produces this statement + declare_constants: (node) => + for name in *node[2] + name = name[2] if ntype(name) == "ref" + @put_name name, const: true, pos: node[-1] + nil declare: (node) => names = node[2] @@ -30,14 +26,16 @@ line_compile = declare_with_shadows: (node) => names = node[2] @declare names + @put_fresh_names names + with @line "local " \append_list [@name name for name in *names], ", " assign: (node) => - _, names, values = unpack node + names, values = unpack node, 2 undeclared = @declare names - declare = "local "..concat(undeclared, ", ") + declare = "local " .. concat(undeclared, ", ") has_fndef = false i = 1 @@ -50,7 +48,7 @@ line_compile = if #undeclared == #names and not has_fndef \append declare else - @add declare if #undeclared > 0 + @add declare, node[-1] if #undeclared > 0 \append_list [@value name for name in *names], ", " \append " = " @@ -91,42 +89,36 @@ line_compile = \stms block while: (node) => - _, cond, block = unpack node - - out = if is_non_atomic cond - with @block "while true do" - \stm {"if", {"not", cond}, {{"break"}}} - else - @block @line "while ", @value(cond), " do" - - out\stms block - out + cond, block = unpack node, 2 + with @block @line "while ", @value(cond), " do" + \stms block for: (node) => - _, name, bounds, block = unpack node + name, bounds, block = unpack node, 2 loop = @line "for ", @name(name), " = ", @value({"explist", unpack bounds}), " do" with @block loop - \declare {name} + \put_fresh_names {name} \stms block -- for x in y ... -- {"foreach", {names...}, {exp...}, body} foreach: (node) => - _, names, exps, block = unpack node + names, exps, block = unpack node, 2 loop = with @line! \append "for " - \append_list [@name name for name in *names], ", " - \append " in " - \append_list [@value exp for exp in *exps], "," - \append " do" with @block loop - \declare names + loop\append_list [\name name, false for name in *names], ", " + loop\append " in " + loop\append_list [@value exp for exp in *exps], "," + loop\append " do" + + \put_fresh_names names \stms block export: (node) => - _, names = unpack node + names = unpack node, 2 if type(names) == "string" if names == "*" @export_all = true @@ -147,3 +139,5 @@ line_compile = with @block! \stms node[2] + noop: => -- nothing! +} diff --git a/moonscript/compile/value.lua b/moonscript/compile/value.lua index 77cb60cb..0a43456c 100644 --- a/moonscript/compile/value.lua +++ b/moonscript/compile/value.lua @@ -1,32 +1,165 @@ -module("moonscript.compile", package.seeall) local util = require("moonscript.util") local data = require("moonscript.data") -require("moonscript.compile.format") local ntype +ntype = require("moonscript.types").ntype +local user_error +user_error = require("moonscript.errors").user_error +local concat, insert do - local _table_0 = require("moonscript.types") - ntype = _table_0.ntype + local _obj_0 = table + concat, insert = _obj_0.concat, _obj_0.insert end -local concat, insert = table.concat, table.insert +local unpack +unpack = util.unpack local table_delim = "," -value_compile = { +local string_chars = { + ["\r"] = "\\r", + ["\n"] = "\\n" +} +local binary_op_prec +do + local out = { } + for prec, ops in ipairs({ + { + "or" + }, + { + "and" + }, + { + "<", + ">", + "<=", + ">=", + "~=", + "!=", + "==" + }, + { + "|" + }, + { + "&" + }, + { + "<<", + ">>" + }, + { + ".." + }, + { + "+", + "-" + }, + { + "*", + "/", + "//", + "%" + }, + { + "^" + } + }) do + for _index_0 = 1, #ops do + local op = ops[_index_0] + out[op] = prec + end + end + binary_op_prec = out +end +local right_assoc_op = { + [".."] = true, + ["^"] = true +} +local exp_precedence +exp_precedence = function(node) + local min_prec + for i = 3, #node, 2 do + do + local prec = binary_op_prec[node[i]] + if prec then + if not min_prec or prec < min_prec then + min_prec = prec + end + end + end + end + return min_prec +end +return { + scoped = function(self, node) + local _, before, value, after + _, before, value, after = node[1], node[2], node[3], node[4] + local _scrap_0 = before and before:call(self) + do + local _with_0 = self:value(value) + local _scrap_1 = after and after:call(self) + return _with_0 + end + end, exp = function(self, node) + local needs_parens + needs_parens = function(value, i) + if not (type(value) == "table" and value[1] == "exp") then + return false + end + local inner = exp_precedence(value) + if not (inner) then + return false + end + if i > 2 then + do + local left = binary_op_prec[node[i - 1]] + if left then + if left > inner then + return true + end + if left == inner and not right_assoc_op[node[i - 1]] then + return true + end + end + end + end + if i < #node then + do + local right = binary_op_prec[node[i + 1]] + if right then + if right > inner then + return true + end + if right == inner and right_assoc_op[node[i + 1]] then + return true + end + end + end + end + return false + end local _comp _comp = function(i, value) if i % 2 == 1 and value == "!=" then value = "~=" end - return self:value(value) + if type(value) == "table" then + value = self.transform.value(value) + end + if needs_parens(value, i) then + return self:line("(", self:value(value), ")") + else + return self:value(value) + end end do local _with_0 = self:line() _with_0:append_list((function() local _accum_0 = { } - local _len_0 = 0 + local _len_0 = 1 for i, v in ipairs(node) do if i > 1 then - _len_0 = _len_0 + 1 _accum_0[_len_0] = _comp(i, v) + _len_0 = _len_0 + 1 end end return _accum_0 @@ -34,22 +167,16 @@ value_compile = { return _with_0 end end, - update = function(self, node) - local _, name = unpack(node) - self:stm(node) - return self:name(name) - end, explist = function(self, node) do local _with_0 = self:line() _with_0:append_list((function() local _accum_0 = { } - local _len_0 = 0 - local _list_0 = node - for _index_0 = 2, #_list_0 do - local v = _list_0[_index_0] - _len_0 = _len_0 + 1 + local _len_0 = 1 + for _index_0 = 2, #node do + local v = node[_index_0] _accum_0[_len_0] = self:value(v) + _len_0 = _len_0 + 1 end return _accum_0 end)(), ", ") @@ -60,21 +187,31 @@ value_compile = { return self:line("(", self:value(node[2]), ")") end, string = function(self, node) - local _, delim, inner = unpack(node) + local delim, inner = unpack(node, 2) local end_delim = delim:gsub("%[", "]") + if delim == "'" or delim == '"' then + inner = inner:gsub("[\r\n]", string_chars) + end return delim .. inner .. end_delim end, chain = function(self, node) local callee = node[2] - if callee == -1 then + local callee_type = ntype(callee) + local item_offset = 3 + if callee_type == "dot" or callee_type == "colon" or callee_type == "index" then callee = self:get("scope_var") - if not callee then - user_error("Short-dot syntax must be called within a with block") + if not (callee) then + user_error("Short-dot syntax must be called within a with block", node[-1]) end + item_offset = 2 end - local sup = self:get("super") - if callee == "super" and sup then - return self:value(sup(self, node)) + if callee_type == "ref" and callee[2] == "super" or callee == "super" then + do + local sup = self:get("super") + if sup then + return self:value(sup(self, node)) + end + end end local chain_item chain_item = function(node) @@ -86,27 +223,24 @@ value_compile = { elseif t == "dot" then return ".", tostring(arg) elseif t == "colon" then - return ":", arg, chain_item(node[3]) - elseif t == "colon_stub" then - return user_error("Uncalled colon stub") + return ":", tostring(arg) else - return error("Unknown chain action: " .. t) + return error("Unknown chain action: " .. tostring(t)) end end - local t = ntype(callee) - if (t == "self" or t == "self_class") and node[3] and ntype(node[3]) == "call" then - callee[1] = t .. "_colon" + if (callee_type == "self" or callee_type == "self_class") and node[3] and ntype(node[3]) == "call" then + callee[1] = callee_type .. "_colon" end local callee_value = self:value(callee) - if ntype(callee) == "exp" then + local _exp_0 = ntype(callee) + if "exp" == _exp_0 or "table" == _exp_0 then callee_value = self:line("(", callee_value, ")") end local actions do local _with_0 = self:line() - local _list_0 = node - for _index_0 = 3, #_list_0 do - local action = _list_0[_index_0] + for _index_0 = item_offset, #node do + local action = node[_index_0] _with_0:append(chain_item(action)) end actions = _with_0 @@ -114,18 +248,21 @@ value_compile = { return self:line(callee_value, actions) end, fndef = function(self, node) - local _, args, whitelist, arrow, block = unpack(node) + local args, whitelist, arrow, block = unpack(node, 2) local default_args = { } local self_args = { } - local arg_names = (function() + local fn_block = self:block() + local arg_names + do local _accum_0 = { } - local _len_0 = 0 - local _list_0 = args - for _index_0 = 1, #_list_0 do - local arg = _list_0[_index_0] + local _len_0 = 1 + for _index_0 = 1, #args do + local arg = args[_index_0] local name, default_value = unpack(arg) if type(name) == "string" then name = name + elseif name[1] == "temp_name" then + name = name:get_name(fn_block, false) else if name[1] == "self" or name[1] == "self_class" then insert(self_args, name) @@ -136,38 +273,42 @@ value_compile = { insert(default_args, arg) end local _value_0 = name - if _value_0 ~= nil then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = _value_0 - end + _accum_0[_len_0] = _value_0 + _len_0 = _len_0 + 1 end - return _accum_0 - end)() + arg_names = _accum_0 + end if arrow == "fat" then insert(arg_names, 1, "self") end do - local _with_0 = self:block() + local _with_0 = fn_block + _with_0.header = "function(" .. concat(arg_names, ", ") .. ")" if #whitelist > 0 then _with_0:whitelist_names(whitelist) end - local _list_0 = arg_names - for _index_0 = 1, #_list_0 do - local name = _list_0[_index_0] + for _index_0 = 1, #arg_names do + local name = arg_names[_index_0] _with_0:put_name(name) end - local _list_1 = default_args - for _index_0 = 1, #_list_1 do - local default = _list_1[_index_0] + for _index_0 = 1, #default_args do + local default = default_args[_index_0] local name, value = unpack(default) if type(name) == "table" then - name = name[2] + if name[1] == "temp_name" then + name = name:get_name(fn_block) + else + name = name[2] + end end _with_0:stm({ 'if', { 'exp', - name, + { + "ref", + name + }, '==', 'nil' }, @@ -184,17 +325,17 @@ value_compile = { } }) end - local self_arg_values = (function() + local self_arg_values + do local _accum_0 = { } - local _len_0 = 0 - local _list_2 = self_args - for _index_0 = 1, #_list_2 do - local arg = _list_2[_index_0] - _len_0 = _len_0 + 1 + local _len_0 = 1 + for _index_0 = 1, #self_args do + local arg = self_args[_index_0] _accum_0[_len_0] = arg[2] + _len_0 = _len_0 + 1 end - return _accum_0 - end)() + self_arg_values = _accum_0 + end if #self_args > 0 then _with_0:stm({ "assign", @@ -203,28 +344,11 @@ value_compile = { }) end _with_0:stms(block) - if #args > #arg_names then - arg_names = (function() - local _accum_0 = { } - local _len_0 = 0 - local _list_2 = args - for _index_0 = 1, #_list_2 do - local arg = _list_2[_index_0] - local _value_0 = arg[1] - if _value_0 ~= nil then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = _value_0 - end - end - return _accum_0 - end)() - end - _with_0.header = "function(" .. concat(arg_names, ", ") .. ")" return _with_0 end end, table = function(self, node) - local _, items = unpack(node) + local items = unpack(node, 2) do local _with_0 = self:block("{", "}") local format_line @@ -244,9 +368,7 @@ value_compile = { else assign = self:line("[", _with_0:value(key), "]") end - _with_0:set("current_block", key) local out = self:line(assign, " = ", _with_0:value(value)) - _with_0:set("current_block", nil) return out else return self:line(_with_0:value(tuple[1])) @@ -268,12 +390,15 @@ value_compile = { minus = function(self, node) return self:line("-", self:value(node[2])) end, - temp_name = function(self, node) - return node:get_name(self) + temp_name = function(self, node, ...) + return node:get_name(self, ...) end, number = function(self, node) return node[2] end, + bitnot = function(self, node) + return self:line("~", self:value(node[2])) + end, length = function(self, node) return self:line("#", self:value(node[2])) end, @@ -281,25 +406,63 @@ value_compile = { return self:line("not ", self:value(node[2])) end, self = function(self, node) - return "self." .. self:value(node[2]) + local field_name = self:name(node[2]) + if data.lua_keywords[field_name] then + return self:value({ + "chain", + "self", + { + "index", + { + "string", + '"', + field_name + } + } + }) + else + return "self." .. tostring(field_name) + end end, self_class = function(self, node) - return "self.__class." .. self:value(node[2]) + local field_name = self:name(node[2]) + if data.lua_keywords[field_name] then + return self:value({ + "chain", + "self", + { + "dot", + "__class" + }, + { + "index", + { + "string", + '"', + field_name + } + } + }) + else + return "self.__class." .. tostring(field_name) + end end, self_colon = function(self, node) - return "self:" .. self:value(node[2]) + return "self:" .. tostring(self:name(node[2])) end, self_class_colon = function(self, node) - return "self.__class:" .. self:value(node[2]) + return "self.__class:" .. tostring(self:name(node[2])) end, - raw_value = function(self, value) - local sup = self:get("super") - if value == "super" and sup then - return self:value(sup(self)) - end - if value == "..." then - self:send("varargs") + ref = function(self, value) + do + local sup = value[2] == "super" and self:get("super") + if sup then + return self:value(sup(self)) + end end + return tostring(value[2]) + end, + raw_value = function(self, value) return tostring(value) end } diff --git a/moonscript/compile/value.moon b/moonscript/compile/value.moon index 8c578124..98715514 100644 --- a/moonscript/compile/value.moon +++ b/moonscript/compile/value.moon @@ -1,35 +1,99 @@ -module "moonscript.compile", package.seeall - util = require "moonscript.util" data = require "moonscript.data" -require "moonscript.compile.format" - import ntype from require "moonscript.types" +import user_error from require "moonscript.errors" import concat, insert from table - -export value_compile +import unpack from util table_delim = "," -value_compile = +string_chars = { + "\r": "\\r" + "\n": "\\n" +} + +-- lua binary operator precedence, from loosest to tightest binding +binary_op_prec = do + out = {} + for prec, ops in ipairs { + {"or"} + {"and"} + {"<", ">", "<=", ">=", "~=", "!=", "=="} + {"|"} + {"&"} + {"<<", ">>"} + {".."} + {"+", "-"} + {"*", "/", "//", "%"} + {"^"} + } + out[op] = prec for op in *ops + out + +right_assoc_op = { + "..": true + "^": true +} + +-- the loosest binding operator in a flat exp node, nil if there are none +exp_precedence = (node) -> + local min_prec + for i=3, #node, 2 + if prec = binary_op_prec[node[i]] + min_prec = prec if not min_prec or prec < min_prec + min_prec + +{ + scoped: (node) => + {_, before, value, after} = node + before and before\call @ + with @value value + after and after\call @ + -- list of values separated by binary operators exp: (node) => + -- exp nodes nested by transformations (eg. string interpolation) must + -- keep their grouping if an adjacent operator binds tighter than one of + -- their own operators + needs_parens = (value, i) -> + return false unless type(value) == "table" and value[1] == "exp" + inner = exp_precedence value + return false unless inner + + if i > 2 + if left = binary_op_prec[node[i - 1]] + return true if left > inner + return true if left == inner and not right_assoc_op[node[i - 1]] + + if i < #node + if right = binary_op_prec[node[i + 1]] + return true if right > inner + -- equal precedence on the right regroups under a right associative + -- operator: an exp holding a .. b rendered flat as a .. b .. c + -- evaluates as a .. (b .. c), observable through __concat + return true if right == inner and right_assoc_op[node[i + 1]] + + false + _comp = (i, value) -> if i % 2 == 1 and value == "!=" value = "~=" - @value value + + -- transform now so nested exps (eg. from string interpolation) are + -- visible to the parenthesization check + if type(value) == "table" + value = @transform.value value + + if needs_parens value, i + @line "(", @value(value), ")" + else + @value value with @line! \append_list [_comp i,v for i,v in ipairs node when i > 1], " " - -- TODO refactor - update: (node) => - _, name = unpack node - @stm node - @name name - -- list of expressions separated by commas explist: (node) => with @line! @@ -39,20 +103,28 @@ value_compile = @line "(", @value(node[2]), ")" string: (node) => - _, delim, inner = unpack node + delim, inner = unpack node, 2 end_delim = delim\gsub "%[", "]" + if delim == "'" or delim == '"' + inner = inner\gsub "[\r\n]", string_chars + delim..inner..end_delim chain: (node) => callee = node[2] + callee_type = ntype callee + item_offset = 3 - if callee == -1 + if callee_type == "dot" or callee_type == "colon" or callee_type == "index" callee = @get "scope_var" - if not callee then user_error "Short-dot syntax must be called within a with block" + unless callee + user_error "Short-dot syntax must be called within a with block", node[-1] + item_offset = 2 - sup = @get "super" - if callee == "super" and sup - return @value sup self, node + -- TODO: don't use string literals as ref + if callee_type == "ref" and callee[2] == "super" or callee == "super" + if sup = @get "super" + return @value sup self, node chain_item = (node) -> t, arg = unpack node @@ -64,33 +136,41 @@ value_compile = elseif t == "dot" ".", tostring arg elseif t == "colon" - ":", arg, chain_item(node[3]) - elseif t == "colon_stub" - user_error "Uncalled colon stub" + ":", tostring arg else - error "Unknown chain action: "..t + error "Unknown chain action: #{t}" - t = ntype(callee) - if (t == "self" or t == "self_class") and node[3] and ntype(node[3]) == "call" - callee[1] = t.."_colon" + if (callee_type == "self" or callee_type == "self_class") and node[3] and ntype(node[3]) == "call" + callee[1] = callee_type.."_colon" callee_value = @value callee - callee_value = @line "(", callee_value, ")" if ntype(callee) == "exp" + + -- expressions and table literals can't be subscripted directly in lua + switch ntype callee + when "exp", "table" + callee_value = @line "(", callee_value, ")" actions = with @line! - \append chain_item action for action in *node[3,] + \append chain_item action for action in *node[item_offset,] @line callee_value, actions fndef: (node) => - _, args, whitelist, arrow, block = unpack node + args, whitelist, arrow, block = unpack node, 2 default_args = {} self_args = {} + + fn_block = @block! + arg_names = for arg in *args name, default_value = unpack arg name = if type(name) == "string" name + elseif name[1] == "temp_name" + -- a destructuring arg holds a proxy. put the resolved name into + -- scope immediately or a second proxy would pick the same free name + name\get_name fn_block, false else if name[1] == "self" or name[1] == "self_class" insert self_args, name @@ -101,7 +181,9 @@ value_compile = if arrow == "fat" insert arg_names, 1, "self" - with @block! + with fn_block + .header = "function("..concat(arg_names, ", ")..")" + if #whitelist > 0 \whitelist_names whitelist @@ -109,9 +191,13 @@ value_compile = for default in *default_args name, value = unpack default - name = name[2] if type(name) == "table" + if type(name) == "table" + name = if name[1] == "temp_name" + name\get_name fn_block + else + name[2] \stm { - 'if', {'exp', name, '==', 'nil'}, { + 'if', {'exp', {"ref", name}, '==', 'nil'}, { {'assign', {name}, {value}} } } @@ -121,15 +207,8 @@ value_compile = \stms block - -- inject more args if the block manipulated arguments - if #args > #arg_names -- will only work for simple adjustments - arg_names = for arg in *args - arg[1] - - .header = "function("..concat(arg_names, ", ")..")" - table: (node) => - _, items = unpack node + items = unpack node, 2 with @block "{", "}" format_line = (tuple) -> if #tuple == 2 @@ -144,9 +223,7 @@ value_compile = else @line "[", \value(key), "]" - \set "current_block", key out = @line assign, " = ", \value(value) - \set "current_block", nil out else @line \value tuple[1] @@ -161,12 +238,15 @@ value_compile = minus: (node) => @line "-", @value node[2] - temp_name: (node) => - node\get_name self + temp_name: (node, ...) => + node\get_name self, ... number: (node) => node[2] + bitnot: (node) => + @line "~", @value node[2] + length: (node) => @line "#", @value node[2] @@ -174,24 +254,38 @@ value_compile = @line "not ", @value node[2] self: (node) => - "self."..@value node[2] + field_name = @name node[2] + if data.lua_keywords[field_name] + @value {"chain", "self", {"index", { + "string", '"', field_name + }}} + else + "self.#{field_name}" self_class: (node) => - "self.__class."..@value node[2] + field_name = @name node[2] + + if data.lua_keywords[field_name] + @value {"chain", "self", {"dot", "__class"}, {"index", { + "string", '"', field_name + }}} + else + "self.__class.#{field_name}" self_colon: (node) => - "self:"..@value node[2] + "self:#{@name node[2]}" self_class_colon: (node) => - "self.__class:"..@value node[2] + "self.__class:#{@name node[2]}" - -- catch all pure string values - raw_value: (value) => - sup = @get"super" - if value == "super" and sup - return @value sup self + -- a variable reference + ref: (value) => + if sup = value[2] == "super" and @get "super" + return @value sup @ - if value == "..." - @send "varargs" + tostring value[2] + -- catch all pure string values + raw_value: (value) => tostring value +} diff --git a/moonscript/data.lua b/moonscript/data.lua index 81fc3c7d..f33f708d 100644 --- a/moonscript/data.lua +++ b/moonscript/data.lua @@ -1,58 +1,49 @@ -module("moonscript.data", package.seeall) -local concat = table.concat +local concat, remove, insert +do + local _obj_0 = table + concat, remove, insert = _obj_0.concat, _obj_0.remove, _obj_0.insert +end +local Set Set = function(items) - local self = { } - local _list_0 = items - for _index_0 = 1, #_list_0 do - local key = _list_0[_index_0] - self[key] = true + local _tbl_0 = { } + for _index_0 = 1, #items do + local k = items[_index_0] + _tbl_0[k] = true end - return self + return _tbl_0 end -Stack = (function() - local _parent_0 = nil +local Stack +do + local _class_0 local _base_0 = { __tostring = function(self) return "" end, pop = function(self) - return table.remove(self) + return remove(self) end, - push = function(self, value) - table.insert(self, value) - return value + push = function(self, value, ...) + insert(self, value) + if ... then + return self:push(...) + else + return value + end end, top = function(self) return self[#self] end } _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ + _class_0 = setmetatable({ __init = function(self, ...) - local _list_0 = { - ... - } - for _index_0 = 1, #_list_0 do - local v = _list_0[_index_0] - self:push(v) - end + self:push(...) return nil end, __base = _base_0, - __name = "Stack", - __parent = _parent_0 + __name = "Stack" }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, + __index = _base_0, __call = function(cls, ...) local _self_0 = setmetatable({}, _base_0) cls.__init(_self_0, ...) @@ -60,12 +51,9 @@ Stack = (function() end }) _base_0.__class = _class_0 - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -lua_keywords = Set({ + Stack = _class_0 +end +local lua_keywords = Set({ 'and', 'break', 'do', @@ -88,3 +76,8 @@ lua_keywords = Set({ 'until', 'while' }) +return { + Set = Set, + Stack = Stack, + lua_keywords = lua_keywords +} diff --git a/moonscript/data.moon b/moonscript/data.moon index 6b9050f5..4bb9b4fb 100644 --- a/moonscript/data.moon +++ b/moonscript/data.moon @@ -1,35 +1,31 @@ -- data structures & static data -module "moonscript.data", package.seeall -export Set, Stack -export lua_keywords +import concat, remove, insert from table -import concat from table - -Set = (items) -> - self = {} - self[key] = true for key in *items - self +Set = (items) -> {k,true for k in *items} class Stack __tostring: => "" new: (...) => - @push v for v in *{...} + @push ... nil pop: => - table.remove self + remove @ - push: (value) => - table.insert self, value - value + push: (value, ...) => + insert @, value + if ... + @push ... + else + value top: => self[#self] -lua_keywords = Set{ +lua_keywords = Set { 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 'function', 'if', 'in', 'local', 'nil', 'not', 'or', @@ -37,3 +33,5 @@ lua_keywords = Set{ 'until', 'while' } +{ :Set, :Stack, :lua_keywords } + diff --git a/moonscript/dump.lua b/moonscript/dump.lua index 7b7fbc0c..26cdd7a3 100644 --- a/moonscript/dump.lua +++ b/moonscript/dump.lua @@ -1,4 +1,3 @@ -module("moonscript.dump", package.seeall) local flat_value flat_value = function(op, depth) if depth == nil then @@ -10,27 +9,38 @@ flat_value = function(op, depth) if type(op) ~= "table" then return tostring(op) end - local items = (function() + local items + do local _accum_0 = { } - local _len_0 = 0 - local _list_0 = op - for _index_0 = 1, #_list_0 do - local item = _list_0[_index_0] - _len_0 = _len_0 + 1 + local _len_0 = 1 + for _index_0 = 1, #op do + local item = op[_index_0] _accum_0[_len_0] = flat_value(item, depth + 1) + _len_0 = _len_0 + 1 end - return _accum_0 - end)() + items = _accum_0 + end local pos = op[-1] return "{" .. (pos and "[" .. pos .. "] " or "") .. table.concat(items, ", ") .. "}" end +local value value = function(op) return flat_value(op) end +local tree tree = function(block) - local _list_0 = block - for _index_0 = 1, #_list_0 do - value = _list_0[_index_0] - print(flat_value(value)) - end + return table.concat((function() + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #block do + local value = block[_index_0] + _accum_0[_len_0] = flat_value(value) + _len_0 = _len_0 + 1 + end + return _accum_0 + end)(), "\n") end +return { + value = value, + tree = tree +} diff --git a/moonscript/dump.moon b/moonscript/dump.moon index 077fc96b..6fbeb375 100644 --- a/moonscript/dump.moon +++ b/moonscript/dump.moon @@ -1,8 +1,4 @@ -module "moonscript.dump", package.seeall - -export value, tree - flat_value = (op, depth=1) -> return '"'..op..'"' if type(op) == "string" return tostring(op) if type(op) != "table" @@ -16,4 +12,7 @@ value = (op) -> flat_value op tree = (block) -> - print flat_value value for value in *block + table.concat [flat_value value for value in *block], "\n" + +{ :value, :tree } + diff --git a/moonscript/errors.lua b/moonscript/errors.lua index cd2992e2..d85c3162 100644 --- a/moonscript/errors.lua +++ b/moonscript/errors.lua @@ -1,14 +1,24 @@ -module("moonscript.errors", package.seeall) -local moon = require("moonscript") local util = require("moonscript.util") -require("lpeg") -local concat, insert = table.concat, table.insert -local split, pos_to_line = util.split, util.pos_to_line +local lpeg = require("lpeg") +local concat, insert +do + local _obj_0 = table + concat, insert = _obj_0.concat, _obj_0.insert +end +local split, pos_to_line +split, pos_to_line = util.split, util.pos_to_line +local user_error +user_error = function(...) + return error({ + "user-error", + ... + }) +end local lookup_line lookup_line = function(fname, pos, cache) if not cache[fname] then do - local _with_0 = io.open(fname) + local _with_0 = assert(io.open(fname)) cache[fname] = _with_0:read("*a") _with_0:close() end @@ -24,6 +34,7 @@ reverse_line_number = function(fname, line_table, line_num, cache) end return "unknown" end +local truncate_traceback truncate_traceback = function(traceback, chunk_func) if chunk_func == nil then chunk_func = "moonscript_chunk" @@ -36,25 +47,26 @@ truncate_traceback = function(traceback, chunk_func) end stop = stop - 1 end - traceback = (function() + do local _accum_0 = { } - local _len_0 = 0 - local _list_0 = traceback + local _len_0 = 1 local _max_0 = stop - for _index_0 = 1, _max_0 < 0 and #_list_0 + _max_0 or _max_0 do - local t = _list_0[_index_0] - _len_0 = _len_0 + 1 + for _index_0 = 1, _max_0 < 0 and #traceback + _max_0 or _max_0 do + local t = traceback[_index_0] _accum_0[_len_0] = t + _len_0 = _len_0 + 1 end - return _accum_0 - end)() + traceback = _accum_0 + end local rep = "function '" .. chunk_func .. "'" traceback[#traceback] = traceback[#traceback]:gsub(rep, "main chunk") return concat(traceback, "\n") end +local rewrite_traceback rewrite_traceback = function(text, err) - local line_tables = moon.line_tables - local V, S, Ct, C = lpeg.V, lpeg.S, lpeg.Ct, lpeg.C + local line_tables = require("moonscript.line_tables") + local V, S, Ct, C + V, S, Ct, C = lpeg.V, lpeg.S, lpeg.Ct, lpeg.C local header_text = "stack traceback:" local Header, Line = V("Header"), V("Line") local Break = lpeg.S("\n") @@ -66,14 +78,17 @@ rewrite_traceback = function(text, err) local cache = { } local rewrite_single rewrite_single = function(trace) - local fname, line, msg = trace:match('^%[string "(.-)"]:(%d+): (.*)$') - local tbl = line_tables[fname] + local fname, line, msg = trace:match('^(.-):(%d+): (.*)$') + local tbl = line_tables["@" .. tostring(fname)] if fname and tbl then return concat({ fname, ":", reverse_line_number(fname, tbl, line, cache), ": ", + "(", + line, + ") ", msg }) else @@ -82,12 +97,21 @@ rewrite_traceback = function(text, err) end err = rewrite_single(err) local match = g:match(text) + if not (match) then + return nil + end for i, trace in ipairs(match) do match[i] = rewrite_single(trace) end return concat({ - "moon:" .. err, + "moon: " .. err, header_text, "\t" .. concat(match, "\n\t") }, "\n") end +return { + rewrite_traceback = rewrite_traceback, + truncate_traceback = truncate_traceback, + user_error = user_error, + reverse_line_number = reverse_line_number +} diff --git a/moonscript/errors.moon b/moonscript/errors.moon index f6ba3e56..b04c5671 100644 --- a/moonscript/errors.moon +++ b/moonscript/errors.moon @@ -1,19 +1,18 @@ -module "moonscript.errors", package.seeall -moon = require "moonscript" util = require "moonscript.util" -require "lpeg" +lpeg = require "lpeg" import concat, insert from table import split, pos_to_line from util -export rewrite_traceback, truncate_traceback +user_error = (...) -> + error {"user-error", ...} -- find the line number of `pos` chars into fname lookup_line = (fname, pos, cache) -> if not cache[fname] - with io.open fname + with assert io.open(fname) cache[fname] = \read "*a" \close! pos_to_line cache[fname], pos @@ -43,7 +42,7 @@ truncate_traceback = (traceback, chunk_func="moonscript_chunk") -> concat traceback, "\n" rewrite_traceback = (text, err) -> - line_tables = moon.line_tables + line_tables = require "moonscript.line_tables" import V, S, Ct, C from lpeg header_text = "stack traceback:" @@ -57,25 +56,33 @@ rewrite_traceback = (text, err) -> cache = {} -- loaded file cache rewrite_single = (trace) -> - fname, line, msg = trace\match '^%[string "(.-)"]:(%d+): (.*)$' - tbl = line_tables[fname] + fname, line, msg = trace\match '^(.-):(%d+): (.*)$' + tbl = line_tables["@#{fname}"] if fname and tbl concat { fname, ":" reverse_line_number(fname, tbl, line, cache) - ": ", msg + ": " + "(", line, ") " + msg } else trace err = rewrite_single err match = g\match text + + return nil unless match + for i, trace in ipairs match match[i] = rewrite_single trace concat { - "moon:" .. err + "moon: " .. err header_text "\t" .. concat match, "\n\t" }, "\n" + +{ :rewrite_traceback, :truncate_traceback, :user_error, :reverse_line_number } + diff --git a/moonscript/init.lua b/moonscript/init.lua index 51a1e047..6de5f120 100644 --- a/moonscript/init.lua +++ b/moonscript/init.lua @@ -1,92 +1,5 @@ -module("moonscript", package.seeall) -require("moonscript.compile") -require("moonscript.parse") -require("moonscript.util") -local concat, insert = table.concat, table.insert -local split, dump = util.split, util.dump -local lua = { - loadstring = loadstring -} -dirsep = "/" -line_tables = { } -local create_moonpath -create_moonpath = function(package_path) - local paths = split(package_path, ";") - for i, path in ipairs(paths) do - local p = path:match("^(.-)%.lua$") - if p then - paths[i] = p .. ".moon" - end - end - return concat(paths, ";") -end -to_lua = function(text) - if "string" ~= type(text) then - local t = type(text) - error("expecting string (got " .. t .. ")", 2) - end - local tree, err = parse.string(text) - if not tree then - error(err, 2) - end - local code, ltable, pos = compile.tree(tree) - if not code then - error(compile.format_error(ltable, pos, text), 2) - end - return code, ltable -end -moon_loader = function(name) - local name_path = name:gsub("%.", dirsep) - local file, file_path = nil, nil - local _list_0 = split(package.moonpath, ";") - for _index_0 = 1, #_list_0 do - local path = _list_0[_index_0] - file_path = path:gsub("?", name_path) - file = io.open(file_path) - if file then - break - end - end - if file then - local text = file:read("*a") - file:close() - return loadstring(text, file_path) - else - return nil, "Could not find moon file" - end -end -if not package.moonpath then - package.moonpath = create_moonpath(package.path) -end -local init_loader -init_loader = function() - return insert(package.loaders, 2, moon_loader) -end -if not _G.moon_no_loader then - init_loader() -end -loadstring = function(str, chunk_name) - local passed, code, ltable = pcall(function() - return to_lua(str) - end) - if not passed then - error(chunk_name .. ": " .. code, 2) - end - if chunk_name then - line_tables[chunk_name] = ltable - end - return lua.loadstring(code, chunk_name or "=(moonscript.loadstring)") -end -loadfile = function(fname) - local file, err = io.open(fname) - if not file then - return nil, err - end - local text = assert(file:read("*a")) - file:close() - return loadstring(text, fname) -end -dofile = function(fname) - local f = assert(loadfile(fname)) - return f() +do + local _with_0 = require("moonscript.base") + _with_0.insert_loader() + return _with_0 end diff --git a/moonscript/init.moon b/moonscript/init.moon index b0593870..396686d6 100644 --- a/moonscript/init.moon +++ b/moonscript/init.moon @@ -1,85 +1,2 @@ - -module "moonscript", package.seeall - -require "moonscript.compile" -require "moonscript.parse" -require "moonscript.util" - -import concat, insert from table -import split, dump from util - -lua = :loadstring - -export to_lua, moon_chunk, moon_loader, dirsep, line_tables -export dofile, loadfile, loadstring - -dirsep = "/" -line_tables = {} - --- create moon path package from lua package path -create_moonpath = (package_path) -> - paths = split package_path, ";" - for i, path in ipairs paths - p = path\match "^(.-)%.lua$" - if p then paths[i] = p..".moon" - concat paths, ";" - -to_lua = (text) -> - if "string" != type text - t = type text - error "expecting string (got ".. t ..")", 2 - - tree, err = parse.string text - if not tree - error err, 2 - - code, ltable, pos = compile.tree tree - if not code - error compile.format_error(ltable, pos, text), 2 - - code, ltable - -moon_loader = (name) -> - name_path = name\gsub "%.", dirsep - - file, file_path = nil, nil - for path in *split package.moonpath, ";" - file_path = path\gsub "?", name_path - file = io.open file_path - break if file - - if file - text = file\read "*a" - file\close! - loadstring text, file_path - else - nil, "Could not find moon file" - -if not package.moonpath - package.moonpath = create_moonpath package.path - -init_loader = -> - insert package.loaders, 2, moon_loader - -init_loader! if not _G.moon_no_loader - -loadstring = (str, chunk_name) -> - passed, code, ltable = pcall -> to_lua str - if not passed - error chunk_name .. ": " .. code, 2 - - line_tables[chunk_name] = ltable if chunk_name - lua.loadstring code, chunk_name or "=(moonscript.loadstring)" - -loadfile = (fname) -> - file, err = io.open fname - return nil, err if not file - text = assert file\read "*a" - file\close! - loadstring text, fname - --- throws errros -dofile = (fname) -> - f = assert loadfile fname - f! - +with require "moonscript.base" + .insert_loader! diff --git a/moonscript/line_tables.lua b/moonscript/line_tables.lua new file mode 100644 index 00000000..61388ecb --- /dev/null +++ b/moonscript/line_tables.lua @@ -0,0 +1 @@ +return { } diff --git a/moonscript/line_tables.moon b/moonscript/line_tables.moon new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/moonscript/line_tables.moon @@ -0,0 +1 @@ +{} diff --git a/moonscript/parse.lua b/moonscript/parse.lua index 7bd102ed..82972cc4 100644 --- a/moonscript/parse.lua +++ b/moonscript/parse.lua @@ -1,585 +1,26 @@ -module("moonscript.parse", package.seeall) - -local util = require"moonscript.util" - -require"lpeg" - -local debug_grammar = false - -local data = require"moonscript.data" -local types = require"moonscript.types" - -local ntype = types.ntype - -local dump = util.dump -local trim = util.trim - -local Stack = data.Stack - -local function count_indent(str) - local sum = 0 - for v in str:gmatch("[\t ]") do - if v == ' ' then sum = sum + 1 end - if v == '\t' then sum = sum + 4 end - end - return sum -end - -local R, S, V, P = lpeg.R, lpeg.S, lpeg.V, lpeg.P -local C, Ct, Cmt, Cg, Cb, Cc = lpeg.C, lpeg.Ct, lpeg.Cmt, lpeg.Cg, lpeg.Cb, lpeg.Cc - -lpeg.setmaxstack(10000) - -local White = S" \t\r\n"^0 -local _Space = S" \t"^0 -local Break = P"\r"^-1 * P"\n" -local Stop = Break + -1 -local Indent = C(S"\t "^0) / count_indent - -local Comment = P"--" * (1 - S"\r\n")^0 * #Stop -local Space = _Space * Comment^-1 -local SomeSpace = S" \t"^1 * Comment^-1 - -local SpaceBreak = Space * Break -local EmptyLine = SpaceBreak - -local AlphaNum = R("az", "AZ", "09", "__") - -local _Name = C(R("az", "AZ", "__") * AlphaNum^0) -local Name = Space * _Name - -local Num = P"0x" * R("09", "af", "AF")^1 + - R"09"^1 * (P"." * R"09"^1)^-1 * (S"eE" * P"-"^-1 * R"09"^1)^-1 - -Num = Space * (Num / function(value) return {"number", value} end) - -local FactorOp = Space * C(S"+-") -local TermOp = Space * C(S"*/%^") - -local Shebang = P"#!" * P(1 - Stop)^0 - --- can't have P(false) because it causes preceding patterns not to run -local Cut = P(function() return false end) - -local function ensure(patt, finally) - return patt * finally + finally * Cut -end - --- auto declare Proper variables with lpeg.V -local function wrap_env(fn) - local env = getfenv(fn) - local wrap_name = V - - if debug_grammar then - local indent = 0 - local indent_char = " " - - local function iprint(...) - local args = {...} - for i=1,#args do - args[i] = tostring(args[i]) - end - - io.stdout:write(indent_char:rep(indent) .. table.concat(args, ", ") .. "\n") - end - - wrap_name = function(name) - local v = V(name) - v = Cmt("", function() - iprint("* " .. name) - indent = indent + 1 - return true - end) * Cmt(v, function(str, pos, ...) - iprint(name, true) - indent = indent - 1 - return true, ... - end) + Cmt("", function() - iprint(name, false) - indent = indent - 1 - return false - end) - return v - end - end - - return setfenv(fn, setmetatable({}, { - __index = function(self, name) - local value = env[name] - if value ~= nil then return value end - - if name:match"^[A-Z][A-Za-z0-9]*$" then - local v = wrap_name(name) - rawset(self, name, v) - return v - end - error("unknown variable referenced: "..name) - end - })) -end - -function extract_line(str, start_pos) - str = str:sub(start_pos) - m = str:match"^(.-)\n" - if m then return m end - return str:match"^.-$" -end - -local function mark(name) - return function(...) - return {name, ...} - end -end - -local function insert_pos(pos, value) - if type(value) == "table" then - value[-1] = pos +local errors = require("moonscript.parse.errors") +local parser = require("moonscript.parse.native") +return { + string = function(str) + local ok, result, label, err_pos = pcall(parser.parse, str) + if not (ok) then + if type(result) == "table" then + local node, msg + node, msg = result[1], result[2] + local node_pos = type(node) == "table" and node[-1] + if node_pos then + return nil, errors.format(str, node_pos, msg) + end + return nil, "failed to parse: " .. tostring(msg) + end + return nil, tostring(result) end - return value -end - -local function pos(patt) - return (lpeg.Cp() * patt) / insert_pos -end - -local function got(what) - return Cmt("", function(str, pos, ...) - local cap = {...} - print("++ got "..what, "["..extract_line(str, pos).."]") - return true - end) -end - -local function flatten(tbl) - if #tbl == 1 then - return tbl[1] - end - return tbl -end - -local function flatten_or_mark(name) - return function(tbl) - if #tbl == 1 then return tbl[1] end - table.insert(tbl, 1, name) - return tbl - end -end - --- makes sure the last item in a chain is an index -local _assignable = { index = true, dot = true, slice = true } -local function check_assignable(str, pos, value) - if ntype(value) == "chain" and _assignable[ntype(value[#value])] - or type(value) == "string" - then - return true, value - end - return false -end - -local flatten_explist = flatten_or_mark"explist" -local function format_assign(lhs_exps, assign) - if not assign then - return flatten_explist(lhs_exps) - end - - local t = ntype(assign) - if t == "assign" then - return {"assign", lhs_exps, unpack(assign, 2)} - elseif t == "update" then - return {"update", lhs_exps[1], unpack(assign, 2)} - end - - error "unknown assign expression" -end - --- the if statement only takes a single lhs, so we wrap in table to git to --- "assign" tuple format -local function format_assign_for_if(lhs, assign) - if assign then - return format_assign({lhs}, assign) - end - return lhs -end - -local function sym(chars) - return Space * chars -end - -local function symx(chars) - return chars -end - -local function simple_string(delim, allow_interpolation) - local inner = P('\\'..delim) + "\\\\" + (1 - S('\r\n'..delim)) - if allow_interpolation then - inter = symx"#{" * V"Exp" * sym"}" - inner = (C((inner - inter)^1) + inter / mark"interpolate")^0 - else - inner = C(inner^0) - end - - return C(symx(delim)) * - inner * sym(delim) / mark"string" -end - -local function wrap_func_arg(value) - return {"call", {value}} -end - --- DOCME -local function flatten_func(callee, args) - if #args == 0 then return callee end - - args = {"call", args} - if ntype(callee) == "chain" then - -- check for colon stub that needs arguments - if ntype(callee[#callee]) == "colon_stub" then - local stub = callee[#callee] - stub[1] = "colon" - table.insert(stub, args) - else - table.insert(callee, args) - end - - return callee - end - - return {"chain", callee, args} -end - -local function flatten_string_chain(str, chain, args) - if not chain then return str end - return flatten_func({"chain", str, unpack(chain)}, args) -end - --- transforms a statement that has a line decorator -local function wrap_decorator(stm, dec) - if not dec then return stm end - return { "decorated", stm, dec } -end - --- wrap if statement if there is a conditional decorator -local function wrap_if(stm, cond) - if cond then - local pass, fail = unpack(cond) - if fail then fail = {"else", {fail}} end - return {"if", cond[2], {stm}, fail} - end - return stm -end - -local function check_lua_string(str, pos, right, left) - return #left == #right -end - --- :name in table literal -local function self_assign(name) - return {{"key_literal", name}, name} -end - -local err_msg = "Failed to parse:\n [%d] >> %s (%d)" - -local build_grammar = wrap_env(function() - local _indent = Stack(0) -- current indent - - local last_pos = 0 -- used to know where to report error - local function check_indent(str, pos, indent) - last_pos = pos - return _indent:top() == indent - end - - local function advance_indent(str, pos, indent) - local top = _indent:top() - if top ~= -1 and indent > _indent:top() then - _indent:push(indent) - return true - end - end - - local function push_indent(str, pos, indent) - _indent:push(indent) - return true - end - - local function pop_indent(str, pos) - if not _indent:pop() then error("unexpected outdent") end - return true - end - - local keywords = {} - local function key(chars) - keywords[chars] = true - return Space * chars * -AlphaNum - end - - local function op(word) - local patt = Space * C(word) - if word:match("^%w*$") then - keywords[word] = true - patt = patt * -AlphaNum - end - return patt - end - - -- make sure name is not a keyword - local Name = Cmt(Name, function(str, pos, name) - if keywords[name] then return false end - return true - end) / trim - - local SelfName = Space * "@" * ( - "@" * (_Name / mark"self_class" + Cc"self.__class") + - _Name / mark"self" + Cc"self") - - local KeyName = SelfName + Space * _Name / mark"key_literal" - - local Name = SelfName + Name + Space * "..." / trim - - local g = lpeg.P{ - File, - File = Shebang^-1 * (Block + Ct""), - Block = Ct(Line * (Break^1 * Line)^0), - CheckIndent = Cmt(Indent, check_indent), -- validates line is in correct indent - Line = (CheckIndent * Statement + Space * #Stop), - - Statement = ( - Import + While + With + For + ForEach + Switch + Return + ClassDecl + - Local + Export + BreakLoop + - Ct(ExpList) * (Update + Assign)^-1 / format_assign - ) * Space * (( - -- statement decorators - key"if" * Exp * (key"else" * Exp)^-1 * Space / mark"if" + - key"unless" * Exp / mark"unless" + - CompInner / mark"comprehension" - ) * Space)^-1 / wrap_decorator, - - Body = Space^-1 * Break * EmptyLine^0 * InBlock + Ct(Statement), -- either a statement, or an indented block - - Advance = #Cmt(Indent, advance_indent), -- Advances the indent, gives back whitespace for CheckIndent - PushIndent = Cmt(Indent, push_indent), - PreventIndent = Cmt(Cc(-1), push_indent), - PopIndent = Cmt("", pop_indent), - InBlock = Advance * Block * PopIndent, - - Local = key"local" * Ct(NameList) / mark"declare_with_shadows", - - Import = key"import" * Ct(ImportNameList) * key"from" * Exp / mark"import", - ImportName = (sym"\\" * Ct(Cc"colon_stub" * Name) + Name), - ImportNameList = ImportName * (sym"," * ImportName)^0, - - NameList = Name * (sym"," * Name)^0, - - BreakLoop = Ct(key"break"/trim) + Ct(key"continue"/trim), - - Return = key"return" * (ExpListLow/mark"explist" + C"") / mark"return", - - With = key"with" * Exp * key"do"^-1 * Body / mark"with", - - Switch = key"switch" * Exp * key"do"^-1 * Space^-1 * Break * SwitchBlock / mark"switch", - - SwitchBlock = EmptyLine^0 * Advance * Ct(SwitchCase * (Break^1 * SwitchCase)^0 * (Break^1 * SwitchElse)^-1) * PopIndent, - SwitchCase = key"when" * Exp * key"then"^-1 * Body / mark"case", - SwitchElse = key"else" * Body / mark"else", - - IfCond = Exp * Assign^-1 / format_assign_for_if, - - If = key"if" * IfCond * key"then"^-1 * Body * - ((Break * CheckIndent)^-1 * EmptyLine^0 * key"elseif" * IfCond * key"then"^-1 * Body / mark"elseif")^0 * - ((Break * CheckIndent)^-1 * EmptyLine^0 * key"else" * Body / mark"else")^-1 / mark"if", - - Unless = key"unless" * IfCond * key"then"^-1 * Body * - ((Break * CheckIndent)^-1 * EmptyLine^0 * key"else" * Body / mark"else")^-1 / mark"unless", - - While = key"while" * Exp * key"do"^-1 * Body / mark"while", - - For = key"for" * (Name * sym"=" * Ct(Exp * sym"," * Exp * (sym"," * Exp)^-1)) * - key"do"^-1 * Body / mark"for", - - ForEach = key"for" * Ct(NameList) * key"in" * Ct(sym"*" * Exp / mark"unpack" + ExpList) * key"do"^-1 * Body / mark"foreach", - - Comprehension = sym"[" * Exp * CompInner * sym"]" / mark"comprehension", - - TblComprehension = sym"{" * Ct(Exp * (sym"," * Exp)^-1) * CompInner * sym"}" / mark"tblcomprehension", - - CompInner = Ct(CompFor * CompClause^0), - CompFor = key"for" * Ct(NameList) * key"in" * (sym"*" * Exp / mark"unpack" + Exp) / mark"for", - CompClause = CompFor + key"when" * Exp / mark"when", - - Assign = sym"=" * (Ct(With + If + Switch) + Ct(TableBlock + ExpListLow)) / mark"assign", - Update = ((sym"..=" + sym"+=" + sym"-=" + sym"*=" + sym"/=" + sym"%=") / trim) * Exp / mark"update", - - -- we can ignore precedence for now - OtherOps = op"or" + op"and" + op"<=" + op">=" + op"~=" + op"!=" + op"==" + op".." + op"<" + op">", - - Assignable = Cmt(DotChain + Chain, check_assignable) + Name, - AssignableList = Assignable * (sym"," * Assignable)^0, - - Exp = Ct(Value * ((OtherOps + FactorOp + TermOp) * Value)^0) / flatten_or_mark"exp", - - -- Exp = Ct(Factor * (OtherOps * Factor)^0) / flatten_or_mark"exp", - -- Factor = Ct(Term * (FactorOp * Term)^0) / flatten_or_mark"exp", - -- Term = Ct(Value * (TermOp * Value)^0) / flatten_or_mark"exp", - - SimpleValue = - If + Unless + - Switch + - With + - ForEach + For + While + - sym"-" * -SomeSpace * Exp / mark"minus" + - sym"#" * Exp / mark"length" + - key"not" * Exp / mark"not" + - TblComprehension + - TableLit + - Comprehension + - FunLit + - Num, - - ChainValue = -- a function call or an object access - StringChain + - ((Chain + DotChain + Callable) * Ct(InvokeArgs^-1)) / flatten_func, - - Value = pos( - SimpleValue + - Ct(KeyValueList) / mark"table" + - ChainValue), - - SliceValue = SimpleValue + ChainValue, - - StringChain = String * - (Ct((ColonCall + ColonSuffix) * ChainTail^-1) * Ct(InvokeArgs^-1))^-1 / flatten_string_chain, - - String = Space * DoubleString + Space * SingleString + LuaString, - SingleString = simple_string("'"), - DoubleString = simple_string('"', true), - - LuaString = Cg(LuaStringOpen, "string_open") * Cb"string_open" * Break^-1 * - C((1 - Cmt(C(LuaStringClose) * Cb"string_open", check_lua_string))^0) * - LuaStringClose / mark"string", - - LuaStringOpen = sym"[" * P"="^0 * "[" / trim, - LuaStringClose = "]" * P"="^0 * "]", - - Callable = Name + Parens / mark"parens", - Parens = sym"(" * Exp * sym")", - - FnArgs = symx"(" * Ct(ExpList^-1) * sym")" + sym"!" * -P"=" * Ct"", - - ChainTail = ChainItem^1 * ColonSuffix^-1 + ColonSuffix, - - -- a list of funcalls and indexes on a callable - Chain = Callable * ChainTail / mark"chain", - - -- shorthand dot call for use in with statement - DotChain = - (sym"." * Cc(-1) * (_Name / mark"dot") * ChainTail^-1) / mark"chain" + - (sym"\\" * Cc(-1) * ( - (_Name * Invoke / mark"colon") * ChainTail^-1 + - (_Name / mark"colon_stub") - )) / mark"chain", - - ChainItem = - Invoke + - Slice + - symx"[" * Exp/mark"index" * sym"]" + - symx"." * _Name/mark"dot" + - ColonCall, - - Slice = symx"[" * (SliceValue + Cc(1)) * sym"," * (SliceValue + Cc"") * - (sym"," * SliceValue)^-1 *sym"]" / mark"slice", - - ColonCall = symx"\\" * (_Name * Invoke) / mark"colon", - ColonSuffix = symx"\\" * _Name / mark"colon_stub", - - Invoke = FnArgs/mark"call" + - SingleString / wrap_func_arg + - DoubleString / wrap_func_arg, - - TableValue = KeyValue + Ct(Exp), - - TableLit = sym"{" * Ct( - TableValueList^-1 * sym","^-1 * - (SpaceBreak * TableLitLine * (sym","^-1 * SpaceBreak * TableLitLine)^0 * sym","^-1)^-1 - ) * White * sym"}" / mark"table", - - TableValueList = TableValue * (sym"," * TableValue)^0, - TableLitLine = PushIndent * ((TableValueList * PopIndent) + (PopIndent * Cut)) + Space, - - -- the unbounded table - TableBlockInner = Ct(KeyValueLine * (SpaceBreak^1 * KeyValueLine)^0), - TableBlock = SpaceBreak^1 * Advance * ensure(TableBlockInner, PopIndent) / mark"table", - - ClassDecl = key"class" * Name * (key"extends" * PreventIndent * ensure(Exp, PopIndent) + C"")^-1 * ClassBlock / mark"class", - - ClassBlock = SpaceBreak^1 * Advance * - Ct(ClassLine * (SpaceBreak^1 * ClassLine)^0) * PopIndent, - ClassLine = CheckIndent * (( - KeyValueList / mark"props" + - Statement / mark"stm" + - Exp / mark"stm" - ) * sym","^-1), - - Export = key"export" * ( - Cc"class" * ClassDecl + - op"*" + op"^" + - Ct(NameList) * (sym"=" * Ct(ExpListLow))^-1) / mark"export", - - KeyValue = (sym":" * Name) / self_assign + Ct((KeyName + sym"[" * Exp * sym"]" + DoubleString + SingleString) * symx":" * (Exp + TableBlock)), - KeyValueList = KeyValue * (sym"," * KeyValue)^0, - KeyValueLine = CheckIndent * KeyValueList * sym","^-1, - - FnArgsDef = sym"(" * Ct(FnArgDefList^-1) * - (key"using" * Ct(NameList + Space * "nil") + Ct"") * - sym")" + Ct"" * Ct"", - - FnArgDefList = FnArgDef * (sym"," * FnArgDef)^0, - FnArgDef = Ct(Name * (sym"=" * Exp)^-1), - - FunLit = FnArgsDef * - (sym"->" * Cc"slim" + sym"=>" * Cc"fat") * - (Body + Ct"") / mark"fndef", - - NameList = Name * (sym"," * Name)^0, - ExpList = Exp * (sym"," * Exp)^0, - ExpListLow = Exp * ((sym"," + sym";") * Exp)^0, - - InvokeArgs = ExpList * (sym"," * (TableBlock + SpaceBreak * Advance * ArgBlock * TableBlock^-1) + TableBlock)^-1 + TableBlock, - ArgBlock = ArgLine * (sym"," * SpaceBreak * ArgLine)^0 * PopIndent, - ArgLine = CheckIndent * ExpList - } - - return { - _g = White * g * White * -1, - match = function(self, str, ...) - - local pos_to_line = function(pos) - return util.pos_to_line(str, pos) - end - - local get_line = function(num) - return util.get_line(str, num) - end - - local tree - local args = {...} - local pass, err = assert(pcall(function() - tree = self._g:match(str, unpack(args)) - end)) - - if not tree then - local line_no = pos_to_line(last_pos) - local line_str = get_line(line_no) or "" - - return nil, err_msg:format(line_no, trim(line_str), _indent:top()) - end - return tree - end - } - -end) - --- parse a string --- returns tree, or nil and error message -function string(str) - local g = build_grammar() - return g:match(str) -end - + if not (result) then + if err_pos then + return nil, errors.format(str, err_pos, label or "failed to parse") + end + return nil, "failed to parse" + end + return result + end +} diff --git a/moonscript/parse.moon b/moonscript/parse.moon new file mode 100644 index 00000000..b49e93b2 --- /dev/null +++ b/moonscript/parse.moon @@ -0,0 +1,39 @@ +-- MoonScript parser interface. The parser itself is generated from the +-- grammar in moonscript/parse/grammar.moon by pgen +-- (https://github.com/leafo/pgen), see `make generate`. +-- moonscript/parse/slow.lua is the same grammar generated as pure Lua, a +-- drop-in for distributions that can't build the C module. + +errors = require "moonscript.parse.errors" +parser = require "moonscript.parse.native" + +{ + -- parse a string as a file + -- returns tree, or nil and error message + string: (str) -> + -- grammar transforms run during parse(); format_assign raises + -- error({node, msg}) for invalid assignment targets + ok, result, label, err_pos = pcall parser.parse, str + + unless ok + if type(result) == "table" + {node, msg} = result + node_pos = type(node) == "table" and node[-1] + + if node_pos + return nil, errors.format str, node_pos, msg + + return nil, "failed to parse: " .. tostring msg + + -- errors thrown by the parser itself (e.g. the recursion depth limit) + -- are returned like any other parse failure, not raised + return nil, tostring result + + unless result + if err_pos + return nil, errors.format str, err_pos, label or "failed to parse" + + return nil, "failed to parse" + + result +} diff --git a/moonscript/parse/errors.lua b/moonscript/parse/errors.lua new file mode 100644 index 00000000..1ec6c173 --- /dev/null +++ b/moonscript/parse/errors.lua @@ -0,0 +1,121 @@ +local errors = {} + +-- Split subject into array of lines (local helper) +local function splitlines(subject) + local lines = {} + local start = 1 + while true do + local newline_pos = subject:find("\n", start, true) + if newline_pos then + lines[#lines + 1] = subject:sub(start, newline_pos - 1) + start = newline_pos + 1 + else + lines[#lines + 1] = subject:sub(start) + break + end + end + return lines +end + +-- Calculate line number and column from byte position (local helper) +-- Returns: line (1-indexed), column (1-indexed) +local function calcline(subject, pos) + if pos <= 1 then return 1, 1 end + local sub = subject:sub(1, pos - 1) + local line = 1 + local last_newline = 0 + for i = 1, #sub do + if sub:sub(i, i) == "\n" then + line = line + 1 + last_newline = i + end + end + local col = pos - last_newline + return line, col +end + +-- Get the line of text containing the given position (local helper) +-- Returns: line text, column position within that line +local function getline(subject, pos) + -- Find start of line + local line_start = pos + while line_start > 1 and subject:sub(line_start - 1, line_start - 1) ~= "\n" do + line_start = line_start - 1 + end + -- Find end of line + local line_end = pos + while line_end <= #subject and subject:sub(line_end, line_end) ~= "\n" do + line_end = line_end + 1 + end + return subject:sub(line_start, line_end - 1), pos - line_start + 1 +end + +-- Format a complete error message +-- subject: the input string being parsed +-- pos: byte position where error occurred (1-indexed) +-- label: error label from T() or nil +-- opts: optional table with: +-- color: boolean - if true, use ansicolors for colored output +-- context: number - lines to show above and below error line (default: 0) +function errors.format(subject, pos, label, opts) + local line, col = calcline(subject, pos) + local line_text, col_in_line = getline(subject, pos) + local context = opts and opts.context or 0 + + local msg + if opts and opts.color then + local colors = require("ansicolors") + msg = colors("%{bright red}" .. (label or "error") .. "%{reset}") + msg = msg .. colors(" %{dim}at line " .. line .. ", column " .. col .. ":%{reset}\n") + + if context > 0 then + local lines = splitlines(subject) + local start_line = math.max(1, line - context) + local end_line = math.min(#lines, line + context) + local max_line_num = end_line + local line_num_width = #tostring(max_line_num) + + for i = start_line, end_line do + local line_num_str = string.format("%" .. line_num_width .. "d", i) + msg = msg .. colors("%{dim}" .. line_num_str .. " |%{reset} ") .. lines[i] .. "\n" + if i == line then + local prefix_width = line_num_width + 3 + col_in_line - 1 + msg = msg .. string.rep(" ", prefix_width) .. colors("%{bright red}^%{reset}") .. "\n" + end + end + msg = msg:sub(1, -2) -- remove trailing newline + else + msg = msg .. " " .. line_text .. "\n" + msg = msg .. " " .. string.rep(" ", col_in_line - 1) + msg = msg .. colors("%{bright red}^%{reset}") + end + else + msg = string.format("%s at line %d, column %d:\n", + label or "error", line, col) + + if context > 0 then + local lines = splitlines(subject) + local start_line = math.max(1, line - context) + local end_line = math.min(#lines, line + context) + local max_line_num = end_line + local line_num_width = #tostring(max_line_num) + + for i = start_line, end_line do + local line_num_str = string.format("%" .. line_num_width .. "d", i) + msg = msg .. line_num_str .. " | " .. lines[i] .. "\n" + if i == line then + local prefix_width = line_num_width + 3 + col_in_line - 1 + msg = msg .. string.rep(" ", prefix_width) .. "^\n" + end + end + msg = msg:sub(1, -2) -- remove trailing newline + else + msg = msg .. " " .. line_text .. "\n" + msg = msg .. " " .. string.rep(" ", col_in_line - 1) .. "^" + end + end + + return msg +end + +return errors diff --git a/moonscript/parse/grammar.lua b/moonscript/parse/grammar.lua new file mode 100644 index 00000000..5d99cd5d --- /dev/null +++ b/moonscript/parse/grammar.lua @@ -0,0 +1,222 @@ +local pgen = require("pgen") +local P, R, S, V, C, Ct, Cc, Cp, Cg, Cmb, Cmt, Cfn, L +P, R, S, V, C, Ct, Cc, Cp, Cg, Cmb, Cmt, Cfn, L = pgen.P, pgen.R, pgen.S, pgen.V, pgen.C, pgen.Ct, pgen.Cc, pgen.Cp, pgen.Cg, pgen.Cmb, pgen.Cmt, pgen.Cfn, pgen.L +local ind = pgen.indenter({ + tab_width = 4, + initial = 0 +}) +local dos = pgen.indenter({ + initial = 1 +}) +local DisableDo = dos.cpush(0) +local PopDo = dos.pop +local CheckDo = dos.ctop("ne", 0) +local AlphaNum = R("az", "AZ", "09", "__") +local keyword_words = { + "continue", + "extends", + "elseif", + "export", + "import", + "return", + "switch", + "unless", + "break", + "class", + "local", + "using", + "while", + "else", + "from", + "then", + "when", + "with", + "and", + "for", + "not", + "do", + "if", + "in", + "or" +} +local keyword_patt +for _index_0 = 1, #keyword_words do + local word = keyword_words[_index_0] + keyword_patt = keyword_patt and (keyword_patt + P(word)) or P(word) +end +local Keyword = keyword_patt * -AlphaNum +local mark +mark = function(name, patt) + return Ct(Cc(name) * patt) +end +local pos +pos = function(patt) + return Cfn(Cp() * patt, [[return function(p, value) + if type(value) == "table" then + value[-1] = p + end + return value + end]]) +end +local assign_transform = [[ local tree = require("moonscript.parse.tree") + return function(lhs, assign) + return tree.format_assign(lhs, assign) + end]] +local sym +sym = function(chars) + return V("Space") * P(chars) +end +local op +op = function(chars) + local patt = V("Space") * C(P(chars)) + if chars:match("^%w*$") then + patt = patt * -AlphaNum + end + return patt +end +local key +key = function(chars) + return V("Space") * P(chars) * -AlphaNum +end +return { + "Root", + Root = V("White") * V("File") * V("White") * P(-1), + White = S(" \t\r\n") ^ 0, + Break = P("\r") ^ -1 * P("\n"), + Stop = V("Break") + P(-1), + Comment = P("--") * (P(1) - S("\r\n")) ^ 0 * L(V("Stop")), + Space = S(" \t") ^ 0 * V("Comment") ^ -1, + SomeSpace = S(" \t") ^ 1 * V("Comment") ^ -1, + SpaceBreak = V("Space") * V("Break"), + EmptyLine = V("SpaceBreak"), + Shebang = P("#!") * (P(1) - V("Stop")) ^ 0, + NameRaw = C(R("az", "AZ", "__") * AlphaNum ^ 0), + Name = V("Space") * -Keyword * V("NameRaw"), + Num = V("Space") * Ct(Cc("number") * C(P("0x") * R("09", "af", "AF") ^ 1 * (S("uU") ^ -1 * S("lL") ^ 2) ^ -1 + R("09") ^ 1 * (S("uU") ^ -1 * S("lL") ^ 2) + (R("09") ^ 1 * (P(".") * R("09") ^ 1) ^ -1 + P(".") * R("09") ^ 1) * (S("eE") * P("-") ^ -1 * R("09") ^ 1) ^ -1)), + SelfName = V("Space") * P("@") * (P("@") * (mark("self_class", V("NameRaw")) + Cc("self.__class")) + mark("self", V("NameRaw")) + Cc("self")), + KeyName = V("SelfName") + V("Space") * mark("key_literal", V("NameRaw")), + VarArg = V("Space") * C(P("...")), + File = V("Shebang") ^ -1 * (V("Block") + Ct(P(""))), + Block = Ct(V("Line") * (V("Break") ^ 1 * V("Line")) ^ 0), + CheckIndent = ind.check, + Line = V("CheckIndent") * V("Statement") + V("Space") * L(V("Stop")), + Statement = Cfn(pos(V("Import") + V("While") + V("With") + V("For") + V("ForEach") + V("Switch") + V("Return") + V("Local") + V("Export") + V("BreakLoop") + Cfn(Ct(V("ExpList")) * (V("Update") + V("Assign")) ^ -1, assign_transform)) * V("Space") * ((mark("if", key("if") * V("Exp") * (key("else") * V("Exp")) ^ -1 * V("Space")) + mark("unless", key("unless") * V("Exp")) + mark("comprehension", V("CompInner"))) * V("Space")) ^ -1, [[return function(stm, dec) + if dec then + return {"decorated", stm, dec} + end + return stm + end]]), + Body = V("Space") * V("Break") * V("EmptyLine") ^ 0 * V("InBlock") + Ct(V("Statement")), + Advance = ind.advance, + PushIndent = ind.push, + PreventIndent = ind.prevent, + PopIndent = ind.pop, + InBlock = V("Advance") * V("Block") * V("PopIndent"), + Local = key("local") * (mark("declare_glob", op("*") + op("^")) + mark("declare_with_shadows", Ct(V("NameList")))), + Import = mark("import", key("import") * Ct(V("ImportNameList")) * V("SpaceBreak") ^ 0 * key("from") * V("Exp")), + ImportName = sym("\\") * Ct(Cc("colon") * V("Name")) + V("Name"), + ImportNameList = V("SpaceBreak") ^ 0 * V("ImportName") * ((V("SpaceBreak") ^ 1 + sym(",") * V("SpaceBreak") ^ 0) * V("ImportName")) ^ 0, + BreakLoop = Ct(key("break") * Cc("break")) + Ct(key("continue") * Cc("continue")), + Return = mark("return", key("return") * (mark("explist", V("ExpListLow")) + C(P("")))), + WithExp = Cfn(Ct(V("ExpList")) * V("Assign") ^ -1, assign_transform), + With = mark("with", key("with") * DisableDo * V("WithExp") * PopDo * key("do") ^ -1 * V("Body")), + Switch = mark("switch", key("switch") * DisableDo * V("Exp") * PopDo * key("do") ^ -1 * V("Space") * V("Break") * V("SwitchBlock")), + SwitchBlock = V("EmptyLine") ^ 0 * V("Advance") * Ct(V("SwitchCase") * (V("Break") ^ 1 * V("SwitchCase")) ^ 0 * (V("Break") ^ 1 * V("SwitchElse")) ^ -1) * V("PopIndent"), + SwitchCase = mark("case", key("when") * Ct(V("ExpList")) * key("then") ^ -1 * V("Body")), + SwitchElse = mark("else", key("else") * V("Body")), + IfCond = Cfn(V("Exp") * V("Assign") ^ -1, [[ local tree = require("moonscript.parse.tree") + return function(lhs, assign) + return tree.format_single_assign(lhs, assign) + end]]), + IfElse = mark("else", (V("Break") * V("EmptyLine") ^ 0 * V("CheckIndent")) ^ -1 * key("else") * V("Body")), + IfElseIf = mark("elseif", (V("Break") * V("EmptyLine") ^ 0 * V("CheckIndent")) ^ -1 * key("elseif") * pos(V("IfCond")) * key("then") ^ -1 * V("Body")), + If = mark("if", key("if") * V("IfCond") * key("then") ^ -1 * V("Body") * V("IfElseIf") ^ 0 * V("IfElse") ^ -1), + Unless = mark("unless", key("unless") * V("IfCond") * key("then") ^ -1 * V("Body") * V("IfElseIf") ^ 0 * V("IfElse") ^ -1), + While = mark("while", key("while") * DisableDo * V("Exp") * PopDo * key("do") ^ -1 * V("Body")), + For = mark("for", key("for") * DisableDo * V("Name") * sym("=") * Ct(V("Exp") * sym(",") * V("Exp") * (sym(",") * V("Exp")) ^ -1) * PopDo * key("do") ^ -1 * V("Body")), + ForEach = mark("foreach", key("for") * Ct(V("AssignableNameList")) * key("in") * DisableDo * Ct(sym("*") * mark("unpack", V("Exp")) + V("ExpList")) * PopDo * key("do") ^ -1 * V("Body")), + Do = mark("do", key("do") * V("Body")), + Comprehension = mark("comprehension", sym("[") * V("Exp") * V("CompInner") * sym("]")), + TblComprehension = mark("tblcomprehension", sym("{") * Ct(V("Exp") * (sym(",") * V("Exp")) ^ -1) * V("CompInner") * sym("}")), + CompInner = Ct((V("CompForEach") + V("CompFor")) * V("CompClause") ^ 0), + CompForEach = mark("foreach", key("for") * Ct(V("AssignableNameList")) * key("in") * (sym("*") * mark("unpack", V("Exp")) + V("Exp"))), + CompFor = V("Space") * Ct(Cc("for") * P("for") * V("Name") * sym("=") * Ct(V("Exp") * sym(",") * V("Exp") * (sym(",") * V("Exp")) ^ -1)) * -AlphaNum, + CompClause = V("CompFor") + V("CompForEach") + mark("when", key("when") * V("Exp")), + Assign = mark("assign", sym("=") * (Ct(V("With") + V("If") + V("Switch")) + Ct(V("TableBlock") + V("ExpListLow")))), + Update = mark("update", V("Space") * C(P("..=") + P("+=") + P("-=") + P("*=") + P("/=") + P("%=") + P("or=") + P("and=") + P("&=") + P("|=") + P(">>=") + P("<<=")) * V("Exp")), + CharOperators = V("Space") * C(S("+-*/%^><|&")), + WordOperators = op("or") + op("and") + op("<=") + op(">=") + op("~=") + op("!=") + op("==") + op("..") + op("<<") + op(">>") + op("//"), + BinaryOperator = (V("WordOperators") + V("CharOperators")) * V("SpaceBreak") ^ 0, + Assignable = Cmt(V("Chain"), [[ local subject, pos, node = ... + local last = node[#node] + local t = type(last) == "table" and last[1] + if t == "dot" or t == "index" or t == "slice" then + return pos, node + end + return false + ]]) + V("Name") + V("SelfName"), + Exp = Cfn(V("Value") * (V("BinaryOperator") * V("Value")) ^ 0, [[return function(...) + if select("#", ...) == 1 then + return ... + end + return {"exp", ...} + end]]), + SimpleValue = V("If") + V("Unless") + V("Switch") + V("With") + V("ClassDecl") + V("ForEach") + V("For") + V("While") + CheckDo * V("Do") + mark("minus", sym("-") * -V("SomeSpace") * V("Exp")) + mark("length", sym("#") * V("Exp")) + mark("bitnot", sym("~") * V("Exp")) + mark("not", key("not") * V("Exp")) + V("TblComprehension") + V("TableLit") + V("Comprehension") + V("FunLit") + V("Num"), + ChainValue = Cfn((V("Chain") + V("Callable")) * Ct(V("InvokeArgs") ^ -1), [[ local tree = require("moonscript.parse.tree") + return function(callee, args) + return tree.join_chain(callee, args) + end]]), + Value = pos(V("SimpleValue") + mark("table", Ct(V("KeyValueList"))) + V("ChainValue") + V("String")), + SliceValue = V("Exp"), + String = V("Space") * V("DoubleString") + V("Space") * V("SingleString") + V("LuaString"), + SingleString = mark("string", C(P("'")) * C((P("\\'") + P("\\\\") + (P(1) - P("'"))) ^ 0) * P("'")), + DoubleString = mark("string", C(P('"')) * (C((V("DoubleStringInner") - P('#{')) ^ 1) + V("DoubleStringInterp")) ^ 0 * P('"')), + DoubleStringInner = P('\\"') + P("\\\\") + (P(1) - P('"')), + DoubleStringInterp = mark("interpolate", P('#{') * V("Exp") * sym("}")), + LuaString = Cfn(V("Space") * P("[") * Cp() * Cg(P("=") ^ 0, "lua_eq") * Cp() * P("[") * V("Break") ^ -1 * C((P(1) - V("LuaStringClose")) ^ 0) * V("LuaStringClose"), [[return function(eq_start, eq_end, content) + return {"string", "[" .. ("="):rep(eq_end - eq_start) .. "[", content} + end]]), + LuaStringClose = P("]") * Cmb("lua_eq") * P("]"), + Callable = pos(mark("ref", V("Name"))) + V("SelfName") + V("VarArg") + mark("parens", V("Parens")), + Parens = sym("(") * V("SpaceBreak") ^ 0 * V("Exp") * V("SpaceBreak") ^ 0 * sym(")"), + FnArgs = P("(") * V("SpaceBreak") ^ 0 * Ct(V("FnArgsExpList") ^ -1) * V("SpaceBreak") ^ 0 * sym(")") + sym("!") * -P("=") * Ct(P("")), + FnArgsExpList = V("Exp") * ((V("Break") + sym(",")) * V("White") * V("Exp")) ^ 0, + Chain = Ct(Cc("chain") * (V("Callable") + V("String") + -S(".\\")) * V("ChainItems")) + Ct(Cc("chain") * V("Space") * (V("DotChainItem") * V("ChainItems") ^ -1 + V("ColonChain"))), + ChainItems = V("ChainItem") ^ 1 * V("ColonChain") ^ -1 + V("ColonChain"), + ChainItem = V("Invoke") + V("DotChainItem") + V("Slice") + mark("index", P("[") * V("Exp")) * sym("]"), + DotChainItem = mark("dot", P(".") * V("NameRaw")), + ColonChainItem = mark("colon", P("\\") * V("NameRaw")), + ColonChain = V("ColonChainItem") * (V("Invoke") * V("ChainItems") ^ -1) ^ -1, + Slice = mark("slice", P("[") * (V("SliceValue") + Cc(1)) * sym(",") * (V("SliceValue") + Cc("")) * (sym(",") * V("SliceValue")) ^ -1 * sym("]")), + Invoke = mark("call", V("FnArgs")) + Ct(Cc("call") * Ct(V("SingleString"))) + Ct(Cc("call") * Ct(V("DoubleString"))) + L(P("[")) * Ct(Cc("call") * Ct(V("LuaString"))), + TableValue = V("KeyValue") + Ct(V("Exp")), + TableLit = mark("table", sym("{") * Ct(V("TableValueList") ^ -1 * sym(",") ^ -1 * (V("SpaceBreak") * V("TableLitLine") * (sym(",") ^ -1 * V("SpaceBreak") * V("TableLitLine")) ^ 0 * sym(",") ^ -1) ^ -1) * V("White") * sym("}")), + TableValueList = V("TableValue") * (sym(",") * V("TableValue")) ^ 0, + TableLitLine = V("PushIndent") * V("TableValueList") * V("PopIndent") + V("Space"), + TableBlockInner = Ct(V("KeyValueLine") * (V("SpaceBreak") ^ 1 * V("KeyValueLine")) ^ 0), + TableBlock = mark("table", V("SpaceBreak") ^ 1 * V("Advance") * V("TableBlockInner") * V("PopIndent")), + ClassDecl = mark("class", key("class") * -P(":") * (V("Assignable") + Cc(nil)) * (key("extends") * V("PreventIndent") * V("Exp") * V("PopIndent") + C(P(""))) ^ -1 * (V("ClassBlock") + Ct(P("")))), + ClassBlock = V("SpaceBreak") ^ 1 * V("Advance") * Ct(V("ClassLine") * (V("SpaceBreak") ^ 1 * V("ClassLine")) ^ 0) * V("PopIndent"), + ClassLine = V("CheckIndent") * ((mark("props", V("KeyValueList")) + mark("stm", V("Statement")) + mark("stm", V("Exp"))) * sym(",") ^ -1), + Export = mark("export", key("export") * (Cc("class") * V("ClassDecl") + op("*") + op("^") + Ct(V("NameList")) * (sym("=") * Ct(V("ExpListLow"))) ^ -1)), + KeyValue = Cfn(sym(":") * -V("SomeSpace") * V("Name") * Cp(), [[return function(name, p) + return { + {"key_literal", name}, + {"ref", name, [-1] = p}, + } + end]]) + Ct((V("KeyName") + sym("[") * V("Exp") * sym("]") + V("Space") * V("DoubleString") + V("Space") * V("SingleString")) * P(":") * (V("Exp") + V("TableBlock") + V("SpaceBreak") ^ 1 * V("Exp"))), + KeyValueList = V("KeyValue") * (sym(",") * V("KeyValue")) ^ 0, + KeyValueLine = V("CheckIndent") * V("KeyValueList") * sym(",") ^ -1, + FnArgsDef = sym("(") * V("White") * Ct(V("FnArgDefList") ^ -1) * (key("using") * Ct(V("NameList") + V("Space") * P("nil")) + Ct(P(""))) * V("White") * sym(")") + Ct(P("")) * Ct(P("")), + FnArgDefList = V("FnArgDef") * ((sym(",") + V("Break")) * V("White") * V("FnArgDef")) ^ 0 * ((sym(",") + V("Break")) * V("White") * Ct(V("VarArg"))) ^ 0 + Ct(V("VarArg")), + FnArgDef = Ct((V("Name") + V("SelfName") + V("TableLit")) * (sym("=") * V("Exp")) ^ -1), + FunLit = mark("fndef", V("FnArgsDef") * (sym("->") * Cc("slim") + sym("=>") * Cc("fat")) * (V("Body") + Ct(P("")))), + NameList = V("Name") * (sym(",") * V("Name")) ^ 0, + NameOrDestructure = V("Name") + V("TableLit"), + AssignableNameList = V("NameOrDestructure") * (sym(",") * V("NameOrDestructure")) ^ 0, + ExpList = V("Exp") * (sym(",") * V("Exp")) ^ 0, + ExpListLow = V("Exp") * ((sym(",") + sym(";")) * V("Exp")) ^ 0, + InvokeArgs = -P("-") * (V("ExpList") * (sym(",") * (V("TableBlock") + V("SpaceBreak") * V("Advance") * V("ArgBlock") * V("TableBlock") ^ -1) + V("TableBlock")) ^ -1 + V("TableBlock")), + ArgBlock = V("ArgLine") * (sym(",") * V("SpaceBreak") * V("ArgLine")) ^ 0 * V("PopIndent"), + ArgLine = V("CheckIndent") * V("ExpList") +} diff --git a/moonscript/parse/grammar.moon b/moonscript/parse/grammar.moon new file mode 100644 index 00000000..1c281122 --- /dev/null +++ b/moonscript/parse/grammar.moon @@ -0,0 +1,385 @@ +-- MoonScript grammar for pgen. Produces the AST consumed by the +-- moonscript compiler. +-- +-- The longer Cfn transform bodies live in moonscript/parse/tree.moon and +-- are required from the callback strings below. The callback strings are +-- Lua source: they are embedded verbatim into the generated parsers. + +pgen = require "pgen" +import P, R, S, V, C, Ct, Cc, Cp, Cg, Cmb, Cmt, Cfn, L from pgen + +ind = pgen.indenter tab_width: 4, initial: 0 + +-- `do` permission stack: the `do` expression is disabled while parsing the +-- header expression of while/with/switch/for, so the `do` in +-- `while x do ...` reads as the block keyword. 0 = disabled. +dos = pgen.indenter initial: 1 +DisableDo = dos.cpush 0 +PopDo = dos.pop +CheckDo = dos.ctop "ne", 0 + +AlphaNum = R "az", "AZ", "09", "__" + +-- Sorted longest-first: pgen's trie optimization requires longer strings +-- before their prefixes, and PEG ordered choice needs "elseif" tried +-- before "else" anyway. +keyword_words = { + "continue", "extends", + "elseif", "export", "import", "return", "switch", "unless", + "break", "class", "local", "using", "while", + "else", "from", "then", "when", "with", + "and", "for", "not", + "do", "if", "in", "or", +} + +local keyword_patt +for word in *keyword_words + keyword_patt = keyword_patt and (keyword_patt + P word) or P word + +Keyword = keyword_patt * -AlphaNum + +mark = (name, patt) -> + Ct Cc(name) * patt + +pos = (patt) -> + Cfn Cp! * patt, [[return function(p, value) + if type(value) == "table" then + value[-1] = p + end + return value + end]] + +-- Shared by Statement and WithExp; may raise error({node, msg}) for invalid +-- assignment targets, surfaced from parse() (see moonscript/parse.moon) +assign_transform = [[ + local tree = require("moonscript.parse.tree") + return function(lhs, assign) + return tree.format_assign(lhs, assign) + end]] + +sym = (chars) -> + V"Space" * P chars + +op = (chars) -> + patt = V"Space" * C P chars + if chars\match "^%w*$" + patt = patt * -AlphaNum + patt + +key = (chars) -> + V"Space" * P(chars) * -AlphaNum + +{ + "Root" + + Root: V"White" * V"File" * V"White" * P(-1) + + White: S(" \t\r\n")^0 + Break: P("\r")^-1 * P"\n" + Stop: V"Break" + P(-1) + Comment: P"--" * (P(1) - S"\r\n")^0 * L(V"Stop") + Space: S(" \t")^0 * V("Comment")^-1 + SomeSpace: S(" \t")^1 * V("Comment")^-1 + SpaceBreak: V"Space" * V"Break" + EmptyLine: V"SpaceBreak" + Shebang: P"#!" * (P(1) - V"Stop")^0 + + NameRaw: C(R("az", "AZ", "__") * AlphaNum^0) + Name: V"Space" * -Keyword * V"NameRaw" + + Num: V"Space" * Ct(Cc("number") * C( + P("0x") * R("09", "af", "AF")^1 * (S("uU")^-1 * S("lL")^2)^-1 + + R("09")^1 * (S("uU")^-1 * S("lL")^2) + + (R("09")^1 * (P(".") * R("09")^1)^-1 + P(".") * R("09")^1) * + (S("eE") * P("-")^-1 * R("09")^1)^-1 + )) + + SelfName: V"Space" * P"@" * ( + P("@") * (mark("self_class", V"NameRaw") + Cc"self.__class") + + mark("self", V"NameRaw") + Cc"self") + KeyName: V"SelfName" + V"Space" * mark("key_literal", V"NameRaw") + VarArg: V"Space" * C(P"...") + + File: V("Shebang")^-1 * (V"Block" + Ct(P"")) + Block: Ct(V"Line" * (V("Break")^1 * V"Line")^0) + CheckIndent: ind.check + Line: V"CheckIndent" * V"Statement" + V"Space" * L(V"Stop") + + Statement: Cfn( + pos( + V"Import" + V"While" + V"With" + V"For" + V"ForEach" + V"Switch" + + V"Return" + V"Local" + V"Export" + V"BreakLoop" + + Cfn(Ct(V"ExpList") * (V"Update" + V"Assign")^-1, assign_transform) + ) * V"Space" * + ((mark("if", key("if") * V"Exp" * (key("else") * V"Exp")^-1 * V"Space") + + mark("unless", key("unless") * V"Exp") + + mark("comprehension", V"CompInner")) * V"Space")^-1, + [[return function(stm, dec) + if dec then + return {"decorated", stm, dec} + end + return stm + end]]) + + Body: V"Space" * V"Break" * V("EmptyLine")^0 * V"InBlock" + Ct(V"Statement") + + Advance: ind.advance + PushIndent: ind.push + PreventIndent: ind.prevent + PopIndent: ind.pop + InBlock: V"Advance" * V"Block" * V"PopIndent" + + Local: key("local") * ( + mark("declare_glob", op("*") + op("^")) + + mark("declare_with_shadows", Ct(V"NameList"))) + + Import: mark("import", + key("import") * Ct(V"ImportNameList") * V("SpaceBreak")^0 * key("from") * V"Exp") + ImportName: sym("\\") * Ct(Cc("colon") * V"Name") + V"Name" + ImportNameList: V("SpaceBreak")^0 * V"ImportName" * + ((V("SpaceBreak")^1 + sym(",") * V("SpaceBreak")^0) * V"ImportName")^0 + + BreakLoop: Ct(key("break") * Cc"break") + Ct(key("continue") * Cc"continue") + + Return: mark("return", + key("return") * (mark("explist", V"ExpListLow") + C(P""))) + + WithExp: Cfn(Ct(V"ExpList") * V("Assign")^-1, assign_transform) + With: mark("with", + key("with") * DisableDo * V"WithExp" * PopDo * key("do")^-1 * V"Body") + + Switch: mark("switch", + key("switch") * DisableDo * V"Exp" * PopDo * key("do")^-1 * + V"Space" * V"Break" * V"SwitchBlock") + SwitchBlock: V("EmptyLine")^0 * V"Advance" * + Ct(V"SwitchCase" * (V("Break")^1 * V"SwitchCase")^0 * + (V("Break")^1 * V"SwitchElse")^-1) * V"PopIndent" + SwitchCase: mark("case", + key("when") * Ct(V"ExpList") * key("then")^-1 * V"Body") + SwitchElse: mark("else", key("else") * V"Body") + + IfCond: Cfn(V"Exp" * V("Assign")^-1, [[ + local tree = require("moonscript.parse.tree") + return function(lhs, assign) + return tree.format_single_assign(lhs, assign) + end]]) + IfElse: mark("else", + (V"Break" * V("EmptyLine")^0 * V"CheckIndent")^-1 * key("else") * V"Body") + IfElseIf: mark("elseif", + (V"Break" * V("EmptyLine")^0 * V"CheckIndent")^-1 * key("elseif") * + pos(V"IfCond") * key("then")^-1 * V"Body") + If: mark("if", + key("if") * V"IfCond" * key("then")^-1 * V"Body" * + V("IfElseIf")^0 * V("IfElse")^-1) + Unless: mark("unless", + key("unless") * V"IfCond" * key("then")^-1 * V"Body" * + V("IfElseIf")^0 * V("IfElse")^-1) + + While: mark("while", + key("while") * DisableDo * V"Exp" * PopDo * key("do")^-1 * V"Body") + + For: mark("for", + key("for") * DisableDo * V"Name" * sym("=") * + Ct(V"Exp" * sym(",") * V"Exp" * (sym(",") * V"Exp")^-1) * PopDo * + key("do")^-1 * V"Body") + ForEach: mark("foreach", + key("for") * Ct(V"AssignableNameList") * key("in") * DisableDo * + Ct(sym("*") * mark("unpack", V"Exp") + V"ExpList") * PopDo * + key("do")^-1 * V"Body") + + Do: mark("do", key("do") * V"Body") + + Comprehension: mark("comprehension", + sym("[") * V"Exp" * V"CompInner" * sym("]")) + TblComprehension: mark("tblcomprehension", + sym("{") * Ct(V"Exp" * (sym(",") * V"Exp")^-1) * V"CompInner" * sym("}")) + + CompInner: Ct((V"CompForEach" + V"CompFor") * V("CompClause")^0) + CompForEach: mark("foreach", + key("for") * Ct(V"AssignableNameList") * key("in") * + (sym("*") * mark("unpack", V"Exp") + V"Exp")) + CompFor: V"Space" * Ct(Cc("for") * P("for") * V"Name" * sym("=") * + Ct(V"Exp" * sym(",") * V"Exp" * (sym(",") * V"Exp")^-1)) * -AlphaNum + CompClause: V"CompFor" + V"CompForEach" + mark("when", key("when") * V"Exp") + + Assign: mark("assign", sym("=") * ( + Ct(V"With" + V"If" + V"Switch") + + Ct(V"TableBlock" + V"ExpListLow"))) + + Update: mark("update", + V"Space" * C( + P("..=") + P("+=") + P("-=") + P("*=") + P("/=") + P("%=") + + P("or=") + P("and=") + P("&=") + P("|=") + P(">>=") + P("<<=")) * V"Exp") + + CharOperators: V"Space" * C(S"+-*/%^><|&") + WordOperators: + op("or") + op("and") + op("<=") + op(">=") + op("~=") + op("!=") + op("==") + + op("..") + op("<<") + op(">>") + op("//") + BinaryOperator: (V"WordOperators" + V"CharOperators") * V("SpaceBreak")^0 + + Assignable: Cmt(V"Chain", [[ + local subject, pos, node = ... + local last = node[#node] + local t = type(last) == "table" and last[1] + if t == "dot" or t == "index" or t == "slice" then + return pos, node + end + return false + ]]) + V"Name" + V"SelfName" + + Exp: Cfn(V"Value" * (V"BinaryOperator" * V"Value")^0, [[return function(...) + if select("#", ...) == 1 then + return ... + end + return {"exp", ...} + end]]) + + SimpleValue: + V"If" + V"Unless" + V"Switch" + V"With" + V"ClassDecl" + + V"ForEach" + V"For" + V"While" + + CheckDo * V"Do" + + mark("minus", sym("-") * -V"SomeSpace" * V"Exp") + + mark("length", sym("#") * V"Exp") + + mark("bitnot", sym("~") * V"Exp") + + mark("not", key("not") * V"Exp") + + V"TblComprehension" + V"TableLit" + V"Comprehension" + V"FunLit" + V"Num" + + ChainValue: Cfn((V"Chain" + V"Callable") * Ct(V("InvokeArgs")^-1), [[ + local tree = require("moonscript.parse.tree") + return function(callee, args) + return tree.join_chain(callee, args) + end]]) + + Value: pos( + V"SimpleValue" + + mark("table", Ct(V"KeyValueList")) + + V"ChainValue" + V"String") + SliceValue: V"Exp" + + String: V"Space" * V"DoubleString" + V"Space" * V"SingleString" + V"LuaString" + SingleString: mark("string", + C(P"'") * C((P("\\'") + P("\\\\") + (P(1) - P"'"))^0) * P"'") + DoubleString: mark("string", + C(P'"') * + (C((V"DoubleStringInner" - P'#{')^1) + V"DoubleStringInterp")^0 * + P'"') + DoubleStringInner: P('\\"') + P("\\\\") + (P(1) - P'"') + DoubleStringInterp: mark("interpolate", P'#{' * V"Exp" * sym("}")) + + -- The lua_eq group must sit at this Cfn's own level, not nested inside + -- another transform, or LuaStringClose's Cmb cannot see it. + LuaString: Cfn( + V"Space" * P"[" * Cp! * Cg(P("=")^0, "lua_eq") * Cp! * P"[" * + V("Break")^-1 * C((P(1) - V"LuaStringClose")^0) * V"LuaStringClose", + [[return function(eq_start, eq_end, content) + return {"string", "[" .. ("="):rep(eq_end - eq_start) .. "[", content} + end]]) + LuaStringClose: P"]" * Cmb("lua_eq") * P"]" + + Callable: pos(mark("ref", V"Name")) + V"SelfName" + V"VarArg" + + mark("parens", V"Parens") + Parens: sym("(") * V("SpaceBreak")^0 * V"Exp" * V("SpaceBreak")^0 * sym(")") + + FnArgs: P("(") * V("SpaceBreak")^0 * Ct(V("FnArgsExpList")^-1) * + V("SpaceBreak")^0 * sym(")") + + sym("!") * -P("=") * Ct(P"") + FnArgsExpList: V"Exp" * ((V"Break" + sym(",")) * V"White" * V"Exp")^0 + + Chain: Ct(Cc("chain") * + (V"Callable" + V"String" + -S".\\") * V"ChainItems") + + Ct(Cc("chain") * + V"Space" * (V"DotChainItem" * V("ChainItems")^-1 + V"ColonChain")) + + ChainItems: V("ChainItem")^1 * V("ColonChain")^-1 + V"ColonChain" + ChainItem: V"Invoke" + V"DotChainItem" + V"Slice" + + mark("index", P("[") * V"Exp") * sym("]") + DotChainItem: mark("dot", P(".") * V"NameRaw") + ColonChainItem: mark("colon", P("\\") * V"NameRaw") + ColonChain: V"ColonChainItem" * (V"Invoke" * V("ChainItems")^-1)^-1 + + Slice: mark("slice", + P("[") * (V"SliceValue" + Cc(1)) * sym(",") * (V"SliceValue" + Cc("")) * + (sym(",") * V"SliceValue")^-1 * sym("]")) + + Invoke: mark("call", V"FnArgs") + + Ct(Cc("call") * Ct(V"SingleString")) + + Ct(Cc("call") * Ct(V"DoubleString")) + + L(P"[") * Ct(Cc("call") * Ct(V"LuaString")) + + TableValue: V"KeyValue" + Ct(V"Exp") + TableLit: mark("table", + sym("{") * Ct( + V("TableValueList")^-1 * sym(",")^-1 * + (V"SpaceBreak" * V"TableLitLine" * + (sym(",")^-1 * V"SpaceBreak" * V"TableLitLine")^0 * sym(",")^-1)^-1 + ) * V"White" * sym("}")) + TableValueList: V"TableValue" * (sym(",") * V"TableValue")^0 + TableLitLine: V"PushIndent" * V"TableValueList" * V"PopIndent" + V"Space" + + TableBlockInner: Ct(V"KeyValueLine" * (V("SpaceBreak")^1 * V"KeyValueLine")^0) + TableBlock: mark("table", + V("SpaceBreak")^1 * V"Advance" * V"TableBlockInner" * V"PopIndent") + + ClassDecl: mark("class", + key("class") * -P(":") * + (V"Assignable" + Cc(nil)) * + (key("extends") * V"PreventIndent" * V"Exp" * V"PopIndent" + C(P""))^-1 * + (V"ClassBlock" + Ct(P""))) + + ClassBlock: V("SpaceBreak")^1 * V"Advance" * + Ct(V"ClassLine" * (V("SpaceBreak")^1 * V"ClassLine")^0) * V"PopIndent" + ClassLine: V"CheckIndent" * ( + (mark("props", V"KeyValueList") + + mark("stm", V"Statement") + + mark("stm", V"Exp")) * sym(",")^-1) + + Export: mark("export", key("export") * ( + Cc("class") * V"ClassDecl" + + op("*") + op("^") + + Ct(V"NameList") * (sym("=") * Ct(V"ExpListLow"))^-1)) + + KeyValue: + -- {:name} shorthand expands to the key/value pair + Cfn(sym(":") * -V"SomeSpace" * V"Name" * Cp!, [[return function(name, p) + return { + {"key_literal", name}, + {"ref", name, [-1] = p}, + } + end]]) + + Ct((V"KeyName" + sym("[") * V"Exp" * sym("]") + + V"Space" * V"DoubleString" + V"Space" * V"SingleString") * + P(":") * + (V"Exp" + V"TableBlock" + V("SpaceBreak")^1 * V"Exp")) + KeyValueList: V"KeyValue" * (sym(",") * V"KeyValue")^0 + KeyValueLine: V"CheckIndent" * V"KeyValueList" * sym(",")^-1 + + FnArgsDef: sym("(") * V"White" * Ct(V("FnArgDefList")^-1) * + (key("using") * Ct(V"NameList" + V"Space" * P"nil") + Ct(P"")) * + V"White" * sym(")") + + Ct(P"") * Ct(P"") + FnArgDefList: V"FnArgDef" * + ((sym(",") + V"Break") * V"White" * V"FnArgDef")^0 * + ((sym(",") + V"Break") * V"White" * Ct(V"VarArg"))^0 + + Ct(V"VarArg") + FnArgDef: Ct((V"Name" + V"SelfName" + V"TableLit") * (sym("=") * V"Exp")^-1) + + FunLit: mark("fndef", + V"FnArgsDef" * + (sym("->") * Cc("slim") + sym("=>") * Cc("fat")) * + (V"Body" + Ct(P""))) + + NameList: V"Name" * (sym(",") * V"Name")^0 + NameOrDestructure: V"Name" + V"TableLit" + AssignableNameList: V"NameOrDestructure" * (sym(",") * V"NameOrDestructure")^0 + + ExpList: V"Exp" * (sym(",") * V"Exp")^0 + ExpListLow: V"Exp" * ((sym(",") + sym(";")) * V"Exp")^0 + + InvokeArgs: -P("-") * ( + V"ExpList" * + (sym(",") * (V"TableBlock" + V"SpaceBreak" * V"Advance" * V"ArgBlock" * V("TableBlock")^-1) + + V"TableBlock")^-1 + + V"TableBlock") + ArgBlock: V"ArgLine" * (sym(",") * V"SpaceBreak" * V"ArgLine")^0 * V"PopIndent" + ArgLine: V"CheckIndent" * V"ExpList" +} diff --git a/moonscript/parse/native.c b/moonscript/parse/native.c new file mode 100644 index 00000000..7f07c1bf --- /dev/null +++ b/moonscript/parse/native.c @@ -0,0 +1,22147 @@ +// Generated by pgen 0.1.0 + +#include +#include +#include +#include +#include +#include +#include +#include + +// moonscript_parse_native - generated parser + +// Maximum rule-call recursion depth before the parse is aborted with a Lua +// error (prevents C stack overflow on deeply nested input). Override with +// the max_depth compile option or -DPGEN_MAX_DEPTH=n +#ifndef PGEN_MAX_DEPTH +#define PGEN_MAX_DEPTH 5000 +#endif + +// --- Capture log --- +// Captures are recorded as log entries during matching and only materialized +// into Lua values after the whole parse succeeds. Backtracking rewinds the +// log length, so discarded speculative captures never touch the Lua runtime. +// The exception is Cmt: its callback runs mid-parse and its extra return +// values live on the Lua stack, referenced by PGEN_CAP_VALUE entries. +enum { + PGEN_CAP_STR, // start/len: slice of the input + PGEN_CAP_CONST, // aux: registry ref of an interned constant + PGEN_CAP_NIL, + PGEN_CAP_POS, // start: input position + PGEN_CAP_VALUE, // aux: absolute Lua stack index (Cmt results) + PGEN_CAP_TBL_OPEN, // Ct brackets + PGEN_CAP_TBL_CLOSE, + PGEN_CAP_GROUP_OPEN, // Cg brackets; aux: name index, start: input position + PGEN_CAP_GROUP_CLOSE, + PGEN_CAP_FN_OPEN, // Cfn brackets; aux: callback registry ref, start: pos + PGEN_CAP_FN_CLOSE +}; + +// Bracket kind tests: OPEN kinds and their CLOSE kinds are laid out in +// matching order after the scalar kinds +#define PGEN_CAP_IS_OPEN(k) \ + ((k) == PGEN_CAP_TBL_OPEN || (k) == PGEN_CAP_GROUP_OPEN || (k) == PGEN_CAP_FN_OPEN) +#define PGEN_CAP_IS_CLOSE(k) \ + ((k) == PGEN_CAP_TBL_CLOSE || (k) == PGEN_CAP_GROUP_CLOSE || (k) == PGEN_CAP_FN_CLOSE) + +typedef struct { + int kind; + int aux; + size_t start; + size_t len; +} PgenCap; + +// Single-slot memo for position-pure rules: pos is the memoized input +// position + 1 (0 = empty slot), endpos the resulting position or +// (size_t)-1 for failure +#define PGEN_MEMO_COUNT 10 +typedef struct { + size_t pos; + size_t endpos; +} PgenMemoSlot; + +#include + +// Indenter (match-time integer stack) infrastructure +#define PGEN_HAS_IND 1 +#define PGEN_IND_STACK_COUNT 2 +// Sentinel pushed by `prevent`: no measured width compares greater than it, +// so any nested `advance` fails +#define PGEN_IND_PREVENT_SENTINEL INT_MAX + +typedef struct { + int *items; + int size; + int cap; +} PgenIndStack; + +// Undo log entry for transactional stack operations. Rewinding the trail on +// backtrack reverses every push/pop performed since the choice point. +typedef struct { + unsigned char stack_id; + unsigned char op; // 0 = push, 1 = pop + int value; // for pop entries: the value that was popped +} PgenTrailEntry; + +typedef struct { + const char *input; + size_t input_len; + size_t pos; + bool success; + char error_message[256]; + const char *throw_label; // Label from T() or NULL for ordinary failure + size_t throw_pos; // Position where T() was thrown + size_t furthest_fail; // Furthest position where a match attempt failed + size_t depth; + int top; // Shadow of lua_gettop(L), exact between patterns + int stack_claimed; // Stack index secured so far via lua_checkstack + PgenCap *caps; // Capture log + size_t cap_len; + size_t cap_cap; + PgenMemoSlot memo[PGEN_MEMO_COUNT]; + lua_State *L; + PgenIndStack ind_stacks[PGEN_IND_STACK_COUNT]; + PgenTrailEntry *trail; + size_t trail_len; + size_t trail_cap; +} Parser; + +typedef struct { + size_t pos; + size_t cap_len; + int stack_size; + size_t trail_index; +} ParserPosition; + +typedef struct { + size_t pos; +} ParserInputPosition; + +// Set the Lua stack top, keeping the parser's shadow copy in sync. Any +// batched lua_checkstack claim beyond what survives GC stack shrinking is +// forfeited: capacity may shrink to twice the in-use size, but never below +// the runtime's minimum allocation (conservatively PGEN_STACK_FLOOR). +#define PGEN_SETTOP(parser, n) \ + do { \ + int pgen_newtop_ = (n); \ + lua_settop((parser)->L, pgen_newtop_); \ + (parser)->top = pgen_newtop_; \ + int pgen_keep_ = 2 * pgen_newtop_; \ + if (pgen_keep_ < PGEN_STACK_FLOOR) \ + pgen_keep_ = PGEN_STACK_FLOOR; \ + if ((parser)->stack_claimed > pgen_keep_) \ + (parser)->stack_claimed = pgen_keep_; \ + } while (0) + +#define REMEMBER_POSITION(parser, pp) \ + ParserPosition pp; \ + (pp).pos = (parser)->pos; \ + (pp).cap_len = (parser)->cap_len; \ + (pp).stack_size = (parser)->top; \ + (pp).trail_index = (parser)->trail_len; + +// Restore parser position +#define RESTORE_POSITION(parser, pp) \ + (parser)->pos = (pp).pos; \ + (parser)->cap_len = (pp).cap_len; \ + PGEN_SETTOP(parser, (pp).stack_size); \ + pgen_ind_trail_rewind(parser, (pp).trail_index); + +#define REMEMBER_INPUT_POSITION(parser, pp) \ + ParserInputPosition pp; \ + (pp).pos = (parser)->pos; + +#define RESTORE_INPUT_POSITION(parser, pp) \ + (parser)->pos = (pp).pos; + +// Records the furthest input position where a match attempt failed (only +// ever increases). Because the parser can only attempt a position it +// reached by matching everything before it, the furthest failure is the +// deepest progress into the input; parse() reports it when the overall +// parse fails without a label. +// +// Not recorded in single-character matchers (literal char, range, set): +// they fail constantly as the parser tries alternatives, and any position +// they fail at also gets tried by larger patterns (multi-char literals, +// tries, predicates, indent checks), so skipping them keeps the cost too +// small to measure without losing useful precision. +// +// Compile with -DPGEN_NO_FURTHEST to remove the tracking entirely (parse() +// then reports position 1 on ordinary failure). +#ifdef PGEN_NO_FURTHEST +#define PGEN_RECORD_FURTHEST(parser) ((void)0) +#else +#define PGEN_RECORD_FURTHEST(parser) \ + do { \ + if ((parser)->pos > (parser)->furthest_fail) \ + (parser)->furthest_fail = (parser)->pos; \ + } while (0) +#endif + +// Ensure the Lua stack can hold n more values. Captures are built on the Lua +// stack, so without this a large parse tree would overflow it (undefined +// behavior). Raises a Lua error when the stack cannot grow any further +// (LUAI_MAXCSTACK). +// +// Claims are batched so most calls are a single comparison against the +// shadow top. Batch size is limited to what survives GC stack shrinking: +// PUC Lua honors lua_checkstack claims for the frame's lifetime, but +// LuaJIT's GC may shrink capacity to twice the in-use size (never below +// its minimum allocation, conservatively PGEN_STACK_FLOOR). Claims above +// released stack space are forfeited by PGEN_SETTOP. +#define PGEN_STACK_BATCH 64 +#define PGEN_STACK_FLOOR 32 + +static void pgen_checkstack_slow(Parser *parser, int n) { + int batch = parser->top - n; // survives 2x-used shrink + int floor_batch = PGEN_STACK_FLOOR - (parser->top + n); // under shrink floor + if (floor_batch > batch) + batch = floor_batch; + if (batch > PGEN_STACK_BATCH) + batch = PGEN_STACK_BATCH; + if (batch < 0) + batch = 0; + if (lua_checkstack(parser->L, n + batch)) { + parser->stack_claimed = parser->top + n + batch; + } else if (lua_checkstack(parser->L, n)) { + // Batched request exceeded the stack limit; the exact one still fits + parser->stack_claimed = parser->top + n; + } else { + luaL_error(parser->L, "pgen: Lua stack overflow while building captures"); + } +} + +// Fast path: one comparison against the already-claimed capacity. n may be +// evaluated twice, so call sites must pass side-effect-free expressions. +#define pgen_checkstack(parser, n) \ + do { \ + if ((parser)->top + (n) > (parser)->stack_claimed) \ + pgen_checkstack_slow(parser, n); \ + } while (0) + +static void pgen_cap_grow(Parser *parser) { + size_t new_cap = parser->cap_cap * 2; + PgenCap *caps = (PgenCap *)realloc(parser->caps, new_cap * sizeof(PgenCap)); + if (!caps) { + luaL_error(parser->L, "pgen: out of memory growing capture log"); + } + parser->caps = caps; + parser->cap_cap = new_cap; +} + +// Append one log entry. A macro so the hot path (bounds check + four +// stores) inlines into every capture site; arguments may be evaluated +// twice, so call sites must pass side-effect-free expressions. +#define pgen_cap_push(parser, k, a, s, l) \ + do { \ + if ((parser)->cap_len == (parser)->cap_cap) \ + pgen_cap_grow(parser); \ + PgenCap *pgen_cap_ = &(parser)->caps[(parser)->cap_len++]; \ + pgen_cap_->kind = (k); \ + pgen_cap_->aux = (a); \ + pgen_cap_->start = (s); \ + pgen_cap_->len = (l); \ + } while (0) + +// Advance *i past one complete log item (a single entry, or a whole +// bracketed Ct/Cg range including anything nested) +static void pgen_cap_skip(Parser *parser, size_t *i) { + int kind = parser->caps[*i].kind; + (*i)++; + if (PGEN_CAP_IS_OPEN(kind)) { + int depth = 1; + while (depth > 0) { + kind = parser->caps[*i].kind; + if (PGEN_CAP_IS_OPEN(kind)) + depth++; + else if (PGEN_CAP_IS_CLOSE(kind)) + depth--; + (*i)++; + } + } +} + +// Reduce caps[base..] to only the nth capture value (group captures don't +// count), or to a single nil when there are fewer than n values +static void pgen_cap_select(Parser *parser, size_t base, int n) { + size_t i = base; + int count = 0; + while (i < parser->cap_len) { + if (parser->caps[i].kind == PGEN_CAP_GROUP_OPEN) { + pgen_cap_skip(parser, &i); + continue; + } + size_t item_start = i; + pgen_cap_skip(parser, &i); + count++; + if (count == n) { + size_t item_len = i - item_start; + memmove(&parser->caps[base], &parser->caps[item_start], item_len * sizeof(PgenCap)); + parser->cap_len = base + item_len; + return; + } + } + parser->cap_len = base; + pgen_cap_push(parser, PGEN_CAP_NIL, 0, 0, 0); +} + +// Match the text of the most recent visible "name" capture group at the +// current input position. Groups inside completed capture tables are not +// visible, mirroring the previous stack-based behavior where Ct consumed +// its inner captures. +static bool pgen_cap_match_back(Parser *parser, int name_idx) { + size_t i = parser->cap_len; + while (i > 0) { + i--; + int kind = parser->caps[i].kind; + if (PGEN_CAP_IS_CLOSE(kind)) { + size_t close = i; + int depth = 1; + while (depth > 0) { + i--; + int k2 = parser->caps[i].kind; + if (PGEN_CAP_IS_CLOSE(k2)) + depth++; + else if (PGEN_CAP_IS_OPEN(k2)) + depth--; + } + if (kind == PGEN_CAP_GROUP_CLOSE && parser->caps[i].aux == name_idx) { + const char *text; + size_t text_len; + size_t inner = i + 1; + if (inner == close) { + // group captured nothing: its value is the text it matched + text = parser->input + parser->caps[i].start; + text_len = parser->caps[close].start - parser->caps[i].start; + } else if (parser->caps[inner].kind == PGEN_CAP_STR) { + text = parser->input + parser->caps[inner].start; + text_len = parser->caps[inner].len; + } else if (parser->caps[inner].kind == PGEN_CAP_CONST) { + // interned constant: compare through the materialized value + bool matched = false; + pgen_checkstack(parser, 1); + lua_rawgeti(parser->L, LUA_REGISTRYINDEX, parser->caps[inner].aux); + if (lua_type(parser->L, -1) == LUA_TSTRING) { + size_t const_len; + const char *const_str = lua_tolstring(parser->L, -1, &const_len); + matched = parser->pos + const_len <= parser->input_len && + memcmp(parser->input + parser->pos, const_str, const_len) == 0; + if (matched) + parser->pos += const_len; + } + lua_pop(parser->L, 1); + return matched; + } else { + return false; // group holds a non-string value + } + if (parser->pos + text_len <= parser->input_len && + memcmp(parser->input + parser->pos, text, text_len) == 0) { + parser->pos += text_len; + return true; + } + return false; + } + } + } + return false; +} + +// Rewind the indenter trail to a previous length, undoing pushes and pops +static void pgen_ind_trail_rewind(Parser *parser, size_t index) { + while (parser->trail_len > index) { + parser->trail_len--; + PgenTrailEntry *e = &parser->trail[parser->trail_len]; + PgenIndStack *s = &parser->ind_stacks[e->stack_id]; + if (e->op == 0) { + // undo push + s->size--; + } else { + // undo pop: the slot is still allocated, restore the value + s->items[s->size] = e->value; + s->size++; + } + } +} + +static void pgen_ind_trail_record(Parser *parser, int stack_id, int op, int value) { + if (parser->trail_len >= parser->trail_cap) { + size_t new_cap = parser->trail_cap == 0 ? 64 : parser->trail_cap * 2; + PgenTrailEntry *trail = (PgenTrailEntry *)realloc(parser->trail, new_cap * sizeof(PgenTrailEntry)); + if (!trail) { + luaL_error(parser->L, "pgen: out of memory growing indenter trail"); + } + parser->trail = trail; + parser->trail_cap = new_cap; + } + parser->trail[parser->trail_len].stack_id = (unsigned char)stack_id; + parser->trail[parser->trail_len].op = (unsigned char)op; + parser->trail[parser->trail_len].value = value; + parser->trail_len++; +} + +static void pgen_ind_push(Parser *parser, int stack_id, int value) { + PgenIndStack *s = &parser->ind_stacks[stack_id]; + if (s->size >= s->cap) { + int new_cap = s->cap * 2; + int *items = (int *)realloc(s->items, new_cap * sizeof(int)); + if (!items) { + luaL_error(parser->L, "pgen: out of memory growing indenter stack"); + } + s->items = items; + s->cap = new_cap; + } + s->items[s->size++] = value; + pgen_ind_trail_record(parser, stack_id, 0, value); +} + +// Pop the stack; returns false if the stack is empty +static bool pgen_ind_pop(Parser *parser, int stack_id) { + PgenIndStack *s = &parser->ind_stacks[stack_id]; + if (s->size == 0) { + return false; + } + s->size--; + pgen_ind_trail_record(parser, stack_id, 1, s->items[s->size]); + return true; +} + +// Measure the indentation width of the run of space/tab characters at the +// current position (space = 1, tab = tab_width). Sets *end_pos to the first +// position past the run. +static int pgen_ind_measure(Parser *parser, size_t *end_pos, int tab_width) { + size_t p = parser->pos; + int width = 0; + while (p < parser->input_len) { + char c = parser->input[p]; + if (c == ' ') { + width += 1; + } else if (c == '\t') { + width += tab_width; + } else { + break; + } + p++; + } + *end_pos = p; + return width; +} + +#ifdef PGEN_DEBUG +static void dumpstack(lua_State *L) { + int top = lua_gettop(L); + for (int i = 1; i <= top; i++) { + printf("%d\t%s\t", i, luaL_typename(L, i)); + switch (lua_type(L, i)) { + case LUA_TNUMBER: + printf("%g\n", lua_tonumber(L, i)); + break; + case LUA_TSTRING: + printf("%s\n", lua_tostring(L, i)); + break; + case LUA_TBOOLEAN: + printf("%s\n", (lua_toboolean(L, i) ? "true" : "false")); + break; + case LUA_TNIL: + printf("%s\n", "nil"); + break; + default: + printf("%p\n", lua_topointer(L, i)); + break; + } + } +} +#endif + +// Named capture group names (GROUP entry aux = index here) +static const char *__cg_names[] = { + "lua_eq", + NULL // terminator +}; + +static int __cg_name_refs[1]; +// Interned constants (pushed once at module load) +static int __const_refs[53]; + +static void __const_init(lua_State *L) { + lua_pushlstring(L, "", 0); + __const_refs[0] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "assign", 6); + __const_refs[1] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "bitnot", 6); + __const_refs[2] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "break", 5); + __const_refs[3] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "call", 4); + __const_refs[4] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "case", 4); + __const_refs[5] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "chain", 5); + __const_refs[6] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "class", 5); + __const_refs[7] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "colon", 5); + __const_refs[8] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "comprehension", 13); + __const_refs[9] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "continue", 8); + __const_refs[10] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "declare_glob", 12); + __const_refs[11] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "declare_with_shadows", 20); + __const_refs[12] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "do", 2); + __const_refs[13] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "dot", 3); + __const_refs[14] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "else", 4); + __const_refs[15] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "elseif", 6); + __const_refs[16] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "explist", 7); + __const_refs[17] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "export", 6); + __const_refs[18] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "fat", 3); + __const_refs[19] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "fndef", 5); + __const_refs[20] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "for", 3); + __const_refs[21] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "foreach", 7); + __const_refs[22] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "if", 2); + __const_refs[23] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "import", 6); + __const_refs[24] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "index", 5); + __const_refs[25] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "interpolate", 11); + __const_refs[26] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "key_literal", 11); + __const_refs[27] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "length", 6); + __const_refs[28] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "minus", 5); + __const_refs[29] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "not", 3); + __const_refs[30] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "number", 6); + __const_refs[31] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "parens", 6); + __const_refs[32] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "props", 5); + __const_refs[33] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "ref", 3); + __const_refs[34] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "return", 6); + __const_refs[35] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "self", 4); + __const_refs[36] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "self.__class", 12); + __const_refs[37] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "self_class", 10); + __const_refs[38] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "slice", 5); + __const_refs[39] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "slim", 4); + __const_refs[40] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "stm", 3); + __const_refs[41] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "string", 6); + __const_refs[42] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "switch", 6); + __const_refs[43] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "table", 5); + __const_refs[44] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "tblcomprehension", 16); + __const_refs[45] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "unless", 6); + __const_refs[46] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "unpack", 6); + __const_refs[47] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "update", 6); + __const_refs[48] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "when", 4); + __const_refs[49] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "while", 5); + __const_refs[50] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushlstring(L, "with", 4); + __const_refs[51] = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushinteger(L, 1); + __const_refs[52] = luaL_ref(L, LUA_REGISTRYINDEX); + for (int i = 0; __cg_names[i] != NULL; i++) { + lua_pushstring(L, __cg_names[i]); + __cg_name_refs[i] = luaL_ref(L, LUA_REGISTRYINDEX); + } +} +// --- Capture log evaluation --- + +static int pgen_cap_eval(Parser *parser, size_t *i); + +// Push the single value a capture group produces: its first inner capture +// value, or the text it matched when its contents produce no values +static void pgen_cap_eval_group(Parser *parser, size_t *i) { + size_t open = *i; + pgen_cap_skip(parser, i); // *i now points past GROUP_CLOSE + size_t close = *i - 1; + + size_t j = open + 1; + while (j < close) { + int produced = pgen_cap_eval(parser, &j); + if (produced > 0) { + if (produced > 1) { + // keep only the first value + lua_pop(parser->L, produced - 1); + parser->top -= produced - 1; + } + return; + } + } + + // no values: the group's value is the text it matched + size_t start = parser->caps[open].start; + pgen_checkstack(parser, 1); + lua_pushlstring(parser->L, parser->input + start, parser->caps[close].start - start); + parser->top++; +} + +// Materialize one log item (entry or bracketed range) at *i, advancing *i +// past the item. Returns the number of Lua values pushed: always 1 except +// for transform captures, whose callbacks may return any number of values. +static int pgen_cap_eval(Parser *parser, size_t *i) { + PgenCap *cap = &parser->caps[*i]; + switch (cap->kind) { + case PGEN_CAP_STR: + pgen_checkstack(parser, 1); + lua_pushlstring(parser->L, parser->input + cap->start, cap->len); + parser->top++; + (*i)++; + return 1; + case PGEN_CAP_CONST: + pgen_checkstack(parser, 1); + lua_rawgeti(parser->L, LUA_REGISTRYINDEX, cap->aux); + parser->top++; + (*i)++; + return 1; + case PGEN_CAP_NIL: + pgen_checkstack(parser, 1); + lua_pushnil(parser->L); + parser->top++; + (*i)++; + return 1; + case PGEN_CAP_POS: + pgen_checkstack(parser, 1); + lua_pushinteger(parser->L, (lua_Integer)(cap->start + 1)); + parser->top++; + (*i)++; + return 1; + case PGEN_CAP_VALUE: + pgen_checkstack(parser, 1); + lua_pushvalue(parser->L, cap->aux); + parser->top++; + (*i)++; + return 1; + case PGEN_CAP_GROUP_OPEN: + pgen_cap_eval_group(parser, i); + return 1; + case PGEN_CAP_FN_OPEN: { + // Transform capture: inner values become arguments, the callback's + // return values become the capture values (innermost-first order falls + // out of the recursion here) + size_t open = *i; + int func_base = parser->top; + pgen_checkstack(parser, 1); + lua_rawgeti(parser->L, LUA_REGISTRYINDEX, cap->aux); + parser->top++; + + int nargs = 0; + size_t j = open + 1; + while (parser->caps[j].kind != PGEN_CAP_FN_CLOSE) { + if (parser->caps[j].kind == PGEN_CAP_GROUP_OPEN) { + // named groups are not visible as arguments (as at the top level) + pgen_cap_skip(parser, &j); + } else { + nargs += pgen_cap_eval(parser, &j); + } + } + + if (nargs == 0) { + // no inner captures: the callback receives the matched text + size_t start = parser->caps[open].start; + pgen_checkstack(parser, 1); + lua_pushlstring(parser->L, parser->input + start, parser->caps[j].start - start); + nargs = 1; + } + + // lua_call propagates errors (aborts materialization on Lua error) + lua_call(parser->L, nargs, LUA_MULTRET); + parser->top = lua_gettop(parser->L); + if (parser->stack_claimed > parser->top) + parser->stack_claimed = parser->top; + + *i = j + 1; // past FN_CLOSE + return parser->top - func_base; + } + default: { // PGEN_CAP_TBL_OPEN + // No presizing: counting items would re-walk every nested subtree at + // each nesting level, which costs more than letting the table grow + pgen_checkstack(parser, 3); + lua_createtable(parser->L, 0, 0); + parser->top++; + int table_idx = parser->top; + + size_t j = *i + 1; + int array_idx = 1; + while (parser->caps[j].kind != PGEN_CAP_TBL_CLOSE) { + if (parser->caps[j].kind == PGEN_CAP_GROUP_OPEN) { + pgen_checkstack(parser, 2); + lua_rawgeti(parser->L, LUA_REGISTRYINDEX, __cg_name_refs[parser->caps[j].aux]); + parser->top++; + pgen_cap_eval_group(parser, &j); + lua_rawset(parser->L, table_idx); + parser->top -= 2; + } else { + // rawseti pops the top value, so multi-value items assign their + // indexes in reverse + int produced = pgen_cap_eval(parser, &j); + for (int v = produced - 1; v >= 0; v--) { + lua_rawseti(parser->L, table_idx, array_idx + v); + } + array_idx += produced; + parser->top -= produced; + } + } + *i = j + 1; // past TBL_CLOSE + return 1; + } + } +} + +// Run a match-time capture: materialize the inner captures, call the +// callback with (subject, pos, ...captures), and interpret its results per +// lpeg semantics: position/true = success, false/nil = failure, extra +// return values become captures (kept on the Lua stack) +static void pgen_run_cmt(Parser *parser, int func_ref, size_t start_pos, size_t cap_base, int top_base) { + lua_State *L = parser->L; + size_t pos_after_inner = parser->pos; + int leftovers = parser->top - top_base; // nested Cmt values still on the stack + + pgen_checkstack(parser, 3); + lua_rawgeti(L, LUA_REGISTRYINDEX, func_ref); + lua_pushlstring(L, parser->input, parser->input_len); + lua_pushinteger(L, (lua_Integer)(pos_after_inner + 1)); // 1-based + parser->top += 3; + + int nargs = 2; + size_t i = cap_base; + while (i < parser->cap_len) { + if (parser->caps[i].kind == PGEN_CAP_GROUP_OPEN) { + // named groups only matter inside Ct; they aren't passed as arguments + pgen_cap_skip(parser, &i); + } else { + nargs += pgen_cap_eval(parser, &i); + } + } + parser->cap_len = cap_base; // consume the inner captures + + // lua_call propagates errors (aborts parse on Lua error) + lua_call(L, nargs, LUA_MULTRET); + parser->top = lua_gettop(L); + if (parser->stack_claimed > parser->top) + parser->stack_claimed = parser->top; + + int returns_count = parser->top - (top_base + leftovers); + + if (returns_count == 0) { + // No return value = match fails + parser->success = false; + PGEN_RECORD_FURTHEST(parser); // record at pos_after_inner, before rewind + parser->pos = start_pos; + } else { + int first = top_base + leftovers + 1; + int first_type = lua_type(L, first); + if (first_type == LUA_TNUMBER) { + // Number = new position (1-based from Lua) + lua_Integer new_pos = lua_tointeger(L, first) - 1; + // Per lpeg: must be in range [pos_after_inner, input_len] + if (new_pos >= (lua_Integer)pos_after_inner && new_pos <= (lua_Integer)parser->input_len) { + parser->pos = (size_t)new_pos; + parser->success = true; + } else { + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + parser->pos = start_pos; + } + } else if (first_type == LUA_TBOOLEAN && lua_toboolean(L, first)) { + // true = succeed without consuming (position stays at pos_after_inner) + parser->success = true; + } else { + // false, nil, or other = fail + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + parser->pos = start_pos; + } + } + + if (parser->success && returns_count > 1) { + // Drop leftover nested-Cmt slots and the first return value; the + // remaining returns stay on the stack as the new captures + for (int r = 0; r < leftovers + 1; r++) { + lua_remove(L, top_base + 1); + } + parser->top -= leftovers + 1; + if (parser->stack_claimed > parser->top) + parser->stack_claimed = parser->top; + int extras = returns_count - 1; + for (int r = 0; r < extras; r++) { + pgen_cap_push(parser, PGEN_CAP_VALUE, top_base + 1 + r, 0, 0); + } + } else { + PGEN_SETTOP(parser, top_base); + } +} +// Callback (Cmt/Cfn) infrastructure + +static const char __cmt_code_0[] = " local tree = require(\"moonscript.parse.tree\")\n return function(lhs, assign)\n return tree.format_single_assign(lhs, assign)\n end"; +static const char __cmt_code_1[] = "return function(stm, dec)\n if dec then\n return {\"decorated\", stm, dec}\n end\n return stm\n end"; +static const char __cmt_code_2[] = "return function(p, value)\n if type(value) == \"table\" then\n value[-1] = p\n end\n return value\n end"; +static const char __cmt_code_3[] = " local tree = require(\"moonscript.parse.tree\")\n return function(lhs, assign)\n return tree.format_assign(lhs, assign)\n end"; +static const char __cmt_code_4[] = "return function(name, p)\n return {\n {\"key_literal\", name},\n {\"ref\", name, [-1] = p},\n }\n end"; +static const char __cmt_code_5[] = " local tree = require(\"moonscript.parse.tree\")\n return function(callee, args)\n return tree.join_chain(callee, args)\n end"; +static const char __cmt_code_6[] = "return function(...)\n if select(\"#\", ...) == 1 then\n return ...\n end\n return {\"exp\", ...}\n end"; +static const char __cmt_code_7[] = "return function(eq_start, eq_end, content)\n return {\"string\", \"[\" .. (\"=\"):rep(eq_end - eq_start) .. \"[\", content}\n end"; +static const char __cmt_code_8[] = " local subject, pos, node = ...\n local last = node[#node]\n local t = type(last) == \"table\" and last[1]\n if t == \"dot\" or t == \"index\" or t == \"slice\" then\n return pos, node\n end\n return false\n "; + +static int __cmt_refs[9]; + +// Initialize callbacks by loading their Lua code +static void __cmt_init(lua_State *L) { + if (luaL_loadstring(L, __cmt_code_0) != 0) { + luaL_error(L, "Failed to load Cfn callback 0: %s", lua_tostring(L, -1)); + } + if (lua_pcall(L, 0, 1, 0) != 0) { + luaL_error(L, "Failed to run Cfn chunk 0: %s", lua_tostring(L, -1)); + } + if (!lua_isfunction(L, -1)) { + luaL_error(L, "Cfn chunk 0 did not return a function"); + } + __cmt_refs[0] = luaL_ref(L, LUA_REGISTRYINDEX); + if (luaL_loadstring(L, __cmt_code_1) != 0) { + luaL_error(L, "Failed to load Cfn callback 1: %s", lua_tostring(L, -1)); + } + if (lua_pcall(L, 0, 1, 0) != 0) { + luaL_error(L, "Failed to run Cfn chunk 1: %s", lua_tostring(L, -1)); + } + if (!lua_isfunction(L, -1)) { + luaL_error(L, "Cfn chunk 1 did not return a function"); + } + __cmt_refs[1] = luaL_ref(L, LUA_REGISTRYINDEX); + if (luaL_loadstring(L, __cmt_code_2) != 0) { + luaL_error(L, "Failed to load Cfn callback 2: %s", lua_tostring(L, -1)); + } + if (lua_pcall(L, 0, 1, 0) != 0) { + luaL_error(L, "Failed to run Cfn chunk 2: %s", lua_tostring(L, -1)); + } + if (!lua_isfunction(L, -1)) { + luaL_error(L, "Cfn chunk 2 did not return a function"); + } + __cmt_refs[2] = luaL_ref(L, LUA_REGISTRYINDEX); + if (luaL_loadstring(L, __cmt_code_3) != 0) { + luaL_error(L, "Failed to load Cfn callback 3: %s", lua_tostring(L, -1)); + } + if (lua_pcall(L, 0, 1, 0) != 0) { + luaL_error(L, "Failed to run Cfn chunk 3: %s", lua_tostring(L, -1)); + } + if (!lua_isfunction(L, -1)) { + luaL_error(L, "Cfn chunk 3 did not return a function"); + } + __cmt_refs[3] = luaL_ref(L, LUA_REGISTRYINDEX); + if (luaL_loadstring(L, __cmt_code_4) != 0) { + luaL_error(L, "Failed to load Cfn callback 4: %s", lua_tostring(L, -1)); + } + if (lua_pcall(L, 0, 1, 0) != 0) { + luaL_error(L, "Failed to run Cfn chunk 4: %s", lua_tostring(L, -1)); + } + if (!lua_isfunction(L, -1)) { + luaL_error(L, "Cfn chunk 4 did not return a function"); + } + __cmt_refs[4] = luaL_ref(L, LUA_REGISTRYINDEX); + if (luaL_loadstring(L, __cmt_code_5) != 0) { + luaL_error(L, "Failed to load Cfn callback 5: %s", lua_tostring(L, -1)); + } + if (lua_pcall(L, 0, 1, 0) != 0) { + luaL_error(L, "Failed to run Cfn chunk 5: %s", lua_tostring(L, -1)); + } + if (!lua_isfunction(L, -1)) { + luaL_error(L, "Cfn chunk 5 did not return a function"); + } + __cmt_refs[5] = luaL_ref(L, LUA_REGISTRYINDEX); + if (luaL_loadstring(L, __cmt_code_6) != 0) { + luaL_error(L, "Failed to load Cfn callback 6: %s", lua_tostring(L, -1)); + } + if (lua_pcall(L, 0, 1, 0) != 0) { + luaL_error(L, "Failed to run Cfn chunk 6: %s", lua_tostring(L, -1)); + } + if (!lua_isfunction(L, -1)) { + luaL_error(L, "Cfn chunk 6 did not return a function"); + } + __cmt_refs[6] = luaL_ref(L, LUA_REGISTRYINDEX); + if (luaL_loadstring(L, __cmt_code_7) != 0) { + luaL_error(L, "Failed to load Cfn callback 7: %s", lua_tostring(L, -1)); + } + if (lua_pcall(L, 0, 1, 0) != 0) { + luaL_error(L, "Failed to run Cfn chunk 7: %s", lua_tostring(L, -1)); + } + if (!lua_isfunction(L, -1)) { + luaL_error(L, "Cfn chunk 7 did not return a function"); + } + __cmt_refs[7] = luaL_ref(L, LUA_REGISTRYINDEX); + if (luaL_loadstring(L, __cmt_code_8) != 0) { + luaL_error(L, "Failed to load Cmt callback 8: %s", lua_tostring(L, -1)); + } + __cmt_refs[8] = luaL_ref(L, LUA_REGISTRYINDEX); +} + +// Forward declarations +static bool parse_Root(Parser *parser); +static bool parse_Advance(Parser *parser); +static bool parse_ArgBlock(Parser *parser); +static bool parse_ArgLine(Parser *parser); +static bool parse_Assign(Parser *parser); +static bool parse_Assignable(Parser *parser); +static bool parse_AssignableNameList(Parser *parser); +static bool parse_BinaryOperator(Parser *parser); +static bool parse_Block(Parser *parser); +static bool parse_Body(Parser *parser); +static bool parse_Break(Parser *parser); +static bool parse_BreakLoop(Parser *parser); +static bool parse_Callable(Parser *parser); +static bool parse_Chain(Parser *parser); +static bool parse_ChainItem(Parser *parser); +static bool parse_ChainItems(Parser *parser); +static bool parse_ChainValue(Parser *parser); +static bool parse_CharOperators(Parser *parser); +static bool parse_CheckIndent(Parser *parser); +static bool parse_ClassBlock(Parser *parser); +static bool parse_ClassDecl(Parser *parser); +static bool parse_ClassLine(Parser *parser); +static bool parse_ColonChain(Parser *parser); +static bool parse_ColonChainItem(Parser *parser); +static bool parse_Comment(Parser *parser); +static bool parse_CompClause(Parser *parser); +static bool parse_CompFor(Parser *parser); +static bool parse_CompForEach(Parser *parser); +static bool parse_CompInner(Parser *parser); +static bool parse_Comprehension(Parser *parser); +static bool parse_Do(Parser *parser); +static bool parse_DotChainItem(Parser *parser); +static bool parse_DoubleString(Parser *parser); +static bool parse_DoubleStringInner(Parser *parser); +static bool parse_DoubleStringInterp(Parser *parser); +static bool parse_EmptyLine(Parser *parser); +static bool parse_Exp(Parser *parser); +static bool parse_ExpList(Parser *parser); +static bool parse_ExpListLow(Parser *parser); +static bool parse_Export(Parser *parser); +static bool parse_File(Parser *parser); +static bool parse_FnArgDef(Parser *parser); +static bool parse_FnArgDefList(Parser *parser); +static bool parse_FnArgs(Parser *parser); +static bool parse_FnArgsDef(Parser *parser); +static bool parse_FnArgsExpList(Parser *parser); +static bool parse_For(Parser *parser); +static bool parse_ForEach(Parser *parser); +static bool parse_FunLit(Parser *parser); +static bool parse_If(Parser *parser); +static bool parse_IfCond(Parser *parser); +static bool parse_IfElse(Parser *parser); +static bool parse_IfElseIf(Parser *parser); +static bool parse_Import(Parser *parser); +static bool parse_ImportName(Parser *parser); +static bool parse_ImportNameList(Parser *parser); +static bool parse_InBlock(Parser *parser); +static bool parse_Invoke(Parser *parser); +static bool parse_InvokeArgs(Parser *parser); +static bool parse_KeyName(Parser *parser); +static bool parse_KeyValue(Parser *parser); +static bool parse_KeyValueLine(Parser *parser); +static bool parse_KeyValueList(Parser *parser); +static bool parse_Line(Parser *parser); +static bool parse_Local(Parser *parser); +static bool parse_LuaString(Parser *parser); +static bool parse_LuaStringClose(Parser *parser); +static bool parse_Name(Parser *parser); +static bool parse_NameList(Parser *parser); +static bool parse_NameOrDestructure(Parser *parser); +static bool parse_NameRaw(Parser *parser); +static bool parse_Num(Parser *parser); +static bool parse_Parens(Parser *parser); +static bool parse_PopIndent(Parser *parser); +static bool parse_PreventIndent(Parser *parser); +static bool parse_PushIndent(Parser *parser); +static bool parse_Return(Parser *parser); +static bool parse_SelfName(Parser *parser); +static bool parse_Shebang(Parser *parser); +static bool parse_SimpleValue(Parser *parser); +static bool parse_SingleString(Parser *parser); +static bool parse_Slice(Parser *parser); +static bool parse_SliceValue(Parser *parser); +static bool parse_SomeSpace(Parser *parser); +static bool parse_Space(Parser *parser); +static bool parse_SpaceBreak(Parser *parser); +static bool parse_Statement(Parser *parser); +static bool parse_Stop(Parser *parser); +static bool parse_String(Parser *parser); +static bool parse_Switch(Parser *parser); +static bool parse_SwitchBlock(Parser *parser); +static bool parse_SwitchCase(Parser *parser); +static bool parse_SwitchElse(Parser *parser); +static bool parse_TableBlock(Parser *parser); +static bool parse_TableBlockInner(Parser *parser); +static bool parse_TableLit(Parser *parser); +static bool parse_TableLitLine(Parser *parser); +static bool parse_TableValue(Parser *parser); +static bool parse_TableValueList(Parser *parser); +static bool parse_TblComprehension(Parser *parser); +static bool parse_Unless(Parser *parser); +static bool parse_Update(Parser *parser); +static bool parse_Value(Parser *parser); +static bool parse_VarArg(Parser *parser); +static bool parse_While(Parser *parser); +static bool parse_White(Parser *parser); +static bool parse_With(Parser *parser); +static bool parse_WithExp(Parser *parser); +static bool parse_WordOperators(Parser *parser); + +// Rule functions +static bool parse_Root(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Root", start); +#endif + + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + parse_White(parser); + if (parser->success) { + parse_File(parser); + if (parser->success) { + parse_White(parser); + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match any 1 characters + if (parser->pos + 1 <= parser->input_len) { + parser->pos += 1; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected at least 1 more characters at position %zu", parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Root", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Root", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Advance(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Advance", start); +#endif + + { // Indenter advance (stack 0): push width if deeper than top, consume nothing + size_t ind_end; + int ind_width = pgen_ind_measure(parser, &ind_end, 4); + (void)ind_end; + PgenIndStack *ind_s = &parser->ind_stacks[0]; + if (ind_s->size > 0 && ind_width > ind_s->items[ind_s->size - 1]) { + pgen_ind_push(parser, 0, ind_width); + } else { + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Indent width %d does not advance current level at position %zu", ind_width, parser->pos); +#endif + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Advance", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Advance", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_ArgBlock(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "ArgBlock", start); +#endif + + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_ArgLine(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_SpaceBreak(parser); + if (parser->success) { + parse_ArgLine(parser); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (parser->success) { + parse_PopIndent(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "ArgBlock", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "ArgBlock", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_ArgLine(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "ArgLine", start); +#endif + + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_CheckIndent(parser); + if (parser->success) { + parse_ExpList(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "ArgLine", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "ArgLine", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Assign(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Assign", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[1], 0, 0); // "assign" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "=" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 61) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "=" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Choice + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Choice + { // Choice + parse_With(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_If(parser); + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Switch(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Choice + parse_TableBlock(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_ExpListLow(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Assign", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Assign", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Assignable(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Assignable", start); +#endif + + { // Choice + { // Choice + { // Match-time capture (Cmt id=8) + size_t cmt_cap_base = parser->cap_len; + int cmt_top_base = parser->top; + size_t cmt_start_pos = parser->pos; +#ifdef PGEN_HAS_IND + size_t cmt_trail_index = parser->trail_len; +#endif + + parse_Chain(parser); + + if (parser->success) { + pgen_run_cmt(parser, __cmt_refs[8], cmt_start_pos, cmt_cap_base, cmt_top_base); + +#ifdef PGEN_HAS_IND + // Callback rejected the match: undo indenter operations performed by the + // inner pattern (an inner failure rewinds itself) + if (!parser->success) { + pgen_ind_trail_rewind(parser, cmt_trail_index); + } +#endif + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Name(parser); + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_SelfName(parser); + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Assignable", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Assignable", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_AssignableNameList(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "AssignableNameList", start); +#endif + + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_NameOrDestructure(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_NameOrDestructure(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "AssignableNameList", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "AssignableNameList", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_BinaryOperator(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "BinaryOperator", start); +#endif + + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Choice + parse_WordOperators(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_CharOperators(parser); + } + } + if (parser->success) { + { // Zero or more repetitions + while (true) { + parse_SpaceBreak(parser); + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "BinaryOperator", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "BinaryOperator", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Block(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Block", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Line(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // At least 1 repetitions + REMEMBER_INPUT_POSITION(parser, pos); + size_t rep_count = 0; + + while (true) { + parse_Break(parser); + + if (!parser->success) { + break; + } + + rep_count += 1; + } + + // Don't recover if labeled failure was thrown + if (parser->throw_label) { + // Keep failure state, propagate labeled failure + } else if (rep_count >= 1) { + parser->success = true; + } else { + RESTORE_INPUT_POSITION(parser, pos); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected 1 repetitions at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + parse_Line(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Block", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Block", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Body(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Body", start); +#endif + + { // Choice + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + parse_Break(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + parse_EmptyLine(parser); + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (parser->success) { + parse_InBlock(parser); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_Statement(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Body", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Body", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Break(Parser *parser) { + size_t start = parser->pos; + // Position-pure rule (no captures, labels, or other state): a + // single-slot memo short-circuits the repeated calls that backtracking + // alternatives make at the same position + if (parser->memo[0].pos == start + 1) { + if (parser->memo[0].endpos == (size_t)-1) { + parser->success = false; + return false; + } + parser->pos = parser->memo[0].endpos; + parser->success = true; + return true; + } + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Break", start); +#endif + + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Match single character "\r" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 13) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "\\r" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + { // Match single character "\n" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 10) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "\\n" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Break", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Break", parser->pos); + } +#endif + parser->memo[0].pos = start + 1; + parser->memo[0].endpos = parser->success ? parser->pos : (size_t)-1; + + parser->depth -= 1; + return parser->success; +} + +static bool parse_BreakLoop(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "BreakLoop", start); +#endif + + { // Choice + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match literal "break" + if (parser->pos + 5 <= parser->input_len && + memcmp(parser->input + parser->pos, "break", 5) == 0) { + parser->pos += 5; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "break" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[3], 0, 0); // "break" + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match literal "continue" + if (parser->pos + 8 <= parser->input_len && + memcmp(parser->input + parser->pos, "continue", 8) == 0) { + parser->pos += 8; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "continue" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[10], 0, 0); // "continue" + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "BreakLoop", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "BreakLoop", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Callable(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Callable", start); +#endif + + { // FIRST-byte dispatched ordered choice + unsigned long long pgen_dispatch_mask; + if (parser->pos < parser->input_len) { + switch ((unsigned char)parser->input[parser->pos]) { + case 64: + pgen_dispatch_mask = 0x0000000000000003ULL; + break; + case 46: + pgen_dispatch_mask = 0x0000000000000005ULL; + break; + case 40: + pgen_dispatch_mask = 0x0000000000000009ULL; + break; + case 9: + case 32: + case 45: + pgen_dispatch_mask = 0x000000000000000fULL; + break; + default: + pgen_dispatch_mask = 0x0000000000000001ULL; + break; + } + } else { + pgen_dispatch_mask = 0x0000000000000001ULL; + } + + parser->success = false; + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 0))) { + parser->success = true; + { // Transform Capture (Cfn id=2) + size_t fn_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_FN_OPEN, __cmt_refs[2], parser->pos, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Position Capture + pgen_cap_push(parser, PGEN_CAP_POS, 0, parser->pos, 0); + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[34], 0, 0); // "ref" + } + if (parser->success) { + parse_Name(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_FN_CLOSE, 0, parser->pos, 0); + } else { + parser->cap_len = fn_cap_start; + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 1))) { + if ((pgen_dispatch_mask & 0x0000000000000001ULL) != 0x0000000000000001ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_SelfName(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 2))) { + if ((pgen_dispatch_mask & 0x0000000000000003ULL) != 0x0000000000000003ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_VarArg(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 3))) { + if ((pgen_dispatch_mask & 0x0000000000000007ULL) != 0x0000000000000007ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[32], 0, 0); // "parens" + } + if (parser->success) { + parse_Parens(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success && !parser->throw_label) { + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + if (pgen_dispatch_mask != 0x000000000000000fULL) { + // Some alternatives were skipped: replay the whole choice in original + // order so error_message reports the same failure the undispatched + // parser would. Every alternative fails, so this only affects error + // state. + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Transform Capture (Cfn id=2) + size_t fn_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_FN_OPEN, __cmt_refs[2], parser->pos, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Position Capture + pgen_cap_push(parser, PGEN_CAP_POS, 0, parser->pos, 0); + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[34], 0, 0); // "ref" + } + if (parser->success) { + parse_Name(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_FN_CLOSE, 0, parser->pos, 0); + } else { + parser->cap_len = fn_cap_start; + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_SelfName(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_VarArg(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[32], 0, 0); // "parens" + } + if (parser->success) { + parse_Parens(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } +#endif + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Callable", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Callable", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Chain(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Chain", start); +#endif + + { // Choice + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[6], 0, 0); // "chain" + } + if (parser->success) { + { // Choice + { // Choice + parse_Callable(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_String(parser); + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character set ".\\" + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 46: /* "." */ + case 92: /* "\\" */ + parser->pos++; + break; + default: +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\".\\\\\"" + " at position %zu", + parser->pos); +#endif + parser->success = false; + } + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\".\\\\\"" + " at position %zu but reached end of input", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + } + } + if (parser->success) { + parse_ChainItems(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[6], 0, 0); // "chain" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Choice + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_DotChainItem(parser); + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + parse_ChainItems(parser); + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_ColonChain(parser); + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Chain", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Chain", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_ChainItem(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "ChainItem", start); +#endif + + { // FIRST-byte dispatched ordered choice + unsigned long long pgen_dispatch_mask; + if (parser->pos < parser->input_len) { + switch ((unsigned char)parser->input[parser->pos]) { + case 46: + pgen_dispatch_mask = 0x0000000000000003ULL; + break; + case 91: + pgen_dispatch_mask = 0x000000000000000dULL; + break; + default: + pgen_dispatch_mask = 0x0000000000000001ULL; + break; + } + } else { + pgen_dispatch_mask = 0x0000000000000001ULL; + } + + parser->success = false; + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 0))) { + parser->success = true; + parse_Invoke(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 1))) { + if ((pgen_dispatch_mask & 0x0000000000000001ULL) != 0x0000000000000001ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_DotChainItem(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 2))) { + if ((pgen_dispatch_mask & 0x0000000000000003ULL) != 0x0000000000000003ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_Slice(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 3))) { + if ((pgen_dispatch_mask & 0x0000000000000007ULL) != 0x0000000000000007ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[25], 0, 0); // "index" + } + if (parser->success) { + { // Match single character "[" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 91) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "[" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_Exp(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "]" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 93) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "]" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + if (pgen_dispatch_mask != 0x000000000000000fULL) { + // Some alternatives were skipped: replay the whole choice in original + // order so error_message reports the same failure the undispatched + // parser would. Every alternative fails, so this only affects error + // state. + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Invoke(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_DotChainItem(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Slice(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[25], 0, 0); // "index" + } + if (parser->success) { + { // Match single character "[" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 91) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "[" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_Exp(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "]" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 93) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "]" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + } +#endif + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "ChainItem", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "ChainItem", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_ChainItems(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "ChainItems", start); +#endif + + { // Choice + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // At least 1 repetitions + REMEMBER_POSITION(parser, pos); + size_t rep_count = 0; + + while (true) { + parse_ChainItem(parser); + + if (!parser->success) { + break; + } + + rep_count += 1; + } + + // Don't recover if labeled failure was thrown + if (parser->throw_label) { + // Keep failure state, propagate labeled failure + } else if (rep_count >= 1) { + parser->success = true; + } else { + RESTORE_POSITION(parser, pos); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected 1 repetitions at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + parse_ColonChain(parser); + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_ColonChain(parser); + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "ChainItems", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "ChainItems", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_ChainValue(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "ChainValue", start); +#endif + + { // Transform Capture (Cfn id=5) + size_t fn_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_FN_OPEN, __cmt_refs[5], parser->pos, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Choice + parse_Chain(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Callable(parser); + } + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + parse_InvokeArgs(parser); + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_FN_CLOSE, 0, parser->pos, 0); + } else { + parser->cap_len = fn_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "ChainValue", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "ChainValue", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_CharOperators(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "CharOperators", start); +#endif + + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match character set "+-*\/%^><|&" + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 43: /* "+" */ + case 45: /* "-" */ + case 42: /* "*" */ + case 47: /* "/" */ + case 37: /* "%" */ + case 94: /* "^" */ + case 62: /* ">" */ + case 60: /* "<" */ + case 124: /* "|" */ + case 38: /* "&" */ + parser->pos++; + break; + default: +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\"+-*/%^><|&\"" + " at position %zu", + parser->pos); +#endif + parser->success = false; + } + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\"+-*/%^><|&\"" + " at position %zu but reached end of input", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "CharOperators", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "CharOperators", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_CheckIndent(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "CheckIndent", start); +#endif + + { // Indenter check (stack 0): consume whitespace, width must equal top + size_t ind_end; + int ind_width = pgen_ind_measure(parser, &ind_end, 4); + PgenIndStack *ind_s = &parser->ind_stacks[0]; + if (ind_s->size > 0 && ind_s->items[ind_s->size - 1] == ind_width) { + parser->pos = ind_end; + } else { + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Indent width %d does not match current level at position %zu", ind_width, parser->pos); +#endif + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "CheckIndent", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "CheckIndent", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_ClassBlock(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "ClassBlock", start); +#endif + + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + { // At least 1 repetitions + REMEMBER_INPUT_POSITION(parser, pos); + size_t rep_count = 0; + + while (true) { + parse_SpaceBreak(parser); + + if (!parser->success) { + break; + } + + rep_count += 1; + } + + // Don't recover if labeled failure was thrown + if (parser->throw_label) { + // Keep failure state, propagate labeled failure + } else if (rep_count >= 1) { + parser->success = true; + } else { + RESTORE_INPUT_POSITION(parser, pos); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected 1 repetitions at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + parse_Advance(parser); + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_ClassLine(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // At least 1 repetitions + REMEMBER_INPUT_POSITION(parser, pos); + size_t rep_count = 0; + + while (true) { + parse_SpaceBreak(parser); + + if (!parser->success) { + break; + } + + rep_count += 1; + } + + // Don't recover if labeled failure was thrown + if (parser->throw_label) { + // Keep failure state, propagate labeled failure + } else if (rep_count >= 1) { + parser->success = true; + } else { + RESTORE_INPUT_POSITION(parser, pos); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected 1 repetitions at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + parse_ClassLine(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + parse_PopIndent(parser); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "ClassBlock", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "ClassBlock", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_ClassDecl(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "ClassDecl", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 8 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[7], 0, 0); // "class" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "class" + if (parser->pos + 5 <= parser->input_len && + memcmp(parser->input + parser->pos, "class", 5) == 0) { + parser->pos += 5; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "class" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match single character ":" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 58) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + ":" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Choice + parse_Assignable(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_NIL, 0, 0, 0); + } + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Choice + { // Sequence with 6 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match literal "extends" + if (parser->pos + 7 <= parser->input_len && + memcmp(parser->input + parser->pos, "extends", 7) == 0) { + parser->pos += 7; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "extends" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + parse_PreventIndent(parser); + if (parser->success) { + parse_Exp(parser); + if (parser->success) { + parse_PopIndent(parser); + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture + size_t start_pos = parser->pos; + { // Match literal "" + if (parser->pos + 0 <= parser->input_len && + memcmp(parser->input + parser->pos, "", 0) == 0) { + parser->pos += 0; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + { // Choice + parse_ClassBlock(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Match literal "" + if (parser->pos + 0 <= parser->input_len && + memcmp(parser->input + parser->pos, "", 0) == 0) { + parser->pos += 0; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "ClassDecl", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "ClassDecl", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_ClassLine(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "ClassLine", start); +#endif + + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_CheckIndent(parser); + if (parser->success) { + { // Choice + { // Choice + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[33], 0, 0); // "props" + } + if (parser->success) { + parse_KeyValueList(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[41], 0, 0); // "stm" + } + if (parser->success) { + parse_Statement(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[41], 0, 0); // "stm" + } + if (parser->success) { + parse_Exp(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "ClassLine", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "ClassLine", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_ColonChain(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "ColonChain", start); +#endif + + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_ColonChainItem(parser); + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Invoke(parser); + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + parse_ChainItems(parser); + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "ColonChain", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "ColonChain", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_ColonChainItem(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "ColonChainItem", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[8], 0, 0); // "colon" + } + if (parser->success) { + { // Match single character "\\" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 92) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "\\\\" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_NameRaw(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "ColonChainItem", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "ColonChainItem", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Comment(Parser *parser) { + size_t start = parser->pos; + // Position-pure rule (no captures, labels, or other state): a + // single-slot memo short-circuits the repeated calls that backtracking + // alternatives make at the same position + if (parser->memo[1].pos == start + 1) { + if (parser->memo[1].endpos == (size_t)-1) { + parser->success = false; + return false; + } + parser->pos = parser->memo[1].endpos; + parser->success = true; + return true; + } + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Comment", start); +#endif + + { // Sequence with 3 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match literal "--" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "--", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "--" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character set "\r\n" + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 13: /* "\r" */ + case 10: /* "\n" */ + parser->pos++; + break; + default: +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\"\\r\\n\"" + " at position %zu", + parser->pos); +#endif + parser->success = false; + } + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\"\\r\\n\"" + " at position %zu but reached end of input", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Match any 1 characters + if (parser->pos + 1 <= parser->input_len) { + parser->pos += 1; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected at least 1 more characters at position %zu", parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (parser->success) { + { // Lookahead (match without consuming input) + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Stop(parser); + + if (parser->success) { + // Pattern matched, but we don't consume any input + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Comment", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Comment", parser->pos); + } +#endif + parser->memo[1].pos = start + 1; + parser->memo[1].endpos = parser->success ? parser->pos : (size_t)-1; + + parser->depth -= 1; + return parser->success; +} + +static bool parse_CompClause(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "CompClause", start); +#endif + + { // Choice + { // Choice + parse_CompFor(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_CompForEach(parser); + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 5 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[49], 0, 0); // "when" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "when" + if (parser->pos + 4 <= parser->input_len && + memcmp(parser->input + parser->pos, "when", 4) == 0) { + parser->pos += 4; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "when" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + parse_Exp(parser); + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "CompClause", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "CompClause", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_CompFor(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "CompFor", start); +#endif + + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 6 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[21], 0, 0); // "for" + } + if (parser->success) { + { // Match literal "for" + if (parser->pos + 3 <= parser->input_len && + memcmp(parser->input + parser->pos, "for", 3) == 0) { + parser->pos += 3; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "for" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + parse_Name(parser); + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "=" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 61) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "=" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 5 patterns + REMEMBER_POSITION(parser, pos); + + parse_Exp(parser); + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_Exp(parser); + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_Exp(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "CompFor", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "CompFor", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_CompForEach(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "CompForEach", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 9 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[22], 0, 0); // "foreach" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "for" + if (parser->pos + 3 <= parser->input_len && + memcmp(parser->input + parser->pos, "for", 3) == 0) { + parser->pos += 3; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "for" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_AssignableNameList(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "in" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "in", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "in" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Choice + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "*" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 42) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "*" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[47], 0, 0); // "unpack" + } + if (parser->success) { + parse_Exp(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Exp(parser); + } + } + } + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "CompForEach", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "CompForEach", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_CompInner(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "CompInner", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Choice + parse_CompForEach(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_CompFor(parser); + } + } + if (parser->success) { + { // Zero or more repetitions + while (true) { + parse_CompClause(parser); + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "CompInner", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "CompInner", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Comprehension(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Comprehension", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 7 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[9], 0, 0); // "comprehension" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "[" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 91) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "[" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_Exp(parser); + if (parser->success) { + parse_CompInner(parser); + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "]" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 93) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "]" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Comprehension", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Comprehension", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Do(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Do", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 5 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[13], 0, 0); // "do" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "do" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "do", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "do" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + parse_Body(parser); + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Do", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Do", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_DotChainItem(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "DotChainItem", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[14], 0, 0); // "dot" + } + if (parser->success) { + { // Match single character "." + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 46) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "." + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_NameRaw(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "DotChainItem", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "DotChainItem", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_DoubleString(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "DoubleString", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[42], 0, 0); // "string" + } + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match single character "\"" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 34) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "\\\"" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Choice + { // Capture + size_t start_pos = parser->pos; + { // At least 1 repetitions + REMEMBER_INPUT_POSITION(parser, pos); + size_t rep_count = 0; + + while (true) { + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match literal "#{" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "#{", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "#{" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + parse_DoubleStringInner(parser); + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + + if (!parser->success) { + break; + } + + rep_count += 1; + } + + // Don't recover if labeled failure was thrown + if (parser->throw_label) { + // Keep failure state, propagate labeled failure + } else if (rep_count >= 1) { + parser->success = true; + } else { + RESTORE_INPUT_POSITION(parser, pos); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected 1 repetitions at position %zu", parser->pos); +#endif + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_DoubleStringInterp(parser); + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (parser->success) { + { // Match single character "\"" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 34) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "\\\"" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "DoubleString", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "DoubleString", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_DoubleStringInner(Parser *parser) { + size_t start = parser->pos; + // Position-pure rule (no captures, labels, or other state): a + // single-slot memo short-circuits the repeated calls that backtracking + // alternatives make at the same position + if (parser->memo[2].pos == start + 1) { + if (parser->memo[2].endpos == (size_t)-1) { + parser->success = false; + return false; + } + parser->pos = parser->memo[2].endpos; + parser->success = true; + return true; + } + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "DoubleStringInner", start); +#endif + + { // Choice + { // Choice + { // Match literal "\\\"" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "\\\"", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "\\\"" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Match literal "\\\\" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "\\\\", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "\\\\" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match single character "\"" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 34) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "\\\"" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Match any 1 characters + if (parser->pos + 1 <= parser->input_len) { + parser->pos += 1; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected at least 1 more characters at position %zu", parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "DoubleStringInner", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "DoubleStringInner", parser->pos); + } +#endif + parser->memo[2].pos = start + 1; + parser->memo[2].endpos = parser->success ? parser->pos : (size_t)-1; + + parser->depth -= 1; + return parser->success; +} + +static bool parse_DoubleStringInterp(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "DoubleStringInterp", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 5 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[26], 0, 0); // "interpolate" + } + if (parser->success) { + { // Match literal "#{" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "#{", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "#{" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + parse_Exp(parser); + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "}" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 125) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "}" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "DoubleStringInterp", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "DoubleStringInterp", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_EmptyLine(Parser *parser) { + size_t start = parser->pos; + // Position-pure rule (no captures, labels, or other state): a + // single-slot memo short-circuits the repeated calls that backtracking + // alternatives make at the same position + if (parser->memo[3].pos == start + 1) { + if (parser->memo[3].endpos == (size_t)-1) { + parser->success = false; + return false; + } + parser->pos = parser->memo[3].endpos; + parser->success = true; + return true; + } + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "EmptyLine", start); +#endif + + parse_SpaceBreak(parser); + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "EmptyLine", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "EmptyLine", parser->pos); + } +#endif + parser->memo[3].pos = start + 1; + parser->memo[3].endpos = parser->success ? parser->pos : (size_t)-1; + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Exp(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Exp", start); +#endif + + { // Transform Capture (Cfn id=6) + size_t fn_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_FN_OPEN, __cmt_refs[6], parser->pos, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Value(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_BinaryOperator(parser); + if (parser->success) { + parse_Value(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_FN_CLOSE, 0, parser->pos, 0); + } else { + parser->cap_len = fn_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Exp", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Exp", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_ExpList(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "ExpList", start); +#endif + + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Exp(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_Exp(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "ExpList", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "ExpList", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_ExpListLow(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "ExpListLow", start); +#endif + + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Exp(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Choice + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character ";" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 59) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + ";" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + } + if (parser->success) { + parse_Exp(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "ExpListLow", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "ExpListLow", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Export(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Export", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 5 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[18], 0, 0); // "export" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "export" + if (parser->pos + 6 <= parser->input_len && + memcmp(parser->input + parser->pos, "export", 6) == 0) { + parser->pos += 6; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "export" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // FIRST-byte dispatched ordered choice + unsigned long long pgen_dispatch_mask; + if (parser->pos < parser->input_len) { + switch ((unsigned char)parser->input[parser->pos]) { + case 99: + pgen_dispatch_mask = 0x0000000000000009ULL; + break; + case 42: + pgen_dispatch_mask = 0x000000000000000aULL; + break; + case 94: + pgen_dispatch_mask = 0x000000000000000cULL; + break; + case 9: + case 32: + case 45: + pgen_dispatch_mask = 0x000000000000000fULL; + break; + default: + pgen_dispatch_mask = 0x0000000000000008ULL; + break; + } + } else { + pgen_dispatch_mask = 0x0000000000000008ULL; + } + + parser->success = false; + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 0))) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[7], 0, 0); // "class" + } + if (parser->success) { + parse_ClassDecl(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 1))) { + if ((pgen_dispatch_mask & 0x0000000000000001ULL) != 0x0000000000000001ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match single character "*" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 42) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "*" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 2))) { + if ((pgen_dispatch_mask & 0x0000000000000003ULL) != 0x0000000000000003ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match single character "^" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 94) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "^" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 3))) { + if ((pgen_dispatch_mask & 0x0000000000000007ULL) != 0x0000000000000007ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_NameList(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "=" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 61) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "=" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_ExpListLow(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + if (pgen_dispatch_mask != 0x000000000000000fULL) { + // Some alternatives were skipped: replay the whole choice in original + // order so error_message reports the same failure the undispatched + // parser would. Every alternative fails, so this only affects error + // state. + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[7], 0, 0); // "class" + } + if (parser->success) { + parse_ClassDecl(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match single character "*" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 42) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "*" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match single character "^" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 94) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "^" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_NameList(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "=" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 61) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "=" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_ExpListLow(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + } +#endif + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Export", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Export", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_File(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "File", start); +#endif + + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + parse_Shebang(parser); + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + { // Choice + parse_Block(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Match literal "" + if (parser->pos + 0 <= parser->input_len && + memcmp(parser->input + parser->pos, "", 0) == 0) { + parser->pos += 0; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "File", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "File", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_FnArgDef(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "FnArgDef", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Choice + { // Choice + parse_Name(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_SelfName(parser); + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_TableLit(parser); + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "=" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 61) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "=" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_Exp(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "FnArgDef", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "FnArgDef", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_FnArgDefList(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "FnArgDefList", start); +#endif + + { // Choice + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_FnArgDef(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + { // Choice + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Break(parser); + } + } + if (parser->success) { + parse_White(parser); + if (parser->success) { + parse_FnArgDef(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + { // Choice + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Break(parser); + } + } + if (parser->success) { + parse_White(parser); + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_VarArg(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_VarArg(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "FnArgDefList", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "FnArgDefList", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_FnArgs(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "FnArgs", start); +#endif + + { // Choice + { // Sequence with 6 patterns + REMEMBER_POSITION(parser, pos); + + { // Match single character "(" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 40) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "(" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Zero or more repetitions + while (true) { + parse_SpaceBreak(parser); + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + parse_FnArgsExpList(parser); + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + { // Zero or more repetitions + while (true) { + parse_SpaceBreak(parser); + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character ")" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 41) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + ")" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "!" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 33) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "!" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match single character "=" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 61) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "=" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Match literal "" + if (parser->pos + 0 <= parser->input_len && + memcmp(parser->input + parser->pos, "", 0) == 0) { + parser->pos += 0; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "FnArgs", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "FnArgs", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_FnArgsDef(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "FnArgsDef", start); +#endif + + { // Choice + { // Sequence with 8 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "(" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 40) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "(" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_White(parser); + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + parse_FnArgDefList(parser); + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + { // Choice + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match literal "using" + if (parser->pos + 5 <= parser->input_len && + memcmp(parser->input + parser->pos, "using", 5) == 0) { + parser->pos += 5; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "using" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Choice + parse_NameList(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match literal "nil" + if (parser->pos + 3 <= parser->input_len && + memcmp(parser->input + parser->pos, "nil", 3) == 0) { + parser->pos += 3; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "nil" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Match literal "" + if (parser->pos + 0 <= parser->input_len && + memcmp(parser->input + parser->pos, "", 0) == 0) { + parser->pos += 0; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + if (parser->success) { + parse_White(parser); + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character ")" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 41) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + ")" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Match literal "" + if (parser->pos + 0 <= parser->input_len && + memcmp(parser->input + parser->pos, "", 0) == 0) { + parser->pos += 0; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Match literal "" + if (parser->pos + 0 <= parser->input_len && + memcmp(parser->input + parser->pos, "", 0) == 0) { + parser->pos += 0; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "FnArgsDef", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "FnArgsDef", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_FnArgsExpList(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "FnArgsExpList", start); +#endif + + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Exp(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + { // Choice + parse_Break(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + } + if (parser->success) { + parse_White(parser); + if (parser->success) { + parse_Exp(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "FnArgsExpList", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "FnArgsExpList", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_For(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "For", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 12 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[21], 0, 0); // "for" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "for" + if (parser->pos + 3 <= parser->input_len && + memcmp(parser->input + parser->pos, "for", 3) == 0) { + parser->pos += 3; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "for" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Indenter cpush (stack 1): push constant 0 + pgen_ind_push(parser, 1, 0); + } + if (parser->success) { + parse_Name(parser); + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "=" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 61) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "=" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 5 patterns + REMEMBER_POSITION(parser, pos); + + parse_Exp(parser); + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_Exp(parser); + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_Exp(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + { // Indenter pop (stack 1) + if (!pgen_ind_pop(parser, 1)) { + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Indenter stack 1 is empty at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 3 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match literal "do" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "do", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "do" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + parse_Body(parser); + } + } + } + } + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "For", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "For", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_ForEach(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "ForEach", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 13 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[22], 0, 0); // "foreach" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "for" + if (parser->pos + 3 <= parser->input_len && + memcmp(parser->input + parser->pos, "for", 3) == 0) { + parser->pos += 3; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "for" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_AssignableNameList(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "in" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "in", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "in" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Indenter cpush (stack 1): push constant 0 + pgen_ind_push(parser, 1, 0); + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Choice + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "*" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 42) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "*" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[47], 0, 0); // "unpack" + } + if (parser->success) { + parse_Exp(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_ExpList(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + { // Indenter pop (stack 1) + if (!pgen_ind_pop(parser, 1)) { + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Indenter stack 1 is empty at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 3 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match literal "do" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "do", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "do" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + parse_Body(parser); + } + } + } + } + } + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "ForEach", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "ForEach", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_FunLit(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "FunLit", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[20], 0, 0); // "fndef" + } + if (parser->success) { + parse_FnArgsDef(parser); + if (parser->success) { + { // Choice + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match literal "->" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "->", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "->" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[40], 0, 0); // "slim" + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match literal "=>" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "=>", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "=>" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[19], 0, 0); // "fat" + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + } + if (parser->success) { + { // Choice + parse_Body(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Match literal "" + if (parser->pos + 0 <= parser->input_len && + memcmp(parser->input + parser->pos, "", 0) == 0) { + parser->pos += 0; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "FunLit", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "FunLit", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_If(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "If", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 9 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[23], 0, 0); // "if" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "if" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "if", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "if" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + parse_IfCond(parser); + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 3 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match literal "then" + if (parser->pos + 4 <= parser->input_len && + memcmp(parser->input + parser->pos, "then", 4) == 0) { + parser->pos += 4; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "then" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + parse_Body(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + parse_IfElseIf(parser); + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + parse_IfElse(parser); + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + } + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "If", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "If", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_IfCond(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "IfCond", start); +#endif + + { // Transform Capture (Cfn id=0) + size_t fn_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_FN_OPEN, __cmt_refs[0], parser->pos, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Exp(parser); + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + parse_Assign(parser); + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_FN_CLOSE, 0, parser->pos, 0); + } else { + parser->cap_len = fn_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "IfCond", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "IfCond", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_IfElse(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "IfElse", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 6 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[15], 0, 0); // "else" + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Break(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + parse_EmptyLine(parser); + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (parser->success) { + parse_CheckIndent(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "else" + if (parser->pos + 4 <= parser->input_len && + memcmp(parser->input + parser->pos, "else", 4) == 0) { + parser->pos += 4; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "else" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + parse_Body(parser); + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "IfElse", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "IfElse", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_IfElseIf(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "IfElseIf", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 8 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[16], 0, 0); // "elseif" + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Break(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + parse_EmptyLine(parser); + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (parser->success) { + parse_CheckIndent(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "elseif" + if (parser->pos + 6 <= parser->input_len && + memcmp(parser->input + parser->pos, "elseif", 6) == 0) { + parser->pos += 6; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "elseif" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Transform Capture (Cfn id=2) + size_t fn_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_FN_OPEN, __cmt_refs[2], parser->pos, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Position Capture + pgen_cap_push(parser, PGEN_CAP_POS, 0, parser->pos, 0); + } + if (parser->success) { + parse_IfCond(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_FN_CLOSE, 0, parser->pos, 0); + } else { + parser->cap_len = fn_cap_start; + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 3 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match literal "then" + if (parser->pos + 4 <= parser->input_len && + memcmp(parser->input + parser->pos, "then", 4) == 0) { + parser->pos += 4; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "then" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + parse_Body(parser); + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "IfElseIf", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "IfElseIf", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Import(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Import", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 10 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[24], 0, 0); // "import" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "import" + if (parser->pos + 6 <= parser->input_len && + memcmp(parser->input + parser->pos, "import", 6) == 0) { + parser->pos += 6; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "import" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_ImportNameList(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + { // Zero or more repetitions + while (true) { + parse_SpaceBreak(parser); + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "from" + if (parser->pos + 4 <= parser->input_len && + memcmp(parser->input + parser->pos, "from", 4) == 0) { + parser->pos += 4; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "from" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + parse_Exp(parser); + } + } + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Import", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Import", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_ImportName(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "ImportName", start); +#endif + + { // Choice + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "\\" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 92) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "\\\\" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[8], 0, 0); // "colon" + } + if (parser->success) { + parse_Name(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Name(parser); + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "ImportName", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "ImportName", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_ImportNameList(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "ImportNameList", start); +#endif + + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + { // Zero or more repetitions + while (true) { + parse_SpaceBreak(parser); + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (parser->success) { + parse_ImportName(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Choice + { // At least 1 repetitions + REMEMBER_INPUT_POSITION(parser, pos); + size_t rep_count = 0; + + while (true) { + parse_SpaceBreak(parser); + + if (!parser->success) { + break; + } + + rep_count += 1; + } + + // Don't recover if labeled failure was thrown + if (parser->throw_label) { + // Keep failure state, propagate labeled failure + } else if (rep_count >= 1) { + parser->success = true; + } else { + RESTORE_INPUT_POSITION(parser, pos); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected 1 repetitions at position %zu", parser->pos); +#endif + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 3 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Zero or more repetitions + while (true) { + parse_SpaceBreak(parser); + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + } + if (parser->success) { + parse_ImportName(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "ImportNameList", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "ImportNameList", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_InBlock(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "InBlock", start); +#endif + + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Advance(parser); + if (parser->success) { + parse_Block(parser); + if (parser->success) { + parse_PopIndent(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "InBlock", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "InBlock", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Invoke(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Invoke", start); +#endif + + { // FIRST-byte dispatched ordered choice + unsigned long long pgen_dispatch_mask; + if (parser->pos < parser->input_len) { + switch ((unsigned char)parser->input[parser->pos]) { + case 9: + case 32: + case 33: + case 40: + case 45: + pgen_dispatch_mask = 0x0000000000000009ULL; + break; + case 39: + pgen_dispatch_mask = 0x000000000000000aULL; + break; + case 34: + pgen_dispatch_mask = 0x000000000000000cULL; + break; + default: + pgen_dispatch_mask = 0x0000000000000008ULL; + break; + } + } else { + pgen_dispatch_mask = 0x0000000000000008ULL; + } + + parser->success = false; + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 0))) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[4], 0, 0); // "call" + } + if (parser->success) { + parse_FnArgs(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 1))) { + if ((pgen_dispatch_mask & 0x0000000000000001ULL) != 0x0000000000000001ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[4], 0, 0); // "call" + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_SingleString(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 2))) { + if ((pgen_dispatch_mask & 0x0000000000000003ULL) != 0x0000000000000003ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[4], 0, 0); // "call" + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_DoubleString(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 3))) { + if ((pgen_dispatch_mask & 0x0000000000000007ULL) != 0x0000000000000007ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Lookahead (match without consuming input) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match single character "[" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 91) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "[" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, but we don't consume any input + RESTORE_INPUT_POSITION(parser, pos); + } + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[4], 0, 0); // "call" + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_LuaString(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + if (pgen_dispatch_mask != 0x000000000000000fULL) { + // Some alternatives were skipped: replay the whole choice in original + // order so error_message reports the same failure the undispatched + // parser would. Every alternative fails, so this only affects error + // state. + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[4], 0, 0); // "call" + } + if (parser->success) { + parse_FnArgs(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[4], 0, 0); // "call" + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_SingleString(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[4], 0, 0); // "call" + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_DoubleString(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Lookahead (match without consuming input) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match single character "[" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 91) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "[" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, but we don't consume any input + RESTORE_INPUT_POSITION(parser, pos); + } + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[4], 0, 0); // "call" + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_LuaString(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + } +#endif + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Invoke", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Invoke", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_InvokeArgs(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "InvokeArgs", start); +#endif + + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match single character "-" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 45) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "-" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Choice + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_ExpList(parser); + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Choice + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Choice + parse_TableBlock(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + parse_SpaceBreak(parser); + if (parser->success) { + parse_Advance(parser); + if (parser->success) { + parse_ArgBlock(parser); + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + parse_TableBlock(parser); + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_TableBlock(parser); + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_TableBlock(parser); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "InvokeArgs", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "InvokeArgs", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_KeyName(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "KeyName", start); +#endif + + { // Choice + parse_SelfName(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[27], 0, 0); // "key_literal" + } + if (parser->success) { + parse_NameRaw(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "KeyName", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "KeyName", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_KeyValue(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "KeyValue", start); +#endif + + { // Choice + { // Transform Capture (Cfn id=4) + size_t fn_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_FN_OPEN, __cmt_refs[4], parser->pos, 0); + { // Sequence with 5 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character ":" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 58) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + ":" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + parse_SomeSpace(parser); + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + parse_Name(parser); + if (parser->success) { + { // Position Capture + pgen_cap_push(parser, PGEN_CAP_POS, 0, parser->pos, 0); + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_FN_CLOSE, 0, parser->pos, 0); + } else { + parser->cap_len = fn_cap_start; + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + { // FIRST-byte dispatched ordered choice + unsigned long long pgen_dispatch_mask; + if (parser->pos < parser->input_len) { + switch ((unsigned char)parser->input[parser->pos]) { + case 64: + case 65: + case 66: + case 67: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + case 84: + case 85: + case 86: + case 87: + case 88: + case 89: + case 90: + case 95: + case 97: + case 98: + case 99: + case 100: + case 101: + case 102: + case 103: + case 104: + case 105: + case 106: + case 107: + case 108: + case 109: + case 110: + case 111: + case 112: + case 113: + case 114: + case 115: + case 116: + case 117: + case 118: + case 119: + case 120: + case 121: + case 122: + pgen_dispatch_mask = 0x0000000000000001ULL; + break; + case 91: + pgen_dispatch_mask = 0x0000000000000002ULL; + break; + case 34: + pgen_dispatch_mask = 0x0000000000000004ULL; + break; + case 39: + pgen_dispatch_mask = 0x0000000000000008ULL; + break; + case 9: + case 32: + case 45: + pgen_dispatch_mask = 0x000000000000000fULL; + break; + default: + pgen_dispatch_mask = 0x0000000000000000ULL; + break; + } + } else { + pgen_dispatch_mask = 0x0000000000000000ULL; + } + + parser->success = false; + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 0))) { + parser->success = true; + parse_KeyName(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 1))) { + if ((pgen_dispatch_mask & 0x0000000000000001ULL) != 0x0000000000000001ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Sequence with 5 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "[" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 91) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "[" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_Exp(parser); + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "]" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 93) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "]" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 2))) { + if ((pgen_dispatch_mask & 0x0000000000000003ULL) != 0x0000000000000003ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + parse_DoubleString(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 3))) { + if ((pgen_dispatch_mask & 0x0000000000000007ULL) != 0x0000000000000007ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + parse_SingleString(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + if (pgen_dispatch_mask != 0x000000000000000fULL) { + // Some alternatives were skipped: replay the whole choice in original + // order so error_message reports the same failure the undispatched + // parser would. Every alternative fails, so this only affects error + // state. + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_KeyName(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 5 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "[" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 91) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "[" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_Exp(parser); + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "]" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 93) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "]" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + parse_DoubleString(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + parse_SingleString(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + } +#endif + } + } + if (parser->success) { + { // Match single character ":" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 58) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + ":" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Choice + { // Choice + parse_Exp(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_TableBlock(parser); + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // At least 1 repetitions + REMEMBER_INPUT_POSITION(parser, pos); + size_t rep_count = 0; + + while (true) { + parse_SpaceBreak(parser); + + if (!parser->success) { + break; + } + + rep_count += 1; + } + + // Don't recover if labeled failure was thrown + if (parser->throw_label) { + // Keep failure state, propagate labeled failure + } else if (rep_count >= 1) { + parser->success = true; + } else { + RESTORE_INPUT_POSITION(parser, pos); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected 1 repetitions at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + parse_Exp(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "KeyValue", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "KeyValue", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_KeyValueLine(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "KeyValueLine", start); +#endif + + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_CheckIndent(parser); + if (parser->success) { + parse_KeyValueList(parser); + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "KeyValueLine", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "KeyValueLine", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_KeyValueList(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "KeyValueList", start); +#endif + + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_KeyValue(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_KeyValue(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "KeyValueList", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "KeyValueList", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Line(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Line", start); +#endif + + { // Choice + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_CheckIndent(parser); + if (parser->success) { + parse_Statement(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Lookahead (match without consuming input) + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Stop(parser); + + if (parser->success) { + // Pattern matched, but we don't consume any input + RESTORE_INPUT_POSITION(parser, pos); + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Line", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Line", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Local(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Local", start); +#endif + + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match literal "local" + if (parser->pos + 5 <= parser->input_len && + memcmp(parser->input + parser->pos, "local", 5) == 0) { + parser->pos += 5; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "local" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Choice + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[11], 0, 0); // "declare_glob" + } + if (parser->success) { + { // Choice + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match single character "*" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 42) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "*" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match single character "^" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 94) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "^" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[12], 0, 0); // "declare_with_shadows" + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_NameList(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Local", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Local", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_LuaString(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "LuaString", start); +#endif + + { // Transform Capture (Cfn id=7) + size_t fn_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_FN_OPEN, __cmt_refs[7], parser->pos, 0); + { // Sequence with 9 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "[" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 91) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "[" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Position Capture + pgen_cap_push(parser, PGEN_CAP_POS, 0, parser->pos, 0); + } + if (parser->success) { + { // Capture Group "lua_eq" + size_t cg_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_GROUP_OPEN, 0, parser->pos, 0); + { // Zero or more repetitions + while (true) { + { // Match single character "=" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 61) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "=" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_GROUP_CLOSE, 0, parser->pos, 0); + } else { + parser->cap_len = cg_cap_start; + } + } + if (parser->success) { + { // Position Capture + pgen_cap_push(parser, PGEN_CAP_POS, 0, parser->pos, 0); + } + if (parser->success) { + { // Match single character "[" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 91) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "[" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + parse_Break(parser); + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Zero or more repetitions + while (true) { + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + parse_LuaStringClose(parser); + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Match any 1 characters + if (parser->pos + 1 <= parser->input_len) { + parser->pos += 1; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected at least 1 more characters at position %zu", parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (parser->success) { + parse_LuaStringClose(parser); + } + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_FN_CLOSE, 0, parser->pos, 0); + } else { + parser->cap_len = fn_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "LuaString", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "LuaString", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_LuaStringClose(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "LuaStringClose", start); +#endif + + { // Sequence with 3 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match single character "]" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 93) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "]" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Capture Match Back "lua_eq" + parser->success = pgen_cap_match_back(parser, 0); + if (!parser->success) { + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Capture match back 'lua_eq' failed at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + { // Match single character "]" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 93) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "]" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "LuaStringClose", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "LuaStringClose", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Name(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Name", start); +#endif + + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // Trie match for: "continue", "extends", "elseif", "export", "import", "return", "switch", "unless", "break", "class", "local", "using", "while", "else", "from", "then", "when", "with", "and", "for", "not", "do", "if", "in", "or" + REMEMBER_POSITION(parser, trie_start); + size_t last_terminal_pos = 0; + int has_terminal = 0; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 97: // "a" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 110: // "n" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 100: // "d" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 98: // "b" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 114: // "r" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 101: // "e" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 97: // "a" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 107: // "k" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 99: // "c" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 108: // "l" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 97: // "a" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 115: // "s" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 115: // "s" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 111: // "o" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 110: // "n" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 116: // "t" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 105: // "i" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 110: // "n" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 117: // "u" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 101: // "e" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 100: // "d" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 111: // "o" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 101: // "e" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 108: // "l" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 115: // "s" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 101: // "e" + parser->pos++; + if (!has_terminal || parser->pos > last_terminal_pos) { + last_terminal_pos = parser->pos; + has_terminal = 1; + } + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 105: // "i" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 102: // "f" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + if (!parser->success) { + // Partial match is valid: "else" + parser->success = true; + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 120: // "x" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 112: // "p" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 111: // "o" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 114: // "r" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 116: // "t" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 116: // "t" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 101: // "e" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 110: // "n" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 100: // "d" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 115: // "s" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 102: // "f" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 111: // "o" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 114: // "r" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 114: // "r" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 111: // "o" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 109: // "m" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 105: // "i" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 102: // "f" + parser->pos++; + break; + case 109: // "m" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 112: // "p" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 111: // "o" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 114: // "r" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 116: // "t" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 110: // "n" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 108: // "l" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 111: // "o" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 99: // "c" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 97: // "a" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 108: // "l" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 110: // "n" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 111: // "o" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 116: // "t" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 111: // "o" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 114: // "r" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 114: // "r" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 101: // "e" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 116: // "t" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 117: // "u" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 114: // "r" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 110: // "n" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 115: // "s" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 119: // "w" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 105: // "i" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 116: // "t" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 99: // "c" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 104: // "h" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 116: // "t" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 104: // "h" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 101: // "e" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 110: // "n" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 117: // "u" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 110: // "n" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 108: // "l" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 101: // "e" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 115: // "s" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 115: // "s" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 115: // "s" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 105: // "i" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 110: // "n" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 103: // "g" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 119: // "w" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 104: // "h" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 101: // "e" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 110: // "n" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 105: // "i" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 108: // "l" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 101: // "e" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 105: // "i" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 116: // "t" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 104: // "h" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, trie_start); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + parse_NameRaw(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Name", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Name", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_NameList(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "NameList", start); +#endif + + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Name(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_Name(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "NameList", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "NameList", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_NameOrDestructure(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "NameOrDestructure", start); +#endif + + { // Choice + parse_Name(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_TableLit(parser); + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "NameOrDestructure", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "NameOrDestructure", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_NameRaw(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "NameRaw", start); +#endif + + { // Capture + size_t start_pos = parser->pos; + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "NameRaw", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "NameRaw", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Num(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Num", start); +#endif + + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[31], 0, 0); // "number" + } + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Choice + { // Choice + { // Sequence with 3 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match literal "0x" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "0x", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "0x" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // At least 1 repetitions + REMEMBER_INPUT_POSITION(parser, pos); + size_t rep_count = 0; + + while (true) { + { // Match character range: "09,af,AF" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 102) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 70))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "0" + " - " + "9" + ", " + "a" + " - " + "f" + ", " + "A" + " - " + "F" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (!parser->success) { + break; + } + + rep_count += 1; + } + + // Don't recover if labeled failure was thrown + if (parser->throw_label) { + // Keep failure state, propagate labeled failure + } else if (rep_count >= 1) { + parser->success = true; + } else { + RESTORE_INPUT_POSITION(parser, pos); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected 1 repetitions at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Match character set "uU" + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 117: /* "u" */ + case 85: /* "U" */ + parser->pos++; + break; + default: +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\"uU\"" + " at position %zu", + parser->pos); +#endif + parser->success = false; + } + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\"uU\"" + " at position %zu but reached end of input", + parser->pos); +#endif + parser->success = false; + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + { // At least 2 repetitions + REMEMBER_INPUT_POSITION(parser, pos); + size_t rep_count = 0; + + while (true) { + { // Match character set "lL" + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 108: /* "l" */ + case 76: /* "L" */ + parser->pos++; + break; + default: +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\"lL\"" + " at position %zu", + parser->pos); +#endif + parser->success = false; + } + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\"lL\"" + " at position %zu but reached end of input", + parser->pos); +#endif + parser->success = false; + } + } + + if (!parser->success) { + break; + } + + rep_count += 1; + } + + // Don't recover if labeled failure was thrown + if (parser->throw_label) { + // Keep failure state, propagate labeled failure + } else if (rep_count >= 2) { + parser->success = true; + } else { + RESTORE_INPUT_POSITION(parser, pos); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected 2 repetitions at position %zu", parser->pos); +#endif + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 3 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // At least 1 repetitions + REMEMBER_INPUT_POSITION(parser, pos); + size_t rep_count = 0; + + while (true) { + { // Match character range: "09" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "0" + " - " + "9" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (!parser->success) { + break; + } + + rep_count += 1; + } + + // Don't recover if labeled failure was thrown + if (parser->throw_label) { + // Keep failure state, propagate labeled failure + } else if (rep_count >= 1) { + parser->success = true; + } else { + RESTORE_INPUT_POSITION(parser, pos); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected 1 repetitions at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Match character set "uU" + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 117: /* "u" */ + case 85: /* "U" */ + parser->pos++; + break; + default: +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\"uU\"" + " at position %zu", + parser->pos); +#endif + parser->success = false; + } + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\"uU\"" + " at position %zu but reached end of input", + parser->pos); +#endif + parser->success = false; + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + { // At least 2 repetitions + REMEMBER_INPUT_POSITION(parser, pos); + size_t rep_count = 0; + + while (true) { + { // Match character set "lL" + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 108: /* "l" */ + case 76: /* "L" */ + parser->pos++; + break; + default: +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\"lL\"" + " at position %zu", + parser->pos); +#endif + parser->success = false; + } + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\"lL\"" + " at position %zu but reached end of input", + parser->pos); +#endif + parser->success = false; + } + } + + if (!parser->success) { + break; + } + + rep_count += 1; + } + + // Don't recover if labeled failure was thrown + if (parser->throw_label) { + // Keep failure state, propagate labeled failure + } else if (rep_count >= 2) { + parser->success = true; + } else { + RESTORE_INPUT_POSITION(parser, pos); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected 2 repetitions at position %zu", parser->pos); +#endif + } + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // Choice + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // At least 1 repetitions + REMEMBER_INPUT_POSITION(parser, pos); + size_t rep_count = 0; + + while (true) { + { // Match character range: "09" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "0" + " - " + "9" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (!parser->success) { + break; + } + + rep_count += 1; + } + + // Don't recover if labeled failure was thrown + if (parser->throw_label) { + // Keep failure state, propagate labeled failure + } else if (rep_count >= 1) { + parser->success = true; + } else { + RESTORE_INPUT_POSITION(parser, pos); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected 1 repetitions at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match single character "." + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 46) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "." + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // At least 1 repetitions + REMEMBER_INPUT_POSITION(parser, pos); + size_t rep_count = 0; + + while (true) { + { // Match character range: "09" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "0" + " - " + "9" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (!parser->success) { + break; + } + + rep_count += 1; + } + + // Don't recover if labeled failure was thrown + if (parser->throw_label) { + // Keep failure state, propagate labeled failure + } else if (rep_count >= 1) { + parser->success = true; + } else { + RESTORE_INPUT_POSITION(parser, pos); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected 1 repetitions at position %zu", parser->pos); +#endif + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match single character "." + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 46) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "." + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // At least 1 repetitions + REMEMBER_INPUT_POSITION(parser, pos); + size_t rep_count = 0; + + while (true) { + { // Match character range: "09" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "0" + " - " + "9" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (!parser->success) { + break; + } + + rep_count += 1; + } + + // Don't recover if labeled failure was thrown + if (parser->throw_label) { + // Keep failure state, propagate labeled failure + } else if (rep_count >= 1) { + parser->success = true; + } else { + RESTORE_INPUT_POSITION(parser, pos); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected 1 repetitions at position %zu", parser->pos); +#endif + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 3 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character set "eE" + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 101: /* "e" */ + case 69: /* "E" */ + parser->pos++; + break; + default: +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\"eE\"" + " at position %zu", + parser->pos); +#endif + parser->success = false; + } + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\"eE\"" + " at position %zu but reached end of input", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Match single character "-" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 45) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "-" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + { // At least 1 repetitions + REMEMBER_INPUT_POSITION(parser, pos); + size_t rep_count = 0; + + while (true) { + { // Match character range: "09" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "0" + " - " + "9" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (!parser->success) { + break; + } + + rep_count += 1; + } + + // Don't recover if labeled failure was thrown + if (parser->throw_label) { + // Keep failure state, propagate labeled failure + } else if (rep_count >= 1) { + parser->success = true; + } else { + RESTORE_INPUT_POSITION(parser, pos); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected 1 repetitions at position %zu", parser->pos); +#endif + } + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Num", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Num", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Parens(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Parens", start); +#endif + + { // Sequence with 7 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "(" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 40) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "(" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Zero or more repetitions + while (true) { + parse_SpaceBreak(parser); + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (parser->success) { + parse_Exp(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + parse_SpaceBreak(parser); + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character ")" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 41) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + ")" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Parens", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Parens", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_PopIndent(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "PopIndent", start); +#endif + + { // Indenter pop (stack 0) + if (!pgen_ind_pop(parser, 0)) { + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Indenter stack 0 is empty at position %zu", parser->pos); +#endif + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "PopIndent", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "PopIndent", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_PreventIndent(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "PreventIndent", start); +#endif + + { // Indenter prevent (stack 0): push sentinel so nested advance fails + pgen_ind_push(parser, 0, PGEN_IND_PREVENT_SENTINEL); + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "PreventIndent", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "PreventIndent", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_PushIndent(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "PushIndent", start); +#endif + + { // Indenter push (stack 0): consume whitespace, push measured width + size_t ind_end; + int ind_width = pgen_ind_measure(parser, &ind_end, 4); + pgen_ind_push(parser, 0, ind_width); + parser->pos = ind_end; + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "PushIndent", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "PushIndent", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Return(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Return", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 5 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[35], 0, 0); // "return" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "return" + if (parser->pos + 6 <= parser->input_len && + memcmp(parser->input + parser->pos, "return", 6) == 0) { + parser->pos += 6; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "return" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Choice + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[17], 0, 0); // "explist" + } + if (parser->success) { + parse_ExpListLow(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture + size_t start_pos = parser->pos; + { // Match literal "" + if (parser->pos + 0 <= parser->input_len && + memcmp(parser->input + parser->pos, "", 0) == 0) { + parser->pos += 0; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Return", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Return", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_SelfName(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "SelfName", start); +#endif + + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "@" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 64) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "@" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Choice + { // Choice + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Match single character "@" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 64) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "@" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Choice + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[38], 0, 0); // "self_class" + } + if (parser->success) { + parse_NameRaw(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[37], 0, 0); // "self.__class" + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[36], 0, 0); // "self" + } + if (parser->success) { + parse_NameRaw(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[36], 0, 0); // "self" + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "SelfName", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "SelfName", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Shebang(Parser *parser) { + size_t start = parser->pos; + // Position-pure rule (no captures, labels, or other state): a + // single-slot memo short-circuits the repeated calls that backtracking + // alternatives make at the same position + if (parser->memo[4].pos == start + 1) { + if (parser->memo[4].endpos == (size_t)-1) { + parser->success = false; + return false; + } + parser->pos = parser->memo[4].endpos; + parser->success = true; + return true; + } + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Shebang", start); +#endif + + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match literal "#!" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "#!", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "#!" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Stop(parser); + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Match any 1 characters + if (parser->pos + 1 <= parser->input_len) { + parser->pos += 1; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected at least 1 more characters at position %zu", parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Shebang", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Shebang", parser->pos); + } +#endif + parser->memo[4].pos = start + 1; + parser->memo[4].endpos = parser->success ? parser->pos : (size_t)-1; + + parser->depth -= 1; + return parser->success; +} + +static bool parse_SimpleValue(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "SimpleValue", start); +#endif + + { // FIRST-byte dispatched ordered choice + unsigned long long pgen_dispatch_mask; + if (parser->pos < parser->input_len) { + switch ((unsigned char)parser->input[parser->pos]) { + case 105: + pgen_dispatch_mask = 0x0000000000000101ULL; + break; + case 117: + pgen_dispatch_mask = 0x0000000000000102ULL; + break; + case 115: + pgen_dispatch_mask = 0x0000000000000104ULL; + break; + case 99: + pgen_dispatch_mask = 0x0000000000000110ULL; + break; + case 102: + pgen_dispatch_mask = 0x0000000000000160ULL; + break; + case 119: + pgen_dispatch_mask = 0x0000000000000188ULL; + break; + case 35: + pgen_dispatch_mask = 0x0000000000000500ULL; + break; + case 126: + pgen_dispatch_mask = 0x0000000000000900ULL; + break; + case 110: + pgen_dispatch_mask = 0x0000000000001100ULL; + break; + case 123: + pgen_dispatch_mask = 0x0000000000006100ULL; + break; + case 91: + pgen_dispatch_mask = 0x0000000000008100ULL; + break; + case 40: + case 61: + pgen_dispatch_mask = 0x0000000000010100ULL; + break; + case 46: + case 48: + case 49: + case 50: + case 51: + case 52: + case 53: + case 54: + case 55: + case 56: + case 57: + pgen_dispatch_mask = 0x0000000000020100ULL; + break; + case 9: + case 32: + case 45: + pgen_dispatch_mask = 0x000000000003ffffULL; + break; + default: + pgen_dispatch_mask = 0x0000000000000100ULL; + break; + } + } else { + pgen_dispatch_mask = 0x0000000000000100ULL; + } + + parser->success = false; + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 0))) { + parser->success = true; + parse_If(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 1))) { + if ((pgen_dispatch_mask & 0x0000000000000001ULL) != 0x0000000000000001ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_Unless(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 2))) { + if ((pgen_dispatch_mask & 0x0000000000000003ULL) != 0x0000000000000003ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_Switch(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 3))) { + if ((pgen_dispatch_mask & 0x0000000000000007ULL) != 0x0000000000000007ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_With(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 4))) { + if ((pgen_dispatch_mask & 0x000000000000000fULL) != 0x000000000000000fULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_ClassDecl(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 5))) { + if ((pgen_dispatch_mask & 0x000000000000001fULL) != 0x000000000000001fULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_ForEach(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 6))) { + if ((pgen_dispatch_mask & 0x000000000000003fULL) != 0x000000000000003fULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_For(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 7))) { + if ((pgen_dispatch_mask & 0x000000000000007fULL) != 0x000000000000007fULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_While(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 8))) { + if ((pgen_dispatch_mask & 0x00000000000000ffULL) != 0x00000000000000ffULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Indenter ctop (stack 1): top ne 0 + PgenIndStack *ind_s = &parser->ind_stacks[1]; + if (!(ind_s->size > 0 && ind_s->items[ind_s->size - 1] != 0)) { + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Indenter stack 1 top failed ne 0 check at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + parse_Do(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 9))) { + if ((pgen_dispatch_mask & 0x00000000000001ffULL) != 0x00000000000001ffULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 5 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[29], 0, 0); // "minus" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "-" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 45) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "-" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + parse_SomeSpace(parser); + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + parse_Exp(parser); + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 10))) { + if ((pgen_dispatch_mask & 0x00000000000003ffULL) != 0x00000000000003ffULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[28], 0, 0); // "length" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "#" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 35) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "#" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_Exp(parser); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 11))) { + if ((pgen_dispatch_mask & 0x00000000000007ffULL) != 0x00000000000007ffULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[2], 0, 0); // "bitnot" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "~" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 126) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "~" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_Exp(parser); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 12))) { + if ((pgen_dispatch_mask & 0x0000000000000fffULL) != 0x0000000000000fffULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 5 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[30], 0, 0); // "not" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "not" + if (parser->pos + 3 <= parser->input_len && + memcmp(parser->input + parser->pos, "not", 3) == 0) { + parser->pos += 3; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "not" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + parse_Exp(parser); + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 13))) { + if ((pgen_dispatch_mask & 0x0000000000001fffULL) != 0x0000000000001fffULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_TblComprehension(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 14))) { + if ((pgen_dispatch_mask & 0x0000000000003fffULL) != 0x0000000000003fffULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_TableLit(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 15))) { + if ((pgen_dispatch_mask & 0x0000000000007fffULL) != 0x0000000000007fffULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_Comprehension(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 16))) { + if ((pgen_dispatch_mask & 0x000000000000ffffULL) != 0x000000000000ffffULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_FunLit(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 17))) { + if ((pgen_dispatch_mask & 0x000000000001ffffULL) != 0x000000000001ffffULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_Num(parser); + } + if (!parser->success && !parser->throw_label) { + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + if (pgen_dispatch_mask != 0x000000000003ffffULL) { + // Some alternatives were skipped: replay the whole choice in original + // order so error_message reports the same failure the undispatched + // parser would. Every alternative fails, so this only affects error + // state. + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_If(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Unless(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Switch(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_With(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_ClassDecl(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_ForEach(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_For(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_While(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Indenter ctop (stack 1): top ne 0 + PgenIndStack *ind_s = &parser->ind_stacks[1]; + if (!(ind_s->size > 0 && ind_s->items[ind_s->size - 1] != 0)) { + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Indenter stack 1 top failed ne 0 check at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + parse_Do(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 5 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[29], 0, 0); // "minus" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "-" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 45) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "-" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + parse_SomeSpace(parser); + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + parse_Exp(parser); + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[28], 0, 0); // "length" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "#" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 35) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "#" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_Exp(parser); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[2], 0, 0); // "bitnot" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "~" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 126) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "~" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_Exp(parser); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 5 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[30], 0, 0); // "not" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "not" + if (parser->pos + 3 <= parser->input_len && + memcmp(parser->input + parser->pos, "not", 3) == 0) { + parser->pos += 3; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "not" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + parse_Exp(parser); + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_TblComprehension(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_TableLit(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Comprehension(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_FunLit(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Num(parser); + } + } +#endif + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "SimpleValue", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "SimpleValue", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_SingleString(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "SingleString", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[42], 0, 0); // "string" + } + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match single character "'" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 39) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "'" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Zero or more repetitions + while (true) { + { // Choice + { // Choice + { // Match literal "\\'" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "\\'", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "\\'" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Match literal "\\\\" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "\\\\", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "\\\\" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match single character "'" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 39) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "'" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Match any 1 characters + if (parser->pos + 1 <= parser->input_len) { + parser->pos += 1; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected at least 1 more characters at position %zu", parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (parser->success) { + { // Match single character "'" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 39) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "'" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "SingleString", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "SingleString", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Slice(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Slice", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 9 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[39], 0, 0); // "slice" + } + if (parser->success) { + { // Match single character "[" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 91) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "[" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Choice + parse_SliceValue(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[52], 0, 0); // 1 + } + } + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Choice + parse_SliceValue(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[0], 0, 0); // "" + } + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_SliceValue(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "]" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 93) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "]" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + } + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Slice", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Slice", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_SliceValue(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "SliceValue", start); +#endif + + parse_Exp(parser); + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "SliceValue", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "SliceValue", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_SomeSpace(Parser *parser) { + size_t start = parser->pos; + // Position-pure rule (no captures, labels, or other state): a + // single-slot memo short-circuits the repeated calls that backtracking + // alternatives make at the same position + if (parser->memo[5].pos == start + 1) { + if (parser->memo[5].endpos == (size_t)-1) { + parser->success = false; + return false; + } + parser->pos = parser->memo[5].endpos; + parser->success = true; + return true; + } + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "SomeSpace", start); +#endif + + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // At least 1 repetitions + REMEMBER_INPUT_POSITION(parser, pos); + size_t rep_count = 0; + + while (true) { + { // Match character set " \t" + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 32: /* " " */ + case 9: /* "\t" */ + parser->pos++; + break; + default: +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\" \\t\"" + " at position %zu", + parser->pos); +#endif + parser->success = false; + } + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\" \\t\"" + " at position %zu but reached end of input", + parser->pos); +#endif + parser->success = false; + } + } + + if (!parser->success) { + break; + } + + rep_count += 1; + } + + // Don't recover if labeled failure was thrown + if (parser->throw_label) { + // Keep failure state, propagate labeled failure + } else if (rep_count >= 1) { + parser->success = true; + } else { + RESTORE_INPUT_POSITION(parser, pos); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected 1 repetitions at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + parse_Comment(parser); + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "SomeSpace", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "SomeSpace", parser->pos); + } +#endif + parser->memo[5].pos = start + 1; + parser->memo[5].endpos = parser->success ? parser->pos : (size_t)-1; + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Space(Parser *parser) { + size_t start = parser->pos; + // Position-pure rule (no captures, labels, or other state): a + // single-slot memo short-circuits the repeated calls that backtracking + // alternatives make at the same position + if (parser->memo[6].pos == start + 1) { + if (parser->memo[6].endpos == (size_t)-1) { + parser->success = false; + return false; + } + parser->pos = parser->memo[6].endpos; + parser->success = true; + return true; + } + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Space", start); +#endif + + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + { // Zero or more repetitions + while (true) { + { // Match character set " \t" + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 32: /* " " */ + case 9: /* "\t" */ + parser->pos++; + break; + default: +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\" \\t\"" + " at position %zu", + parser->pos); +#endif + parser->success = false; + } + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\" \\t\"" + " at position %zu but reached end of input", + parser->pos); +#endif + parser->success = false; + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + parse_Comment(parser); + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Space", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Space", parser->pos); + } +#endif + parser->memo[6].pos = start + 1; + parser->memo[6].endpos = parser->success ? parser->pos : (size_t)-1; + + parser->depth -= 1; + return parser->success; +} + +static bool parse_SpaceBreak(Parser *parser) { + size_t start = parser->pos; + // Position-pure rule (no captures, labels, or other state): a + // single-slot memo short-circuits the repeated calls that backtracking + // alternatives make at the same position + if (parser->memo[7].pos == start + 1) { + if (parser->memo[7].endpos == (size_t)-1) { + parser->success = false; + return false; + } + parser->pos = parser->memo[7].endpos; + parser->success = true; + return true; + } + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "SpaceBreak", start); +#endif + + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + parse_Break(parser); + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "SpaceBreak", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "SpaceBreak", parser->pos); + } +#endif + parser->memo[7].pos = start + 1; + parser->memo[7].endpos = parser->success ? parser->pos : (size_t)-1; + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Statement(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Statement", start); +#endif + + { // Transform Capture (Cfn id=1) + size_t fn_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_FN_OPEN, __cmt_refs[1], parser->pos, 0); + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + { // Transform Capture (Cfn id=2) + size_t fn_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_FN_OPEN, __cmt_refs[2], parser->pos, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Position Capture + pgen_cap_push(parser, PGEN_CAP_POS, 0, parser->pos, 0); + } + if (parser->success) { + { // FIRST-byte dispatched ordered choice + unsigned long long pgen_dispatch_mask; + if (parser->pos < parser->input_len) { + switch ((unsigned char)parser->input[parser->pos]) { + case 105: + pgen_dispatch_mask = 0x0000000000000401ULL; + break; + case 119: + pgen_dispatch_mask = 0x0000000000000406ULL; + break; + case 102: + pgen_dispatch_mask = 0x0000000000000418ULL; + break; + case 115: + pgen_dispatch_mask = 0x0000000000000420ULL; + break; + case 114: + pgen_dispatch_mask = 0x0000000000000440ULL; + break; + case 108: + pgen_dispatch_mask = 0x0000000000000480ULL; + break; + case 101: + pgen_dispatch_mask = 0x0000000000000500ULL; + break; + case 98: + case 99: + pgen_dispatch_mask = 0x0000000000000600ULL; + break; + case 9: + case 32: + case 45: + pgen_dispatch_mask = 0x00000000000007ffULL; + break; + default: + pgen_dispatch_mask = 0x0000000000000400ULL; + break; + } + } else { + pgen_dispatch_mask = 0x0000000000000400ULL; + } + + parser->success = false; + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 0))) { + parser->success = true; + parse_Import(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 1))) { + if ((pgen_dispatch_mask & 0x0000000000000001ULL) != 0x0000000000000001ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_While(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 2))) { + if ((pgen_dispatch_mask & 0x0000000000000003ULL) != 0x0000000000000003ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_With(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 3))) { + if ((pgen_dispatch_mask & 0x0000000000000007ULL) != 0x0000000000000007ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_For(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 4))) { + if ((pgen_dispatch_mask & 0x000000000000000fULL) != 0x000000000000000fULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_ForEach(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 5))) { + if ((pgen_dispatch_mask & 0x000000000000001fULL) != 0x000000000000001fULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_Switch(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 6))) { + if ((pgen_dispatch_mask & 0x000000000000003fULL) != 0x000000000000003fULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_Return(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 7))) { + if ((pgen_dispatch_mask & 0x000000000000007fULL) != 0x000000000000007fULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_Local(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 8))) { + if ((pgen_dispatch_mask & 0x00000000000000ffULL) != 0x00000000000000ffULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_Export(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 9))) { + if ((pgen_dispatch_mask & 0x00000000000001ffULL) != 0x00000000000001ffULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_BreakLoop(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 10))) { + if ((pgen_dispatch_mask & 0x00000000000003ffULL) != 0x00000000000003ffULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Transform Capture (Cfn id=3) + size_t fn_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_FN_OPEN, __cmt_refs[3], parser->pos, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_ExpList(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Choice + parse_Update(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Assign(parser); + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_FN_CLOSE, 0, parser->pos, 0); + } else { + parser->cap_len = fn_cap_start; + } + } + } + if (!parser->success && !parser->throw_label) { + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + if (pgen_dispatch_mask != 0x00000000000007ffULL) { + // Some alternatives were skipped: replay the whole choice in original + // order so error_message reports the same failure the undispatched + // parser would. Every alternative fails, so this only affects error + // state. + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Import(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_While(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_With(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_For(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_ForEach(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Switch(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Return(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Local(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Export(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_BreakLoop(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Transform Capture (Cfn id=3) + size_t fn_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_FN_OPEN, __cmt_refs[3], parser->pos, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_ExpList(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Choice + parse_Update(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Assign(parser); + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_FN_CLOSE, 0, parser->pos, 0); + } else { + parser->cap_len = fn_cap_start; + } + } + } + } +#endif + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_FN_CLOSE, 0, parser->pos, 0); + } else { + parser->cap_len = fn_cap_start; + } + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Choice + { // Choice + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 7 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[23], 0, 0); // "if" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "if" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "if", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "if" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + parse_Exp(parser); + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match literal "else" + if (parser->pos + 4 <= parser->input_len && + memcmp(parser->input + parser->pos, "else", 4) == 0) { + parser->pos += 4; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "else" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + parse_Exp(parser); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + parse_Space(parser); + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 5 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[46], 0, 0); // "unless" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "unless" + if (parser->pos + 6 <= parser->input_len && + memcmp(parser->input + parser->pos, "unless", 6) == 0) { + parser->pos += 6; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "unless" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + parse_Exp(parser); + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[9], 0, 0); // "comprehension" + } + if (parser->success) { + parse_CompInner(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + if (parser->success) { + parse_Space(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_FN_CLOSE, 0, parser->pos, 0); + } else { + parser->cap_len = fn_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Statement", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Statement", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Stop(Parser *parser) { + size_t start = parser->pos; + // Position-pure rule (no captures, labels, or other state): a + // single-slot memo short-circuits the repeated calls that backtracking + // alternatives make at the same position + if (parser->memo[8].pos == start + 1) { + if (parser->memo[8].endpos == (size_t)-1) { + parser->success = false; + return false; + } + parser->pos = parser->memo[8].endpos; + parser->success = true; + return true; + } + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Stop", start); +#endif + + { // Choice + parse_Break(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match any 1 characters + if (parser->pos + 1 <= parser->input_len) { + parser->pos += 1; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected at least 1 more characters at position %zu", parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Stop", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Stop", parser->pos); + } +#endif + parser->memo[8].pos = start + 1; + parser->memo[8].endpos = parser->success ? parser->pos : (size_t)-1; + + parser->depth -= 1; + return parser->success; +} + +static bool parse_String(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "String", start); +#endif + + { // Choice + { // Choice + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + parse_DoubleString(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + parse_SingleString(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_LuaString(parser); + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "String", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "String", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Switch(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Switch", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 11 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[43], 0, 0); // "switch" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "switch" + if (parser->pos + 6 <= parser->input_len && + memcmp(parser->input + parser->pos, "switch", 6) == 0) { + parser->pos += 6; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "switch" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Indenter cpush (stack 1): push constant 0 + pgen_ind_push(parser, 1, 0); + } + if (parser->success) { + parse_Exp(parser); + if (parser->success) { + { // Indenter pop (stack 1) + if (!pgen_ind_pop(parser, 1)) { + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Indenter stack 1 is empty at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 3 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match literal "do" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "do", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "do" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + parse_Break(parser); + if (parser->success) { + parse_SwitchBlock(parser); + } + } + } + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Switch", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Switch", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_SwitchBlock(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "SwitchBlock", start); +#endif + + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + { // Zero or more repetitions + while (true) { + parse_EmptyLine(parser); + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (parser->success) { + parse_Advance(parser); + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_SwitchCase(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // At least 1 repetitions + REMEMBER_INPUT_POSITION(parser, pos); + size_t rep_count = 0; + + while (true) { + parse_Break(parser); + + if (!parser->success) { + break; + } + + rep_count += 1; + } + + // Don't recover if labeled failure was thrown + if (parser->throw_label) { + // Keep failure state, propagate labeled failure + } else if (rep_count >= 1) { + parser->success = true; + } else { + RESTORE_INPUT_POSITION(parser, pos); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected 1 repetitions at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + parse_SwitchCase(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // At least 1 repetitions + REMEMBER_INPUT_POSITION(parser, pos); + size_t rep_count = 0; + + while (true) { + parse_Break(parser); + + if (!parser->success) { + break; + } + + rep_count += 1; + } + + // Don't recover if labeled failure was thrown + if (parser->throw_label) { + // Keep failure state, propagate labeled failure + } else if (rep_count >= 1) { + parser->success = true; + } else { + RESTORE_INPUT_POSITION(parser, pos); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected 1 repetitions at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + parse_SwitchElse(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + parse_PopIndent(parser); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "SwitchBlock", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "SwitchBlock", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_SwitchCase(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "SwitchCase", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 7 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[5], 0, 0); // "case" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "when" + if (parser->pos + 4 <= parser->input_len && + memcmp(parser->input + parser->pos, "when", 4) == 0) { + parser->pos += 4; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "when" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_ExpList(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 3 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match literal "then" + if (parser->pos + 4 <= parser->input_len && + memcmp(parser->input + parser->pos, "then", 4) == 0) { + parser->pos += 4; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "then" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + parse_Body(parser); + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "SwitchCase", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "SwitchCase", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_SwitchElse(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "SwitchElse", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 5 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[15], 0, 0); // "else" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "else" + if (parser->pos + 4 <= parser->input_len && + memcmp(parser->input + parser->pos, "else", 4) == 0) { + parser->pos += 4; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "else" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + parse_Body(parser); + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "SwitchElse", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "SwitchElse", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_TableBlock(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "TableBlock", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 5 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[44], 0, 0); // "table" + } + if (parser->success) { + { // At least 1 repetitions + REMEMBER_INPUT_POSITION(parser, pos); + size_t rep_count = 0; + + while (true) { + parse_SpaceBreak(parser); + + if (!parser->success) { + break; + } + + rep_count += 1; + } + + // Don't recover if labeled failure was thrown + if (parser->throw_label) { + // Keep failure state, propagate labeled failure + } else if (rep_count >= 1) { + parser->success = true; + } else { + RESTORE_INPUT_POSITION(parser, pos); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected 1 repetitions at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + parse_Advance(parser); + if (parser->success) { + parse_TableBlockInner(parser); + if (parser->success) { + parse_PopIndent(parser); + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "TableBlock", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "TableBlock", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_TableBlockInner(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "TableBlockInner", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_KeyValueLine(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // At least 1 repetitions + REMEMBER_INPUT_POSITION(parser, pos); + size_t rep_count = 0; + + while (true) { + parse_SpaceBreak(parser); + + if (!parser->success) { + break; + } + + rep_count += 1; + } + + // Don't recover if labeled failure was thrown + if (parser->throw_label) { + // Keep failure state, propagate labeled failure + } else if (rep_count >= 1) { + parser->success = true; + } else { + RESTORE_INPUT_POSITION(parser, pos); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected 1 repetitions at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + parse_KeyValueLine(parser); + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "TableBlockInner", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "TableBlockInner", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_TableLit(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "TableLit", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 7 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[44], 0, 0); // "table" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "{" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 123) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "{" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + parse_TableValueList(parser); + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + parse_SpaceBreak(parser); + if (parser->success) { + parse_TableLitLine(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + parse_SpaceBreak(parser); + if (parser->success) { + parse_TableLitLine(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 2 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + parse_White(parser); + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "}" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 125) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "}" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "TableLit", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "TableLit", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_TableLitLine(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "TableLitLine", start); +#endif + + { // Choice + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_PushIndent(parser); + if (parser->success) { + parse_TableValueList(parser); + if (parser->success) { + parse_PopIndent(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_Space(parser); + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "TableLitLine", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "TableLitLine", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_TableValue(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "TableValue", start); +#endif + + { // Choice + parse_KeyValue(parser); + + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_Exp(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "TableValue", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "TableValue", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_TableValueList(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "TableValueList", start); +#endif + + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_TableValue(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_TableValue(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "TableValueList", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "TableValueList", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_TblComprehension(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "TblComprehension", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 7 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[45], 0, 0); // "tblcomprehension" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "{" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 123) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "{" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Exp(parser); + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match single character "," + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 44) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "," + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + if (parser->success) { + parse_Exp(parser); + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + parse_CompInner(parser); + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match single character "}" + if (parser->pos < parser->input_len && + parser->input[parser->pos] == 125) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character `" + "}" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "TblComprehension", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "TblComprehension", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Unless(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Unless", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 9 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[46], 0, 0); // "unless" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "unless" + if (parser->pos + 6 <= parser->input_len && + memcmp(parser->input + parser->pos, "unless", 6) == 0) { + parser->pos += 6; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "unless" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + parse_IfCond(parser); + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 3 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match literal "then" + if (parser->pos + 4 <= parser->input_len && + memcmp(parser->input + parser->pos, "then", 4) == 0) { + parser->pos += 4; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "then" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + parse_Body(parser); + if (parser->success) { + { // Zero or more repetitions + while (true) { + parse_IfElseIf(parser); + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + parse_IfElse(parser); + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + } + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Unless", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Unless", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Update(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Update", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 4 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[48], 0, 0); // "update" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Trie match for: "..=", "+=", "-=", "*=", "\/=", "%=", "or=", "and=", "&=", "|=", ">>=", "<<=" + REMEMBER_POSITION(parser, trie_start); + size_t last_terminal_pos = 0; + int has_terminal = 0; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 37: // "%" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 61: // "=" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 38: // "&" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 61: // "=" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 42: // "*" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 61: // "=" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 43: // "+" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 61: // "=" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 45: // "-" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 61: // "=" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 46: // "." + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 46: // "." + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 61: // "=" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 47: // "/" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 61: // "=" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 60: // "<" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 60: // "<" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 61: // "=" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 62: // ">" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 62: // ">" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 61: // "=" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 97: // "a" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 110: // "n" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 100: // "d" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 61: // "=" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 111: // "o" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 114: // "r" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 61: // "=" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + case 124: // "|" + parser->pos++; + + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 61: // "=" + parser->pos++; + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + break; + default: + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + } else { + parser->success = false; + if (has_terminal) { + parser->pos = last_terminal_pos; + parser->success = true; + } else { + PGEN_RECORD_FURTHEST(parser); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, trie_start); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (parser->success) { + parse_Exp(parser); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Update", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Update", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_Value(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "Value", start); +#endif + + { // Transform Capture (Cfn id=2) + size_t fn_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_FN_OPEN, __cmt_refs[2], parser->pos, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Position Capture + pgen_cap_push(parser, PGEN_CAP_POS, 0, parser->pos, 0); + } + if (parser->success) { + { // FIRST-byte dispatched ordered choice + unsigned long long pgen_dispatch_mask; + if (parser->pos < parser->input_len) { + switch ((unsigned char)parser->input[parser->pos]) { + case 58: + case 64: + case 65: + case 66: + case 67: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + case 84: + case 85: + case 86: + case 87: + case 88: + case 89: + case 90: + case 95: + case 97: + case 98: + case 99: + case 100: + case 101: + case 102: + case 103: + case 104: + case 105: + case 106: + case 107: + case 108: + case 109: + case 110: + case 111: + case 112: + case 113: + case 114: + case 115: + case 116: + case 117: + case 118: + case 119: + case 120: + case 121: + case 122: + pgen_dispatch_mask = 0x0000000000000007ULL; + break; + case 9: + case 32: + case 34: + case 39: + case 45: + case 91: + pgen_dispatch_mask = 0x000000000000000fULL; + break; + default: + pgen_dispatch_mask = 0x0000000000000005ULL; + break; + } + } else { + pgen_dispatch_mask = 0x0000000000000005ULL; + } + + parser->success = false; + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 0))) { + parser->success = true; + parse_SimpleValue(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 1))) { + if ((pgen_dispatch_mask & 0x0000000000000001ULL) != 0x0000000000000001ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[44], 0, 0); // "table" + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_KeyValueList(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 2))) { + if ((pgen_dispatch_mask & 0x0000000000000003ULL) != 0x0000000000000003ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_ChainValue(parser); + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 3))) { + if ((pgen_dispatch_mask & 0x0000000000000007ULL) != 0x0000000000000007ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + parse_String(parser); + } + if (!parser->success && !parser->throw_label) { + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + if (pgen_dispatch_mask != 0x000000000000000fULL) { + // Some alternatives were skipped: replay the whole choice in original + // order so error_message reports the same failure the undispatched + // parser would. Every alternative fails, so this only affects error + // state. + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_SimpleValue(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[44], 0, 0); // "table" + } + if (parser->success) { + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_KeyValueList(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_ChainValue(parser); + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + parse_String(parser); + } + } +#endif + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_FN_CLOSE, 0, parser->pos, 0); + } else { + parser->cap_len = fn_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "Value", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "Value", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_VarArg(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "VarArg", start); +#endif + + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal "..." + if (parser->pos + 3 <= parser->input_len && + memcmp(parser->input + parser->pos, "...", 3) == 0) { + parser->pos += 3; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "..." + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "VarArg", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "VarArg", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_While(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "While", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 9 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[50], 0, 0); // "while" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "while" + if (parser->pos + 5 <= parser->input_len && + memcmp(parser->input + parser->pos, "while", 5) == 0) { + parser->pos += 5; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "while" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Indenter cpush (stack 1): push constant 0 + pgen_ind_push(parser, 1, 0); + } + if (parser->success) { + parse_Exp(parser); + if (parser->success) { + { // Indenter pop (stack 1) + if (!pgen_ind_pop(parser, 1)) { + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Indenter stack 1 is empty at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 3 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match literal "do" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "do", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "do" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + parse_Body(parser); + } + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "While", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "While", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_White(Parser *parser) { + size_t start = parser->pos; + // Position-pure rule (no captures, labels, or other state): a + // single-slot memo short-circuits the repeated calls that backtracking + // alternatives make at the same position + if (parser->memo[9].pos == start + 1) { + if (parser->memo[9].endpos == (size_t)-1) { + parser->success = false; + return false; + } + parser->pos = parser->memo[9].endpos; + parser->success = true; + return true; + } + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "White", start); +#endif + + { // Zero or more repetitions + while (true) { + { // Match character set " \t\r\n" + if (parser->pos < parser->input_len) { + switch (parser->input[parser->pos]) { + case 32: /* " " */ + case 9: /* "\t" */ + case 13: /* "\r" */ + case 10: /* "\n" */ + parser->pos++; + break; + default: +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\" \\t\\r\\n\"" + " at position %zu", + parser->pos); +#endif + parser->success = false; + } + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected one of " + "\" \\t\\r\\n\"" + " at position %zu but reached end of input", + parser->pos); +#endif + parser->success = false; + } + } + if (!parser->success) { + break; + } + } + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "White", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "White", parser->pos); + } +#endif + parser->memo[9].pos = start + 1; + parser->memo[9].endpos = parser->success ? parser->pos : (size_t)-1; + + parser->depth -= 1; + return parser->success; +} + +static bool parse_With(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "With", start); +#endif + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + { // Sequence with 9 patterns + REMEMBER_POSITION(parser, pos); + + { // Constant Capture + // A constant capture matches the empty string and produces all given values + pgen_cap_push(parser, PGEN_CAP_CONST, __const_refs[51], 0, 0); // "with" + } + if (parser->success) { + parse_Space(parser); + if (parser->success) { + { // Match literal "with" + if (parser->pos + 4 <= parser->input_len && + memcmp(parser->input + parser->pos, "with", 4) == 0) { + parser->pos += 4; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "with" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + if (parser->success) { + { // Indenter cpush (stack 1): push constant 0 + pgen_ind_push(parser, 1, 0); + } + if (parser->success) { + parse_WithExp(parser); + if (parser->success) { + { // Indenter pop (stack 1) + if (!pgen_ind_pop(parser, 1)) { + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Indenter stack 1 is empty at position %zu", parser->pos); +#endif + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + { // Sequence with 3 patterns + REMEMBER_INPUT_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Match literal "do" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "do", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "do" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + } + if (!parser->success) { + RESTORE_INPUT_POSITION(parser, pos); + } + } + } + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (parser->success) { + parse_Body(parser); + } + } + } + } + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "With", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "With", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_WithExp(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "WithExp", start); +#endif + + { // Transform Capture (Cfn id=3) + size_t fn_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_FN_OPEN, __cmt_refs[3], parser->pos, 0); + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + { // Capture Table + size_t ct_cap_start = parser->cap_len; + pgen_cap_push(parser, PGEN_CAP_TBL_OPEN, 0, 0, 0); + parse_ExpList(parser); + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_TBL_CLOSE, 0, 0, 0); + } else { + parser->cap_len = ct_cap_start; + } + } + if (parser->success) { + { // At most 1 repetitions + size_t rep_count = 0; + + while (rep_count < 1) { + size_t before_pos = parser->pos; + + { + parse_Assign(parser); + } + + if (!parser->success || before_pos == parser->pos) { + // Break on failure or zero-width match + // Only recover from ordinary failure, not labeled failure from T() + if (!parser->throw_label) { + parser->success = true; + } + break; + } + + rep_count += 1; + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_FN_CLOSE, 0, parser->pos, 0); + } else { + parser->cap_len = fn_cap_start; + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "WithExp", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "WithExp", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +static bool parse_WordOperators(Parser *parser) { + size_t start = parser->pos; + + parser->depth += 1; + if (parser->depth > PGEN_MAX_DEPTH) { + // A Lua error (rather than a match failure) so the overflow can't be + // silently converted into a successful parse by a predicate or choice + luaL_error(parser->L, "pgen: max recursion depth (%d) exceeded at position %d", (int)PGEN_MAX_DEPTH, (int)(parser->pos + 1)); + } + +#ifdef PGEN_DEBUG + fprintf(stderr, "%*sEntering rule %s at position %zu\n", (int)parser->depth, "", "WordOperators", start); +#endif + + { // FIRST-byte dispatched ordered choice + unsigned long long pgen_dispatch_mask; + if (parser->pos < parser->input_len) { + switch ((unsigned char)parser->input[parser->pos]) { + case 111: + pgen_dispatch_mask = 0x0000000000000001ULL; + break; + case 97: + pgen_dispatch_mask = 0x0000000000000002ULL; + break; + case 126: + pgen_dispatch_mask = 0x0000000000000010ULL; + break; + case 33: + pgen_dispatch_mask = 0x0000000000000020ULL; + break; + case 61: + pgen_dispatch_mask = 0x0000000000000040ULL; + break; + case 46: + pgen_dispatch_mask = 0x0000000000000080ULL; + break; + case 60: + pgen_dispatch_mask = 0x0000000000000104ULL; + break; + case 62: + pgen_dispatch_mask = 0x0000000000000208ULL; + break; + case 47: + pgen_dispatch_mask = 0x0000000000000400ULL; + break; + case 9: + case 32: + case 45: + pgen_dispatch_mask = 0x00000000000007ffULL; + break; + default: + pgen_dispatch_mask = 0x0000000000000000ULL; + break; + } + } else { + pgen_dispatch_mask = 0x0000000000000000ULL; + } + + parser->success = false; + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 0))) { + parser->success = true; + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal "or" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "or", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "or" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 1))) { + if ((pgen_dispatch_mask & 0x0000000000000001ULL) != 0x0000000000000001ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal "and" + if (parser->pos + 3 <= parser->input_len && + memcmp(parser->input + parser->pos, "and", 3) == 0) { + parser->pos += 3; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "and" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 2))) { + if ((pgen_dispatch_mask & 0x0000000000000003ULL) != 0x0000000000000003ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal "<=" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "<=", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "<=" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 3))) { + if ((pgen_dispatch_mask & 0x0000000000000007ULL) != 0x0000000000000007ULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal ">=" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, ">=", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + ">=" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 4))) { + if ((pgen_dispatch_mask & 0x000000000000000fULL) != 0x000000000000000fULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal "~=" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "~=", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "~=" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 5))) { + if ((pgen_dispatch_mask & 0x000000000000001fULL) != 0x000000000000001fULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal "!=" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "!=", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "!=" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 6))) { + if ((pgen_dispatch_mask & 0x000000000000003fULL) != 0x000000000000003fULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal "==" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "==", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "==" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 7))) { + if ((pgen_dispatch_mask & 0x000000000000007fULL) != 0x000000000000007fULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal ".." + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "..", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + ".." + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 8))) { + if ((pgen_dispatch_mask & 0x00000000000000ffULL) != 0x00000000000000ffULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal "<<" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "<<", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "<<" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 9))) { + if ((pgen_dispatch_mask & 0x00000000000001ffULL) != 0x00000000000001ffULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal ">>" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, ">>", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + ">>" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label && (pgen_dispatch_mask & (1ULL << 10))) { + if ((pgen_dispatch_mask & 0x00000000000003ffULL) != 0x00000000000003ffULL) { + PGEN_RECORD_FURTHEST(parser); + } + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal "\/\/" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "//", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "//" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + if (pgen_dispatch_mask != 0x00000000000007ffULL) { + // Some alternatives were skipped: replay the whole choice in original + // order so error_message reports the same failure the undispatched + // parser would. Every alternative fails, so this only affects error + // state. + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal "or" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "or", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "or" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 3 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal "and" + if (parser->pos + 3 <= parser->input_len && + memcmp(parser->input + parser->pos, "and", 3) == 0) { + parser->pos += 3; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "and" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (parser->success) { + { // Negate (only match if pattern fails) + REMEMBER_INPUT_POSITION(parser, pos); + + { // Match character range: "az,AZ,09,__" + if (parser->pos < parser->input_len && + ((parser->input[parser->pos] >= 97 && parser->input[parser->pos] <= 122) || (parser->input[parser->pos] >= 65 && parser->input[parser->pos] <= 90) || (parser->input[parser->pos] >= 48 && parser->input[parser->pos] <= 57) || (parser->input[parser->pos] >= 95 && parser->input[parser->pos] <= 95))) { + parser->pos++; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected character in ranges [" + "a" + " - " + "z" + ", " + "A" + " - " + "Z" + ", " + "0" + " - " + "9" + ", " + "_" + " - " + "_" + "] at position %zu", + parser->pos); +#endif + parser->success = false; + } + } + + if (parser->success) { + // Pattern matched, so negate fails + RESTORE_INPUT_POSITION(parser, pos); + parser->success = false; + PGEN_RECORD_FURTHEST(parser); +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Negated pattern unexpectedly matched at position %zu", pos.pos); +#endif + } else { + // Pattern failed, so negate succeeds + parser->success = true; + // Swallow labeled failures inside predicates (LPegLabel behavior) + if (parser->throw_label) { + parser->throw_label = NULL; + parser->throw_pos = 0; + } + RESTORE_INPUT_POSITION(parser, pos); // Restore original position (technically not necessary since failed pattern should make no changes to position) + } + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal "<=" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "<=", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "<=" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal ">=" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, ">=", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + ">=" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal "~=" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "~=", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "~=" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal "!=" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "!=", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "!=" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal "==" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "==", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "==" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal ".." + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "..", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + ".." + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal "<<" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "<<", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "<<" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal ">>" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, ">>", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + ">>" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + if (!parser->success && !parser->throw_label) { + parser->success = true; + { // Sequence with 2 patterns + REMEMBER_POSITION(parser, pos); + + parse_Space(parser); + if (parser->success) { + { // Capture + size_t start_pos = parser->pos; + { // Match literal "\/\/" + if (parser->pos + 2 <= parser->input_len && + memcmp(parser->input + parser->pos, "//", 2) == 0) { + parser->pos += 2; + } else { +#ifdef PGEN_ERRORS + sprintf(parser->error_message, "Expected `" + "//" + "` at position %zu", + parser->pos); +#endif + parser->success = false; + PGEN_RECORD_FURTHEST(parser); + } + } + + if (parser->success) { + pgen_cap_push(parser, PGEN_CAP_STR, 0, start_pos, parser->pos - start_pos); + } + } + if (!parser->success) { + RESTORE_POSITION(parser, pos); + } + } + } + } + } +#endif + } + } + +#ifdef PGEN_DEBUG + if (parser->success) { + fprintf(stderr, "%*sRule %s matched range: %zu-%zu\n", (int)parser->depth, "", "WordOperators", start, parser->pos); + fprintf(stderr, "%*s\t%.*s\n", (int)parser->depth, "", (int)(parser->pos - start), parser->input + start); + } else { + fprintf(stderr, "%*sRule %s failed at position %zu\n", (int)parser->depth, "", "WordOperators", parser->pos); + } +#endif + + parser->depth -= 1; + return parser->success; +} + +#define PGEN_PARSER_MT "pgen.moonscript_parse_native" + +// Initialize a parser anchored in a Lua userdata (left on the stack). Its +// metatable's __gc frees the owned allocations, so a Lua error unwinding +// out of a parse (transform/Cmt callbacks, recursion depth, out of memory) +// cannot leak them. +static Parser *moonscript_parse_native_init(const char *input, lua_State *L) { + Parser *parser = (Parser *)lua_newuserdata(L, sizeof(Parser)); + + // Null the owned pointers before attaching the metatable so __gc is + // safe even if a later allocation fails mid-init + parser->caps = NULL; + parser->trail = NULL; + parser->trail_len = 0; + parser->trail_cap = 0; + for (int i = 0; i < PGEN_IND_STACK_COUNT; i++) { + parser->ind_stacks[i].items = NULL; + } + luaL_getmetatable(L, PGEN_PARSER_MT); + lua_setmetatable(L, -2); + + parser->input = input; + parser->input_len = strlen(input); + parser->pos = 0; + parser->depth = 0; + parser->success = true; + parser->error_message[0] = '\0'; + parser->throw_label = NULL; + parser->throw_pos = 0; + parser->furthest_fail = 0; + parser->top = lua_gettop(L); + parser->stack_claimed = parser->top; + parser->L = L; + + parser->caps = (PgenCap *)malloc(64 * sizeof(PgenCap)); + if (!parser->caps) { + luaL_error(L, "pgen: out of memory initializing parser"); + } + parser->cap_len = 0; + parser->cap_cap = 64; + for (int i = 0; i < PGEN_MEMO_COUNT; i++) { + parser->memo[i].pos = 0; // empty slot + } + + // Initialize indenter stacks (each starts holding its initial value) + static const int pgen_ind_initials[PGEN_IND_STACK_COUNT] = {0, 1}; + for (int i = 0; i < PGEN_IND_STACK_COUNT; i++) { + parser->ind_stacks[i].items = (int *)malloc(8 * sizeof(int)); + if (!parser->ind_stacks[i].items) { + luaL_error(L, "pgen: out of memory initializing parser"); + } + parser->ind_stacks[i].cap = 8; + parser->ind_stacks[i].size = 1; + parser->ind_stacks[i].items[0] = pgen_ind_initials[i]; + } + + return parser; +} + +// Free the parser's owned allocations. Idempotent: called eagerly on +// normal completion and again from __gc, which also covers error unwinds +static void moonscript_parse_native_free(Parser *parser) { + if (parser) { + for (int i = 0; i < PGEN_IND_STACK_COUNT; i++) { + free(parser->ind_stacks[i].items); + parser->ind_stacks[i].items = NULL; + } + free(parser->trail); + parser->trail = NULL; + free(parser->caps); + parser->caps = NULL; + } +} + +// --- Lua Module Interface --- + +// __gc for the parser userdata: frees whatever the eager free didn't +static int l_moonscript_parse_native_gc(lua_State *L) { + moonscript_parse_native_free((Parser *)lua_touserdata(L, 1)); + return 0; +} + +// Lua wrapper function +static int l_moonscript_parse_native_parse(lua_State *L) { + // Check type and get the input string + if (!lua_isstring(L, 1)) { + return luaL_error(L, "Expected string argument for parsing"); + } + const char *input = lua_tostring(L, 1); + if (!input) { + // Should not happen if lua_isstring passed, but good practice + return luaL_error(L, "Failed to get string argument"); + } + + // Initialize the parser (a userdata anchored on the stack; see _init) + Parser *parser = moonscript_parse_native_init(input, L); + + int initial_stack_size = lua_gettop(parser->L); + + parse_Root(parser); + + int final_stack_size = lua_gettop(parser->L); + assert(parser->top == final_stack_size && "Shadow stack top out of sync."); + + // Return nil and error info on failure + if (!parser->success) { + assert(final_stack_size == initial_stack_size && "Unexpected stack size change on parse failure."); + assert(parser->cap_len == 0 && "Capture log not empty on parse failure."); + lua_pushnil(L); + if (parser->throw_label) { + // Labeled failure: return nil, label, position + lua_pushstring(L, parser->throw_label); + lua_pushinteger(L, parser->throw_pos + 1); // 1-indexed for Lua + moonscript_parse_native_free(parser); + return 3; + } else { + // Ordinary failure: return nil, message (PGEN_ERRORS builds only) and + // the furthest input position a match attempt failed at (1-indexed) +#ifdef PGEN_ERRORS + lua_pushstring(L, parser->error_message); +#else + lua_pushnil(L); +#endif + lua_pushinteger(L, parser->furthest_fail + 1); + moonscript_parse_native_free(parser); + return 3; + } + } + + // Materialize the capture log into return values. Named groups produce + // no top-level values (they only matter inside Ct). + int cmt_slots = parser->top - initial_stack_size; // lingering Cmt values + int result_count = 0; + size_t cap_i = 0; + while (cap_i < parser->cap_len) { + if (parser->caps[cap_i].kind == PGEN_CAP_GROUP_OPEN) { + pgen_cap_skip(parser, &cap_i); + } else { + result_count += pgen_cap_eval(parser, &cap_i); + } + } + + // Drop the lingering Cmt value slots sitting beneath the results + for (int i = 0; i < cmt_slots; i++) { + lua_remove(L, initial_stack_size + 1); + } + + if (result_count > 0) { + moonscript_parse_native_free(parser); + return result_count; + } + + // Success case with no captures + lua_pushinteger(L, parser->pos + 1); + moonscript_parse_native_free(parser); + return 1; // Return position of consumed input +} + +// Lua module function registration table +static const struct luaL_Reg moonscript_parse_native_module[] = { + {"parse", l_moonscript_parse_native_parse}, // Expose l_parsername_parse as "parse" in Lua + {NULL, NULL} // Sentinel +}; + +// Lua module entry point (compatible with Lua 5.1+) +// Note: LUA_VERSION_NUM wasn't defined before 5.1 +#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 502 +// Lua 5.2+ uses luaL_setfuncs +int luaopen_moonscript_parse_native(lua_State *L) { + if (luaL_newmetatable(L, PGEN_PARSER_MT)) { + lua_pushcfunction(L, l_moonscript_parse_native_gc); + lua_setfield(L, -2, "__gc"); + } + lua_pop(L, 1); + __const_init(L); + __cmt_init(L); + luaL_newlib(L, moonscript_parse_native_module); // Creates table and registers functions + return 1; +} +#else +// Lua 5.1 uses luaL_register. Register into a fresh table rather than a +// named global: a name would be shared through package.loaded, so loading +// two parsers compiled with the same parser_name in one process would +// silently overwrite the first module's parse function. +int luaopen_moonscript_parse_native(lua_State *L) { + if (luaL_newmetatable(L, PGEN_PARSER_MT)) { + lua_pushcfunction(L, l_moonscript_parse_native_gc); + lua_setfield(L, -2, "__gc"); + } + lua_pop(L, 1); + __const_init(L); + __cmt_init(L); + lua_newtable(L); + luaL_register(L, NULL, moonscript_parse_native_module); + return 1; +} +#endif + +/* +To compile as a Lua module: +gcc -shared -o moonscript_parse_native.so -fPIC moonscript_parse_native.c `pkg-config --cflags --libs lua5.1` + +To use in Lua: +local moonscript_parse_native = require "moonscript_parse_native" +local result = moonscript_parse_native.parse("your input string") +*/ diff --git a/moonscript/parse/slow.lua b/moonscript/parse/slow.lua new file mode 100644 index 00000000..b210e02c --- /dev/null +++ b/moonscript/parse/slow.lua @@ -0,0 +1,13560 @@ +-- Generated by pgen 0.1.0 (Lua target) +-- moonscript_parse_slow - generated parser +-- +-- Self-contained pure Lua module (Lua 5.1+ and LuaJIT), no dependencies. +-- local moonscript_parse_slow = require "moonscript_parse_slow" +-- local result = moonscript_parse_slow.parse("your input string") + +local select, type, error, pcall, tostring = select, type, error, pcall, tostring +local byte, sub = string.byte, string.sub +local unpack = table.unpack or unpack +local pack = table.pack or function(...) return {n = select("#", ...), ...} end + +-- Maximum rule-call recursion depth before the parse is aborted with a Lua +-- error (catch with pcall). Configure with the max_depth compile option. +local MAX_DEPTH = 5000 + +-- Capture log entry kinds. Captures are recorded as log entries during +-- matching and only materialized into Lua values after the whole parse +-- succeeds; backtracking rewinds the log length, so discarded speculative +-- captures are never built. The exception is Cmt: its callback runs +-- mid-parse and its extra return values are stored in the parser's values +-- array, referenced by CAP_VALUE entries. +local CAP_STR, CAP_CONST, CAP_NIL, CAP_POS, CAP_VALUE = 1, 2, 3, 4, 5 +local CAP_TBL_OPEN, CAP_TBL_CLOSE = 6, 7 +local CAP_GROUP_OPEN, CAP_GROUP_CLOSE = 8, 9 +local CAP_FN_OPEN, CAP_FN_CLOSE = 10, 11 + +local rules = {} +local sets = {} +local disp = {} +local cmt_fns = {} +-- Sentinel pushed by `prevent`: no measured width compares greater +local IND_PREVENT = math.huge + +-- Records the furthest input position where a match attempt failed (only +-- ever increases); parse() reports it when the overall parse fails without +-- a label. Not recorded in single-character matchers, mirroring the C +-- target. +local function record_furthest(parser) + if parser.pos > parser.furthest_fail then + parser.furthest_fail = parser.pos + end +end + +-- Append one capture log entry (parallel arrays, truncated by cap_n rewinds) +local function cap_push(parser, kind, aux, start, len) + local n = parser.cap_n + 1 + parser.cap_n = n + parser.cap_kind[n] = kind + parser.cap_aux[n] = aux + parser.cap_start[n] = start + parser.cap_size[n] = len +end + +-- Advance past one complete log item (a single entry, or a whole bracketed +-- range including anything nested), returning the index after it +local function cap_skip(parser, i) + local ck = parser.cap_kind + local kind = ck[i] + i = i + 1 + if kind == CAP_TBL_OPEN or kind == CAP_GROUP_OPEN or kind == CAP_FN_OPEN then + local depth = 1 + while depth > 0 do + kind = ck[i] + if kind == CAP_TBL_OPEN or kind == CAP_GROUP_OPEN or kind == CAP_FN_OPEN then + depth = depth + 1 + elseif kind == CAP_TBL_CLOSE or kind == CAP_GROUP_CLOSE or kind == CAP_FN_CLOSE then + depth = depth - 1 + end + i = i + 1 + end + end + return i +end + +local cap_eval + +-- Append the single value a capture group produces to out: its first inner +-- capture value, or the text it matched when its contents produce no values. +-- Returns the index past the group's close entry. +local function cap_eval_group(parser, i, out) + local open = i + local after = cap_skip(parser, i) + local close = after - 1 + + local j = open + 1 + while j < close do + local before_n = out.n + j = cap_eval(parser, j, out) + if out.n > before_n then + -- keep only the first value + for k = before_n + 2, out.n do out[k] = nil end + out.n = before_n + 1 + return after + end + end + + -- no values: the group's value is the text it matched + local start = parser.cap_start[open] + out.n = out.n + 1 + out[out.n] = sub(parser.input, start + 1, parser.cap_start[close]) + return after +end + +-- Materialize one log item (entry or bracketed range) at i, appending its +-- values to out (out.n counts values so nil captures are preserved). +-- Returns the index past the item. Runs once after a successful parse (and +-- on demand at Cmt boundaries), so it is not on the matching hot path. +function cap_eval(parser, i, out) + local ck = parser.cap_kind + local kind = ck[i] + if kind == CAP_STR then + local start = parser.cap_start[i] + out.n = out.n + 1 + out[out.n] = sub(parser.input, start + 1, start + parser.cap_size[i]) + return i + 1 + elseif kind == CAP_CONST then + out.n = out.n + 1 + out[out.n] = parser.cap_aux[i] + return i + 1 + elseif kind == CAP_NIL then + out.n = out.n + 1 + out[out.n] = nil + return i + 1 + elseif kind == CAP_POS then + out.n = out.n + 1 + out[out.n] = parser.cap_start[i] + 1 + return i + 1 + elseif kind == CAP_VALUE then + out.n = out.n + 1 + out[out.n] = parser.values[parser.cap_aux[i]] + return i + 1 + elseif kind == CAP_GROUP_OPEN then + return cap_eval_group(parser, i, out) + elseif kind == CAP_FN_OPEN then + -- Transform capture: inner values become arguments, the callback's + -- return values become the capture values (innermost-first order falls + -- out of the recursion here) + local open = i + local fn = parser.cap_aux[i] + local args = {n = 0} + local j = open + 1 + while ck[j] ~= CAP_FN_CLOSE do + if ck[j] == CAP_GROUP_OPEN then + -- named groups are not visible as arguments (as at the top level) + j = cap_skip(parser, j) + else + j = cap_eval(parser, j, args) + end + end + if args.n == 0 then + -- no inner captures: the callback receives the matched text + local start = parser.cap_start[open] + args[1] = sub(parser.input, start + 1, parser.cap_start[j]) + args.n = 1 + end + -- callback errors propagate (abort materialization with the original + -- error value) + local rets = pack(fn(unpack(args, 1, args.n))) + for k = 1, rets.n do + out.n = out.n + 1 + out[out.n] = rets[k] + end + return j + 1 + else -- CAP_TBL_OPEN + local tbl = {} + local j = i + 1 + local array_idx = 1 + local item = {n = 0} + while ck[j] ~= CAP_TBL_CLOSE do + if ck[j] == CAP_GROUP_OPEN then + local group_name = parser.cap_aux[j] + item.n = 0 + j = cap_eval_group(parser, j, item) + tbl[group_name] = item[1] + else + item.n = 0 + j = cap_eval(parser, j, item) + for k = 1, item.n do + tbl[array_idx] = item[k] + array_idx = array_idx + 1 + end + end + end + out.n = out.n + 1 + out[out.n] = tbl + return j + 1 + end +end + +-- Match the text of the most recent visible named capture group at the +-- current input position. Groups inside completed capture tables are not +-- visible. +local function cap_match_back(parser, name) + local ck, ca = parser.cap_kind, parser.cap_aux + local cs, cz = parser.cap_start, parser.cap_size + local i = parser.cap_n + while i >= 1 do + local kind = ck[i] + if kind == CAP_TBL_CLOSE or kind == CAP_GROUP_CLOSE or kind == CAP_FN_CLOSE then + local close = i + local depth = 1 + while depth > 0 do + i = i - 1 + local k2 = ck[i] + if k2 == CAP_TBL_CLOSE or k2 == CAP_GROUP_CLOSE or k2 == CAP_FN_CLOSE then + depth = depth + 1 + elseif k2 == CAP_TBL_OPEN or k2 == CAP_GROUP_OPEN or k2 == CAP_FN_OPEN then + depth = depth - 1 + end + end + if kind == CAP_GROUP_CLOSE and ca[i] == name then + local text + local inner = i + 1 + if inner == close then + -- group captured nothing: its value is the text it matched + text = sub(parser.input, cs[i] + 1, cs[close]) + elseif ck[inner] == CAP_STR then + text = sub(parser.input, cs[inner] + 1, cs[inner] + cz[inner]) + elseif ck[inner] == CAP_CONST then + if type(ca[inner]) ~= "string" then + return false + end + text = ca[inner] + else + return false -- group holds a non-string value + end + if parser.pos + #text <= parser.input_len and + sub(parser.input, parser.pos + 1, parser.pos + #text) == text then + parser.pos = parser.pos + #text + return true + end + return false + end + end + i = i - 1 + end + return false +end + +local floor = math.floor + +-- Run a match-time capture: materialize the inner captures, call the +-- callback with (subject, pos, ...captures), and interpret its results per +-- lpeg semantics: position/true = success, false/nil = failure, extra +-- return values become captures (stored in the parser's values array) +local function run_cmt(parser, fn, start_pos, cap_base) + local pos_after_inner = parser.pos + + local args = {parser.input, pos_after_inner + 1, n = 2} + local i = cap_base + 1 + while i <= parser.cap_n do + if parser.cap_kind[i] == CAP_GROUP_OPEN then + -- named groups only matter inside Ct; they aren't passed as arguments + i = cap_skip(parser, i) + else + i = cap_eval(parser, i, args) + end + end + parser.cap_n = cap_base -- consume the inner captures + + -- callback errors propagate (abort the parse with the original value) + local rets = pack(fn(unpack(args, 1, args.n))) + + local ok = false + if rets.n > 0 then + local first = rets[1] + if type(first) == "number" then + -- number = new position (1-based from Lua), must be in range + -- [pos_after_inner, input_len]. Floored so a fractional return can't + -- put the parser on a non-integer position. + local new_pos = floor(first) - 1 + if new_pos >= pos_after_inner and new_pos <= parser.input_len then + parser.pos = new_pos + ok = true + end + elseif first == true then + -- true = succeed without consuming (position stays at pos_after_inner) + ok = true + end + end + + if ok then + parser.success = true + if rets.n > 1 then + local values = parser.values + local vn = parser.values_n + for r = 2, rets.n do + vn = vn + 1 + values[vn] = rets[r] + cap_push(parser, CAP_VALUE, vn, 0, 0) + end + parser.values_n = vn + end + else + parser.success = false + record_furthest(parser) + parser.pos = start_pos + end +end + +-- Rewind the indenter trail to a previous length, undoing pushes and pops +local function ind_trail_rewind(parser, index) + local n = parser.trail_n + if n <= index then return end + local trail_id, trail_op, trail_val = parser.trail_id, parser.trail_op, parser.trail_val + local stacks = parser.ind_stacks + while n > index do + local s = stacks[trail_id[n]] + if trail_op[n] == 0 then + -- undo push + s.n = s.n - 1 + else + -- undo pop: restore the popped value + s.n = s.n + 1 + s[s.n] = trail_val[n] + end + n = n - 1 + end + parser.trail_n = n +end + +local function ind_push(parser, sidx, value) + local s = parser.ind_stacks[sidx] + s.n = s.n + 1 + s[s.n] = value + local tn = parser.trail_n + 1 + parser.trail_n = tn + parser.trail_id[tn] = sidx + parser.trail_op[tn] = 0 + parser.trail_val[tn] = value +end + +-- Pop the stack; returns false if the stack is empty +local function ind_pop(parser, sidx) + local s = parser.ind_stacks[sidx] + local sn = s.n + if sn == 0 then + return false + end + local value = s[sn] + s.n = sn - 1 + local tn = parser.trail_n + 1 + parser.trail_n = tn + parser.trail_id[tn] = sidx + parser.trail_op[tn] = 1 + parser.trail_val[tn] = value + return true +end + +-- Measure the indentation width of the run of space/tab characters at the +-- current position (space = 1, tab = tab_width). Also returns the first +-- position past the run. +local function ind_measure(parser, tab_width) + local input, input_len = parser.input, parser.input_len + local p = parser.pos + local width = 0 + while p < input_len do + local c = byte(input, p + 1) + if c == 32 then + width = width + 1 + elseif c == 9 then + width = width + tab_width + else + break + end + p = p + 1 + end + return width, p +end + +-- Callback (Cmt/Cfn) infrastructure: load each callback's Lua code once +do + local load_chunk = loadstring or load + do + local chunk, load_err = load_chunk(" local tree = require(\"moonscript.parse.tree\")\n return function(lhs, assign)\n return tree.format_single_assign(lhs, assign)\n end", "pgen Cfn 0") + if not chunk then + error("Failed to load Cfn callback 0: " .. tostring(load_err)) + end + local run_ok, fn = pcall(chunk) + if not run_ok then + error("Failed to run Cfn chunk 0: " .. tostring(fn)) + end + if type(fn) ~= "function" then + error("Cfn chunk 0 did not return a function") + end + cmt_fns[0] = fn + end + do + local chunk, load_err = load_chunk("return function(stm, dec)\n if dec then\n return {\"decorated\", stm, dec}\n end\n return stm\n end", "pgen Cfn 1") + if not chunk then + error("Failed to load Cfn callback 1: " .. tostring(load_err)) + end + local run_ok, fn = pcall(chunk) + if not run_ok then + error("Failed to run Cfn chunk 1: " .. tostring(fn)) + end + if type(fn) ~= "function" then + error("Cfn chunk 1 did not return a function") + end + cmt_fns[1] = fn + end + do + local chunk, load_err = load_chunk("return function(p, value)\n if type(value) == \"table\" then\n value[-1] = p\n end\n return value\n end", "pgen Cfn 2") + if not chunk then + error("Failed to load Cfn callback 2: " .. tostring(load_err)) + end + local run_ok, fn = pcall(chunk) + if not run_ok then + error("Failed to run Cfn chunk 2: " .. tostring(fn)) + end + if type(fn) ~= "function" then + error("Cfn chunk 2 did not return a function") + end + cmt_fns[2] = fn + end + do + local chunk, load_err = load_chunk(" local tree = require(\"moonscript.parse.tree\")\n return function(lhs, assign)\n return tree.format_assign(lhs, assign)\n end", "pgen Cfn 3") + if not chunk then + error("Failed to load Cfn callback 3: " .. tostring(load_err)) + end + local run_ok, fn = pcall(chunk) + if not run_ok then + error("Failed to run Cfn chunk 3: " .. tostring(fn)) + end + if type(fn) ~= "function" then + error("Cfn chunk 3 did not return a function") + end + cmt_fns[3] = fn + end + do + local chunk, load_err = load_chunk("return function(name, p)\n return {\n {\"key_literal\", name},\n {\"ref\", name, [-1] = p},\n }\n end", "pgen Cfn 4") + if not chunk then + error("Failed to load Cfn callback 4: " .. tostring(load_err)) + end + local run_ok, fn = pcall(chunk) + if not run_ok then + error("Failed to run Cfn chunk 4: " .. tostring(fn)) + end + if type(fn) ~= "function" then + error("Cfn chunk 4 did not return a function") + end + cmt_fns[4] = fn + end + do + local chunk, load_err = load_chunk(" local tree = require(\"moonscript.parse.tree\")\n return function(callee, args)\n return tree.join_chain(callee, args)\n end", "pgen Cfn 5") + if not chunk then + error("Failed to load Cfn callback 5: " .. tostring(load_err)) + end + local run_ok, fn = pcall(chunk) + if not run_ok then + error("Failed to run Cfn chunk 5: " .. tostring(fn)) + end + if type(fn) ~= "function" then + error("Cfn chunk 5 did not return a function") + end + cmt_fns[5] = fn + end + do + local chunk, load_err = load_chunk("return function(...)\n if select(\"#\", ...) == 1 then\n return ...\n end\n return {\"exp\", ...}\n end", "pgen Cfn 6") + if not chunk then + error("Failed to load Cfn callback 6: " .. tostring(load_err)) + end + local run_ok, fn = pcall(chunk) + if not run_ok then + error("Failed to run Cfn chunk 6: " .. tostring(fn)) + end + if type(fn) ~= "function" then + error("Cfn chunk 6 did not return a function") + end + cmt_fns[6] = fn + end + do + local chunk, load_err = load_chunk("return function(eq_start, eq_end, content)\n return {\"string\", \"[\" .. (\"=\"):rep(eq_end - eq_start) .. \"[\", content}\n end", "pgen Cfn 7") + if not chunk then + error("Failed to load Cfn callback 7: " .. tostring(load_err)) + end + local run_ok, fn = pcall(chunk) + if not run_ok then + error("Failed to run Cfn chunk 7: " .. tostring(fn)) + end + if type(fn) ~= "function" then + error("Cfn chunk 7 did not return a function") + end + cmt_fns[7] = fn + end + do + local chunk, load_err = load_chunk(" local subject, pos, node = ...\n local last = node[#node]\n local t = type(last) == \"table\" and last[1]\n if t == \"dot\" or t == \"index\" or t == \"slice\" then\n return pos, node\n end\n return false\n ", "pgen Cmt 8") + if not chunk then + error("Failed to load Cmt callback 8: " .. tostring(load_err)) + end + cmt_fns[8] = chunk + end +end + +-- Character set lookup tables (byte -> true) +sets[1] = { [46] = true, [92] = true } -- ".\\" +sets[2] = { [43] = true, [45] = true, [42] = true, [47] = true, [37] = true, [94] = true, [62] = true, [60] = true, [124] = true, [38] = true } -- "+-*/%^><|&" +sets[3] = { [13] = true, [10] = true } -- "\r\n" +sets[4] = { [117] = true, [85] = true } -- "uU" +sets[5] = { [108] = true, [76] = true } -- "lL" +sets[6] = { [101] = true, [69] = true } -- "eE" +sets[7] = { [32] = true, [9] = true } -- " \t" +sets[8] = { [32] = true, [9] = true, [13] = true, [10] = true } -- " \t\r\n" + +-- FIRST-byte dispatch tables +do -- dispatch tables for choice 1 + local dmask1 = { [1] = true, s = { }, full = false } + local dmask2 = { [1] = true, [2] = true, [3] = true, [4] = true, s = { }, full = true } + local dmask3 = { [1] = true, [4] = true, s = { [4] = true }, full = false } + local dmask4 = { [1] = true, [3] = true, s = { [3] = true }, full = false } + local dmask5 = { [1] = true, [2] = true, s = { }, full = false } + local bytes = {} + for b = 0, 255 do bytes[b] = dmask1 end + bytes[9] = dmask2 + bytes[32] = dmask2 + bytes[40] = dmask3 + bytes[45] = dmask2 + bytes[46] = dmask4 + bytes[64] = dmask5 + disp[1] = { bytes = bytes, eof = dmask1 } +end +do -- dispatch tables for choice 2 + local dmask1 = { [1] = true, s = { }, full = false } + local dmask2 = { [1] = true, [2] = true, s = { }, full = false } + local dmask3 = { [1] = true, [3] = true, [4] = true, s = { [3] = true, [4] = true }, full = false } + local bytes = {} + for b = 0, 255 do bytes[b] = dmask1 end + bytes[46] = dmask2 + bytes[91] = dmask3 + disp[2] = { bytes = bytes, eof = dmask1 } +end +do -- dispatch tables for choice 3 + local dmask1 = { [4] = true, s = { [4] = true }, full = false } + local dmask2 = { [1] = true, [2] = true, [3] = true, [4] = true, s = { }, full = true } + local dmask3 = { [2] = true, [4] = true, s = { [2] = true, [4] = true }, full = false } + local dmask4 = { [3] = true, [4] = true, s = { [3] = true, [4] = true }, full = false } + local dmask5 = { [1] = true, [4] = true, s = { [4] = true }, full = false } + local bytes = {} + for b = 0, 255 do bytes[b] = dmask1 end + bytes[9] = dmask2 + bytes[32] = dmask2 + bytes[42] = dmask3 + bytes[45] = dmask2 + bytes[94] = dmask4 + bytes[99] = dmask5 + disp[3] = { bytes = bytes, eof = dmask1 } +end +do -- dispatch tables for choice 4 + local dmask1 = { [4] = true, s = { [4] = true }, full = false } + local dmask2 = { [1] = true, [4] = true, s = { [4] = true }, full = false } + local dmask3 = { [3] = true, [4] = true, s = { [3] = true, [4] = true }, full = false } + local dmask4 = { [2] = true, [4] = true, s = { [2] = true, [4] = true }, full = false } + local bytes = {} + for b = 0, 255 do bytes[b] = dmask1 end + bytes[9] = dmask2 + bytes[32] = dmask2 + bytes[33] = dmask2 + bytes[34] = dmask3 + bytes[39] = dmask4 + bytes[40] = dmask2 + bytes[45] = dmask2 + disp[4] = { bytes = bytes, eof = dmask1 } +end +do -- dispatch tables for choice 5 + local dmask1 = { s = { }, full = false } + local dmask2 = { [1] = true, [2] = true, [3] = true, [4] = true, s = { }, full = true } + local dmask3 = { [3] = true, s = { [3] = true }, full = false } + local dmask4 = { [4] = true, s = { [4] = true }, full = false } + local dmask5 = { [1] = true, s = { }, full = false } + local dmask6 = { [2] = true, s = { [2] = true }, full = false } + local bytes = {} + for b = 0, 255 do bytes[b] = dmask1 end + bytes[9] = dmask2 + bytes[32] = dmask2 + bytes[34] = dmask3 + bytes[39] = dmask4 + bytes[45] = dmask2 + bytes[64] = dmask5 + bytes[65] = dmask5 + bytes[66] = dmask5 + bytes[67] = dmask5 + bytes[68] = dmask5 + bytes[69] = dmask5 + bytes[70] = dmask5 + bytes[71] = dmask5 + bytes[72] = dmask5 + bytes[73] = dmask5 + bytes[74] = dmask5 + bytes[75] = dmask5 + bytes[76] = dmask5 + bytes[77] = dmask5 + bytes[78] = dmask5 + bytes[79] = dmask5 + bytes[80] = dmask5 + bytes[81] = dmask5 + bytes[82] = dmask5 + bytes[83] = dmask5 + bytes[84] = dmask5 + bytes[85] = dmask5 + bytes[86] = dmask5 + bytes[87] = dmask5 + bytes[88] = dmask5 + bytes[89] = dmask5 + bytes[90] = dmask5 + bytes[91] = dmask6 + bytes[95] = dmask5 + bytes[97] = dmask5 + bytes[98] = dmask5 + bytes[99] = dmask5 + bytes[100] = dmask5 + bytes[101] = dmask5 + bytes[102] = dmask5 + bytes[103] = dmask5 + bytes[104] = dmask5 + bytes[105] = dmask5 + bytes[106] = dmask5 + bytes[107] = dmask5 + bytes[108] = dmask5 + bytes[109] = dmask5 + bytes[110] = dmask5 + bytes[111] = dmask5 + bytes[112] = dmask5 + bytes[113] = dmask5 + bytes[114] = dmask5 + bytes[115] = dmask5 + bytes[116] = dmask5 + bytes[117] = dmask5 + bytes[118] = dmask5 + bytes[119] = dmask5 + bytes[120] = dmask5 + bytes[121] = dmask5 + bytes[122] = dmask5 + disp[5] = { bytes = bytes, eof = dmask1 } +end +do -- dispatch tables for choice 6 + local dmask1 = { [9] = true, s = { [9] = true }, full = false } + local dmask2 = { [1] = true, [2] = true, [3] = true, [4] = true, [5] = true, [6] = true, [7] = true, [8] = true, [9] = true, [10] = true, [11] = true, [12] = true, [13] = true, [14] = true, [15] = true, [16] = true, [17] = true, [18] = true, s = { }, full = true } + local dmask3 = { [9] = true, [11] = true, s = { [9] = true, [11] = true }, full = false } + local dmask4 = { [9] = true, [17] = true, s = { [9] = true, [17] = true }, full = false } + local dmask5 = { [9] = true, [18] = true, s = { [9] = true, [18] = true }, full = false } + local dmask6 = { [9] = true, [16] = true, s = { [9] = true, [16] = true }, full = false } + local dmask7 = { [5] = true, [9] = true, s = { [5] = true, [9] = true }, full = false } + local dmask8 = { [6] = true, [7] = true, [9] = true, s = { [6] = true, [7] = true, [9] = true }, full = false } + local dmask9 = { [1] = true, [9] = true, s = { [9] = true }, full = false } + local dmask10 = { [9] = true, [13] = true, s = { [9] = true, [13] = true }, full = false } + local dmask11 = { [3] = true, [9] = true, s = { [3] = true, [9] = true }, full = false } + local dmask12 = { [2] = true, [9] = true, s = { [2] = true, [9] = true }, full = false } + local dmask13 = { [4] = true, [8] = true, [9] = true, s = { [4] = true, [8] = true, [9] = true }, full = false } + local dmask14 = { [9] = true, [14] = true, [15] = true, s = { [9] = true, [14] = true, [15] = true }, full = false } + local dmask15 = { [9] = true, [12] = true, s = { [9] = true, [12] = true }, full = false } + local bytes = {} + for b = 0, 255 do bytes[b] = dmask1 end + bytes[9] = dmask2 + bytes[32] = dmask2 + bytes[35] = dmask3 + bytes[40] = dmask4 + bytes[45] = dmask2 + bytes[46] = dmask5 + bytes[48] = dmask5 + bytes[49] = dmask5 + bytes[50] = dmask5 + bytes[51] = dmask5 + bytes[52] = dmask5 + bytes[53] = dmask5 + bytes[54] = dmask5 + bytes[55] = dmask5 + bytes[56] = dmask5 + bytes[57] = dmask5 + bytes[61] = dmask4 + bytes[91] = dmask6 + bytes[99] = dmask7 + bytes[102] = dmask8 + bytes[105] = dmask9 + bytes[110] = dmask10 + bytes[115] = dmask11 + bytes[117] = dmask12 + bytes[119] = dmask13 + bytes[123] = dmask14 + bytes[126] = dmask15 + disp[6] = { bytes = bytes, eof = dmask1 } +end +do -- dispatch tables for choice 7 + local dmask1 = { [11] = true, s = { [11] = true }, full = false } + local dmask2 = { [1] = true, [2] = true, [3] = true, [4] = true, [5] = true, [6] = true, [7] = true, [8] = true, [9] = true, [10] = true, [11] = true, s = { }, full = true } + local dmask3 = { [10] = true, [11] = true, s = { [10] = true, [11] = true }, full = false } + local dmask4 = { [9] = true, [11] = true, s = { [9] = true, [11] = true }, full = false } + local dmask5 = { [4] = true, [5] = true, [11] = true, s = { [4] = true, [5] = true, [11] = true }, full = false } + local dmask6 = { [1] = true, [11] = true, s = { [11] = true }, full = false } + local dmask7 = { [8] = true, [11] = true, s = { [8] = true, [11] = true }, full = false } + local dmask8 = { [7] = true, [11] = true, s = { [7] = true, [11] = true }, full = false } + local dmask9 = { [6] = true, [11] = true, s = { [6] = true, [11] = true }, full = false } + local dmask10 = { [2] = true, [3] = true, [11] = true, s = { [2] = true, [3] = true, [11] = true }, full = false } + local bytes = {} + for b = 0, 255 do bytes[b] = dmask1 end + bytes[9] = dmask2 + bytes[32] = dmask2 + bytes[45] = dmask2 + bytes[98] = dmask3 + bytes[99] = dmask3 + bytes[101] = dmask4 + bytes[102] = dmask5 + bytes[105] = dmask6 + bytes[108] = dmask7 + bytes[114] = dmask8 + bytes[115] = dmask9 + bytes[119] = dmask10 + disp[7] = { bytes = bytes, eof = dmask1 } +end +do -- dispatch tables for choice 8 + local dmask1 = { [1] = true, [3] = true, s = { [3] = true }, full = false } + local dmask2 = { [1] = true, [2] = true, [3] = true, [4] = true, s = { }, full = true } + local dmask3 = { [1] = true, [2] = true, [3] = true, s = { }, full = false } + local bytes = {} + for b = 0, 255 do bytes[b] = dmask1 end + bytes[9] = dmask2 + bytes[32] = dmask2 + bytes[34] = dmask2 + bytes[39] = dmask2 + bytes[45] = dmask2 + bytes[58] = dmask3 + bytes[64] = dmask3 + bytes[65] = dmask3 + bytes[66] = dmask3 + bytes[67] = dmask3 + bytes[68] = dmask3 + bytes[69] = dmask3 + bytes[70] = dmask3 + bytes[71] = dmask3 + bytes[72] = dmask3 + bytes[73] = dmask3 + bytes[74] = dmask3 + bytes[75] = dmask3 + bytes[76] = dmask3 + bytes[77] = dmask3 + bytes[78] = dmask3 + bytes[79] = dmask3 + bytes[80] = dmask3 + bytes[81] = dmask3 + bytes[82] = dmask3 + bytes[83] = dmask3 + bytes[84] = dmask3 + bytes[85] = dmask3 + bytes[86] = dmask3 + bytes[87] = dmask3 + bytes[88] = dmask3 + bytes[89] = dmask3 + bytes[90] = dmask3 + bytes[91] = dmask2 + bytes[95] = dmask3 + bytes[97] = dmask3 + bytes[98] = dmask3 + bytes[99] = dmask3 + bytes[100] = dmask3 + bytes[101] = dmask3 + bytes[102] = dmask3 + bytes[103] = dmask3 + bytes[104] = dmask3 + bytes[105] = dmask3 + bytes[106] = dmask3 + bytes[107] = dmask3 + bytes[108] = dmask3 + bytes[109] = dmask3 + bytes[110] = dmask3 + bytes[111] = dmask3 + bytes[112] = dmask3 + bytes[113] = dmask3 + bytes[114] = dmask3 + bytes[115] = dmask3 + bytes[116] = dmask3 + bytes[117] = dmask3 + bytes[118] = dmask3 + bytes[119] = dmask3 + bytes[120] = dmask3 + bytes[121] = dmask3 + bytes[122] = dmask3 + disp[8] = { bytes = bytes, eof = dmask1 } +end +do -- dispatch tables for choice 9 + local dmask1 = { s = { }, full = false } + local dmask2 = { [1] = true, [2] = true, [3] = true, [4] = true, [5] = true, [6] = true, [7] = true, [8] = true, [9] = true, [10] = true, [11] = true, s = { }, full = true } + local dmask3 = { [6] = true, s = { [6] = true }, full = false } + local dmask4 = { [8] = true, s = { [8] = true }, full = false } + local dmask5 = { [11] = true, s = { [11] = true }, full = false } + local dmask6 = { [3] = true, [9] = true, s = { [3] = true, [9] = true }, full = false } + local dmask7 = { [7] = true, s = { [7] = true }, full = false } + local dmask8 = { [4] = true, [10] = true, s = { [4] = true, [10] = true }, full = false } + local dmask9 = { [2] = true, s = { [2] = true }, full = false } + local dmask10 = { [1] = true, s = { }, full = false } + local dmask11 = { [5] = true, s = { [5] = true }, full = false } + local bytes = {} + for b = 0, 255 do bytes[b] = dmask1 end + bytes[9] = dmask2 + bytes[32] = dmask2 + bytes[33] = dmask3 + bytes[45] = dmask2 + bytes[46] = dmask4 + bytes[47] = dmask5 + bytes[60] = dmask6 + bytes[61] = dmask7 + bytes[62] = dmask8 + bytes[97] = dmask9 + bytes[111] = dmask10 + bytes[126] = dmask11 + disp[9] = { bytes = bytes, eof = dmask1 } +end + +-- Rule functions +rules["Root"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 4 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["White"](parser) +if parser.success then +rules["File"](parser) +end +if parser.success then +rules["White"](parser) +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + -- match any 1 characters +if parser.pos + 1 <= parser.input_len then + parser.pos = parser.pos + 1 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Advance"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- indenter advance (stack 0): push width if deeper than top, consume nothing + local ind_width = ind_measure(parser, 4) + local ind_s = parser.ind_stacks[1] + if ind_s.n > 0 and ind_width > ind_s[ind_s.n] then + ind_push(parser, 1, ind_width) + else + parser.success = false + record_furthest(parser) + + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["ArgBlock"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["ArgLine"](parser) +if parser.success then +do -- zero or more repetitions + while true do + do -- sequence with 4 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["SpaceBreak"](parser) +end +if parser.success then +rules["ArgLine"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end +if parser.success then +rules["PopIndent"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["ArgLine"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["CheckIndent"](parser) +if parser.success then +rules["ExpList"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Assign"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 4 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "assign", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character "=" +if byte(parser.input, parser.pos + 1) == 61 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +do -- choice with 2 alternatives + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- choice with 3 alternatives + rules["With"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + rules["If"](parser) +end +if not parser.success and not parser.throw_label then + parser.success = true + rules["Switch"](parser) +end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- choice with 2 alternatives + rules["TableBlock"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + rules["ExpListLow"](parser) +end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Assignable"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- choice with 3 alternatives + do -- match-time capture (Cmt id=8) + local cmt_cap_base = parser.cap_n + local cmt_start_pos = parser.pos + local cmt_trail = parser.trail_n + rules["Chain"](parser) + if parser.success then + run_cmt(parser, cmt_fns[8], cmt_start_pos, cmt_cap_base) + if not parser.success then + ind_trail_rewind(parser, cmt_trail) + end + end +end +if not parser.success and not parser.throw_label then + parser.success = true + rules["Name"](parser) +end +if not parser.success and not parser.throw_label then + parser.success = true + rules["SelfName"](parser) +end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["AssignableNameList"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["NameOrDestructure"](parser) +if parser.success then +do -- zero or more repetitions + while true do + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["NameOrDestructure"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["BinaryOperator"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- choice with 2 alternatives + rules["WordOperators"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + rules["CharOperators"](parser) +end +end +if parser.success then +do -- zero or more repetitions + while true do + rules["SpaceBreak"](parser) + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Block"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Line"](parser) +if parser.success then +do -- zero or more repetitions + while true do + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- at least 1 repetitions + local pp_pos = parser.pos + local rep_count = 0 + while true do + rules["Break"](parser) + if not parser.success then + break + end + rep_count = rep_count + 1 + end + if parser.throw_label then + -- Keep failure state, propagate labeled failure + elseif rep_count >= 1 then + parser.success = true + else + parser.pos = pp_pos + + end +end +if parser.success then +rules["Line"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Body"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- choice with 2 alternatives + do -- sequence with 4 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +rules["Break"](parser) +end +if parser.success then +do -- zero or more repetitions + while true do + rules["EmptyLine"](parser) + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end +if parser.success then +rules["InBlock"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + rules["Statement"](parser) + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Break"] = function(parser) + local start = parser.pos + -- Position-pure rule: a single-slot memo short-circuits the repeated + -- calls that backtracking alternatives make at the same position + if parser.memo_pos[1] == start + 1 then + local memo_end = parser.memo_end[1] + if memo_end == -1 then + parser.success = false + return false + end + parser.pos = memo_end + parser.success = true + return true + end +local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 2 patterns + local pp_pos = parser.pos + do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + -- match single character "\r" +if byte(parser.input, parser.pos + 1) == 13 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +if parser.success then +-- match single character "\n" +if byte(parser.input, parser.pos + 1) == 10 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos + end +end + parser.memo_pos[1] = start + 1 + parser.memo_end[1] = parser.success and parser.pos or -1 + + parser.depth = depth - 1 + return parser.success +end + + +rules["BreakLoop"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- choice with 2 alternatives + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 4 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match literal "break" +if sub(parser.input, parser.pos + 1, parser.pos + 5) == "break" then + parser.pos = parser.pos + 5 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "break", 0, 0) +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 4 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match literal "continue" +if sub(parser.input, parser.pos + 1, parser.pos + 8) == "continue" then + parser.pos = parser.pos + 8 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "continue", 0, 0) +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Callable"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- FIRST-byte dispatched ordered choice + local dd = disp[1] + local db = byte(parser.input, parser.pos + 1) + local dm = db and dd.bytes[db] or dd.eof + parser.success = false + if not parser.success and not parser.throw_label and dm[1] then + parser.success = true + do -- transform capture (Cfn id=2) + local fn_cap_start = parser.cap_n + cap_push(parser, CAP_FN_OPEN, cmt_fns[2], parser.pos, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + cap_push(parser, CAP_POS, nil, parser.pos, 0) -- position capture +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "ref", 0, 0) +end +if parser.success then +rules["Name"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_FN_CLOSE, nil, parser.pos, 0) + else + parser.cap_n = fn_cap_start + end +end +end +if not parser.success and not parser.throw_label and dm[2] then + if dm.s[2] then record_furthest(parser) end + parser.success = true + rules["SelfName"](parser) +end +if not parser.success and not parser.throw_label and dm[3] then + if dm.s[3] then record_furthest(parser) end + parser.success = true + rules["VarArg"](parser) +end +if not parser.success and not parser.throw_label and dm[4] then + if dm.s[4] then record_furthest(parser) end + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "parens", 0, 0) +end +if parser.success then +rules["Parens"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end + if not parser.success and not parser.throw_label then + record_furthest(parser) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Chain"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- choice with 2 alternatives + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "chain", 0, 0) +end +if parser.success then +do -- choice with 3 alternatives + rules["Callable"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + rules["String"](parser) +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character set ".\\" + local sb = byte(parser.input, parser.pos + 1) + if sb and sets[1][sb] then + parser.pos = parser.pos + 1 + else + parser.success = false + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +end +end +if parser.success then +rules["ChainItems"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "chain", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +do -- choice with 2 alternatives + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["DotChainItem"](parser) +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + rules["ChainItems"](parser) + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +if not parser.success and not parser.throw_label then + parser.success = true + rules["ColonChain"](parser) +end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["ChainItem"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- FIRST-byte dispatched ordered choice + local dd = disp[2] + local db = byte(parser.input, parser.pos + 1) + local dm = db and dd.bytes[db] or dd.eof + parser.success = false + if not parser.success and not parser.throw_label and dm[1] then + parser.success = true + rules["Invoke"](parser) +end +if not parser.success and not parser.throw_label and dm[2] then + if dm.s[2] then record_furthest(parser) end + parser.success = true + rules["DotChainItem"](parser) +end +if not parser.success and not parser.throw_label and dm[3] then + if dm.s[3] then record_furthest(parser) end + parser.success = true + rules["Slice"](parser) +end +if not parser.success and not parser.throw_label and dm[4] then + if dm.s[4] then record_furthest(parser) end + parser.success = true + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "index", 0, 0) +end +if parser.success then +-- match single character "[" +if byte(parser.input, parser.pos + 1) == 91 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character "]" +if byte(parser.input, parser.pos + 1) == 93 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end + if not parser.success and not parser.throw_label then + record_furthest(parser) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["ChainItems"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- choice with 2 alternatives + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- at least 1 repetitions + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + local rep_count = 0 + while true do + rules["ChainItem"](parser) + if not parser.success then + break + end + rep_count = rep_count + 1 + end + if parser.throw_label then + -- Keep failure state, propagate labeled failure + elseif rep_count >= 1 then + parser.success = true + else + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + + end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + rules["ColonChain"](parser) + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +if not parser.success and not parser.throw_label then + parser.success = true + rules["ColonChain"](parser) +end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["ChainValue"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- transform capture (Cfn id=5) + local fn_cap_start = parser.cap_n + cap_push(parser, CAP_FN_OPEN, cmt_fns[5], parser.pos, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- choice with 2 alternatives + rules["Chain"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + rules["Callable"](parser) +end +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + rules["InvokeArgs"](parser) + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_FN_CLOSE, nil, parser.pos, 0) + else + parser.cap_n = fn_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["CharOperators"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- capture + local cap_start_pos = parser.pos + do -- match character set "+-*/%^><|&" + local sb = byte(parser.input, parser.pos + 1) + if sb and sets[2][sb] then + parser.pos = parser.pos + 1 + else + parser.success = false + end +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["CheckIndent"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- indenter check (stack 0): consume whitespace, width must equal top + local ind_width, ind_end = ind_measure(parser, 4) + local ind_s = parser.ind_stacks[1] + if ind_s.n > 0 and ind_s[ind_s.n] == ind_width then + parser.pos = ind_end + else + parser.success = false + record_furthest(parser) + + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["ClassBlock"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 4 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- at least 1 repetitions + local pp_pos = parser.pos + local rep_count = 0 + while true do + rules["SpaceBreak"](parser) + if not parser.success then + break + end + rep_count = rep_count + 1 + end + if parser.throw_label then + -- Keep failure state, propagate labeled failure + elseif rep_count >= 1 then + parser.success = true + else + parser.pos = pp_pos + + end +end +if parser.success then +rules["Advance"](parser) +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["ClassLine"](parser) +if parser.success then +do -- zero or more repetitions + while true do + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- at least 1 repetitions + local pp_pos = parser.pos + local rep_count = 0 + while true do + rules["SpaceBreak"](parser) + if not parser.success then + break + end + rep_count = rep_count + 1 + end + if parser.throw_label then + -- Keep failure state, propagate labeled failure + elseif rep_count >= 1 then + parser.success = true + else + parser.pos = pp_pos + + end +end +if parser.success then +rules["ClassLine"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if parser.success then +rules["PopIndent"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["ClassDecl"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 8 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "class", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "class" +if sub(parser.input, parser.pos + 1, parser.pos + 5) == "class" then + parser.pos = parser.pos + 5 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + -- match single character ":" +if byte(parser.input, parser.pos + 1) == 58 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +do -- choice with 2 alternatives + rules["Assignable"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + do -- constant capture (1 values) + cap_push(parser, CAP_NIL, nil, 0, 0) +end +end +end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- choice with 2 alternatives + do -- sequence with 6 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match literal "extends" +if sub(parser.input, parser.pos + 1, parser.pos + 7) == "extends" then + parser.pos = parser.pos + 7 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +rules["PreventIndent"](parser) +end +if parser.success then +rules["Exp"](parser) +end +if parser.success then +rules["PopIndent"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- capture + local cap_start_pos = parser.pos + -- match literal "" +if sub(parser.input, parser.pos + 1, parser.pos + 0) == "" then + parser.pos = parser.pos + 0 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end +if parser.success then +do -- choice with 2 alternatives + rules["ClassBlock"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + -- match literal "" +if sub(parser.input, parser.pos + 1, parser.pos + 0) == "" then + parser.pos = parser.pos + 0 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["ClassLine"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["CheckIndent"](parser) +if parser.success then +do -- choice with 3 alternatives + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "props", 0, 0) +end +if parser.success then +rules["KeyValueList"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "stm", 0, 0) +end +if parser.success then +rules["Statement"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "stm", 0, 0) +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 2 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["ColonChain"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["ColonChainItem"](parser) +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Invoke"](parser) +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + rules["ChainItems"](parser) + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["ColonChainItem"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "colon", 0, 0) +end +if parser.success then +-- match single character "\\" +if byte(parser.input, parser.pos + 1) == 92 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["NameRaw"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Comment"] = function(parser) + local start = parser.pos + -- Position-pure rule: a single-slot memo short-circuits the repeated + -- calls that backtracking alternatives make at the same position + if parser.memo_pos[2] == start + 1 then + local memo_end = parser.memo_end[2] + if memo_end == -1 then + parser.success = false + return false + end + parser.pos = memo_end + parser.success = true + return true + end +local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 3 patterns + local pp_pos = parser.pos + -- match literal "--" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "--" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end +if parser.success then +do -- zero or more repetitions + while true do + do -- sequence with 2 patterns + local pp_pos = parser.pos + do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character set "\r\n" + local sb = byte(parser.input, parser.pos + 1) + if sb and sets[3][sb] then + parser.pos = parser.pos + 1 + else + parser.success = false + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +if parser.success then +-- match any 1 characters +if parser.pos + 1 <= parser.input_len then + parser.pos = parser.pos + 1 +else + parser.success = false + record_furthest(parser) + +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end +if parser.success then +do -- lookahead (match without consuming input) + local pp_pos = parser.pos + rules["Stop"](parser) + if parser.success then + parser.pos = pp_pos + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end + parser.memo_pos[2] = start + 1 + parser.memo_end[2] = parser.success and parser.pos or -1 + + parser.depth = depth - 1 + return parser.success +end + + +rules["CompClause"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- choice with 3 alternatives + rules["CompFor"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + rules["CompForEach"](parser) +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 5 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "when", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "when" +if sub(parser.input, parser.pos + 1, parser.pos + 4) == "when" then + parser.pos = parser.pos + 4 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["CompFor"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 6 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "for", 0, 0) +end +if parser.success then +-- match literal "for" +if sub(parser.input, parser.pos + 1, parser.pos + 3) == "for" then + parser.pos = parser.pos + 3 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +rules["Name"](parser) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character "=" +if byte(parser.input, parser.pos + 1) == 61 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 5 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Exp"](parser) +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["Exp"](parser) +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["CompForEach"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 9 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "foreach", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "for" +if sub(parser.input, parser.pos + 1, parser.pos + 3) == "for" then + parser.pos = parser.pos + 3 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + rules["AssignableNameList"](parser) + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "in" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "in" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +do -- choice with 2 alternatives + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "*" +if byte(parser.input, parser.pos + 1) == 42 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "unpack", 0, 0) +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +if not parser.success and not parser.throw_label then + parser.success = true + rules["Exp"](parser) +end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["CompInner"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- choice with 2 alternatives + rules["CompForEach"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + rules["CompFor"](parser) +end +end +if parser.success then +do -- zero or more repetitions + while true do + rules["CompClause"](parser) + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Comprehension"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 7 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "comprehension", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character "[" +if byte(parser.input, parser.pos + 1) == 91 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["Exp"](parser) +end +if parser.success then +rules["CompInner"](parser) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character "]" +if byte(parser.input, parser.pos + 1) == 93 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Do"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 5 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "do", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "do" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "do" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +rules["Body"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["DotChainItem"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "dot", 0, 0) +end +if parser.success then +-- match single character "." +if byte(parser.input, parser.pos + 1) == 46 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["NameRaw"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["DoubleString"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 4 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "string", 0, 0) +end +if parser.success then +do -- capture + local cap_start_pos = parser.pos + -- match single character "\"" +if byte(parser.input, parser.pos + 1) == 34 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end +if parser.success then +do -- zero or more repetitions + while true do + do -- choice with 2 alternatives + do -- capture + local cap_start_pos = parser.pos + do -- at least 1 repetitions + local pp_pos = parser.pos + local rep_count = 0 + while true do + do -- sequence with 2 patterns + local pp_pos = parser.pos + do -- negate (only match if pattern fails) + local pp_pos = parser.pos + -- match literal "#{" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "#{" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +if parser.success then +rules["DoubleStringInner"](parser) +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success then + break + end + rep_count = rep_count + 1 + end + if parser.throw_label then + -- Keep failure state, propagate labeled failure + elseif rep_count >= 1 then + parser.success = true + else + parser.pos = pp_pos + + end +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +if not parser.success and not parser.throw_label then + parser.success = true + rules["DoubleStringInterp"](parser) +end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end +if parser.success then +-- match single character "\"" +if byte(parser.input, parser.pos + 1) == 34 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["DoubleStringInner"] = function(parser) + local start = parser.pos + -- Position-pure rule: a single-slot memo short-circuits the repeated + -- calls that backtracking alternatives make at the same position + if parser.memo_pos[3] == start + 1 then + local memo_end = parser.memo_end[3] + if memo_end == -1 then + parser.success = false + return false + end + parser.pos = memo_end + parser.success = true + return true + end +local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- choice with 3 alternatives + -- match literal "\\\"" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "\\\"" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end +if not parser.success and not parser.throw_label then + parser.success = true + -- match literal "\\\\" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "\\\\" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- sequence with 2 patterns + local pp_pos = parser.pos + do -- negate (only match if pattern fails) + local pp_pos = parser.pos + -- match single character "\"" +if byte(parser.input, parser.pos + 1) == 34 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +if parser.success then +-- match any 1 characters +if parser.pos + 1 <= parser.input_len then + parser.pos = parser.pos + 1 +else + parser.success = false + record_furthest(parser) + +end +end + if not parser.success then + parser.pos = pp_pos + end +end +end +end + parser.memo_pos[3] = start + 1 + parser.memo_end[3] = parser.success and parser.pos or -1 + + parser.depth = depth - 1 + return parser.success +end + + +rules["DoubleStringInterp"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 5 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "interpolate", 0, 0) +end +if parser.success then +-- match literal "#{" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "#{" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +rules["Exp"](parser) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character "}" +if byte(parser.input, parser.pos + 1) == 125 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["EmptyLine"] = function(parser) + local start = parser.pos + -- Position-pure rule: a single-slot memo short-circuits the repeated + -- calls that backtracking alternatives make at the same position + if parser.memo_pos[4] == start + 1 then + local memo_end = parser.memo_end[4] + if memo_end == -1 then + parser.success = false + return false + end + parser.pos = memo_end + parser.success = true + return true + end +local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + rules["SpaceBreak"](parser) + parser.memo_pos[4] = start + 1 + parser.memo_end[4] = parser.success and parser.pos or -1 + + parser.depth = depth - 1 + return parser.success +end + + +rules["Exp"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- transform capture (Cfn id=6) + local fn_cap_start = parser.cap_n + cap_push(parser, CAP_FN_OPEN, cmt_fns[6], parser.pos, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Value"](parser) +if parser.success then +do -- zero or more repetitions + while true do + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["BinaryOperator"](parser) +if parser.success then +rules["Value"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_FN_CLOSE, nil, parser.pos, 0) + else + parser.cap_n = fn_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["ExpList"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Exp"](parser) +if parser.success then +do -- zero or more repetitions + while true do + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["ExpListLow"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Exp"](parser) +if parser.success then +do -- zero or more repetitions + while true do + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- choice with 2 alternatives + do -- sequence with 2 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- sequence with 2 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match single character ";" +if byte(parser.input, parser.pos + 1) == 59 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos + end +end +end +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Export"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 5 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "export", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "export" +if sub(parser.input, parser.pos + 1, parser.pos + 6) == "export" then + parser.pos = parser.pos + 6 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +do -- FIRST-byte dispatched ordered choice + local dd = disp[3] + local db = byte(parser.input, parser.pos + 1) + local dm = db and dd.bytes[db] or dd.eof + parser.success = false + if not parser.success and not parser.throw_label and dm[1] then + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "class", 0, 0) +end +if parser.success then +rules["ClassDecl"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +if not parser.success and not parser.throw_label and dm[2] then + if dm.s[2] then record_furthest(parser) end + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- capture + local cap_start_pos = parser.pos + -- match single character "*" +if byte(parser.input, parser.pos + 1) == 42 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +if not parser.success and not parser.throw_label and dm[3] then + if dm.s[3] then record_furthest(parser) end + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- capture + local cap_start_pos = parser.pos + -- match single character "^" +if byte(parser.input, parser.pos + 1) == 94 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +if not parser.success and not parser.throw_label and dm[4] then + if dm.s[4] then record_furthest(parser) end + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + rules["NameList"](parser) + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "=" +if byte(parser.input, parser.pos + 1) == 61 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + rules["ExpListLow"](parser) + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end + if not parser.success and not parser.throw_label then + record_furthest(parser) + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["File"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + rules["Shebang"](parser) + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +if parser.success then +do -- choice with 2 alternatives + rules["Block"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + -- match literal "" +if sub(parser.input, parser.pos + 1, parser.pos + 0) == "" then + parser.pos = parser.pos + 0 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["FnArgDef"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- choice with 3 alternatives + rules["Name"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + rules["SelfName"](parser) +end +if not parser.success and not parser.throw_label then + parser.success = true + rules["TableLit"](parser) +end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "=" +if byte(parser.input, parser.pos + 1) == 61 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["FnArgDefList"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- choice with 2 alternatives + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["FnArgDef"](parser) +if parser.success then +do -- zero or more repetitions + while true do + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- choice with 2 alternatives + do -- sequence with 2 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos + end +end +if not parser.success and not parser.throw_label then + parser.success = true + rules["Break"](parser) +end +end +if parser.success then +rules["White"](parser) +end +if parser.success then +rules["FnArgDef"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end +if parser.success then +do -- zero or more repetitions + while true do + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- choice with 2 alternatives + do -- sequence with 2 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos + end +end +if not parser.success and not parser.throw_label then + parser.success = true + rules["Break"](parser) +end +end +if parser.success then +rules["White"](parser) +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + rules["VarArg"](parser) + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + rules["VarArg"](parser) + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["FnArgs"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- choice with 2 alternatives + do -- sequence with 6 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + -- match single character "(" +if byte(parser.input, parser.pos + 1) == 40 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +if parser.success then +do -- zero or more repetitions + while true do + rules["SpaceBreak"](parser) + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + rules["FnArgsExpList"](parser) + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if parser.success then +do -- zero or more repetitions + while true do + rules["SpaceBreak"](parser) + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character ")" +if byte(parser.input, parser.pos + 1) == 41 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- sequence with 4 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "!" +if byte(parser.input, parser.pos + 1) == 33 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + -- match single character "=" +if byte(parser.input, parser.pos + 1) == 61 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + -- match literal "" +if sub(parser.input, parser.pos + 1, parser.pos + 0) == "" then + parser.pos = parser.pos + 0 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["FnArgsDef"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- choice with 2 alternatives + do -- sequence with 8 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "(" +if byte(parser.input, parser.pos + 1) == 40 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["White"](parser) +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + rules["FnArgDefList"](parser) + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if parser.success then +do -- choice with 2 alternatives + do -- sequence with 4 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match literal "using" +if sub(parser.input, parser.pos + 1, parser.pos + 5) == "using" then + parser.pos = parser.pos + 5 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- choice with 2 alternatives + rules["NameList"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + do -- sequence with 2 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match literal "nil" +if sub(parser.input, parser.pos + 1, parser.pos + 3) == "nil" then + parser.pos = parser.pos + 3 +else + parser.success = false + record_furthest(parser) + +end +end + if not parser.success then + parser.pos = pp_pos + end +end +end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + -- match literal "" +if sub(parser.input, parser.pos + 1, parser.pos + 0) == "" then + parser.pos = parser.pos + 0 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +end +end +if parser.success then +rules["White"](parser) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character ")" +if byte(parser.input, parser.pos + 1) == 41 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + -- match literal "" +if sub(parser.input, parser.pos + 1, parser.pos + 0) == "" then + parser.pos = parser.pos + 0 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + -- match literal "" +if sub(parser.input, parser.pos + 1, parser.pos + 0) == "" then + parser.pos = parser.pos + 0 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["FnArgsExpList"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Exp"](parser) +if parser.success then +do -- zero or more repetitions + while true do + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- choice with 2 alternatives + rules["Break"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + do -- sequence with 2 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos + end +end +end +end +if parser.success then +rules["White"](parser) +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["For"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 12 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "for", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "for" +if sub(parser.input, parser.pos + 1, parser.pos + 3) == "for" then + parser.pos = parser.pos + 3 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +-- indenter cpush (stack 1): push constant 0 +ind_push(parser, 2, 0) +end +if parser.success then +rules["Name"](parser) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character "=" +if byte(parser.input, parser.pos + 1) == 61 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 5 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Exp"](parser) +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["Exp"](parser) +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if parser.success then +-- indenter pop (stack 1) +if not ind_pop(parser, 2) then + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 3 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match literal "do" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "do" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end +if parser.success then +rules["Body"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["ForEach"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 13 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "foreach", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "for" +if sub(parser.input, parser.pos + 1, parser.pos + 3) == "for" then + parser.pos = parser.pos + 3 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + rules["AssignableNameList"](parser) + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "in" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "in" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +-- indenter cpush (stack 1): push constant 0 +ind_push(parser, 2, 0) +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- choice with 2 alternatives + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "*" +if byte(parser.input, parser.pos + 1) == 42 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "unpack", 0, 0) +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +if not parser.success and not parser.throw_label then + parser.success = true + rules["ExpList"](parser) +end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if parser.success then +-- indenter pop (stack 1) +if not ind_pop(parser, 2) then + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 3 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match literal "do" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "do" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end +if parser.success then +rules["Body"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["FunLit"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 4 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "fndef", 0, 0) +end +if parser.success then +rules["FnArgsDef"](parser) +end +if parser.success then +do -- choice with 2 alternatives + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match literal "->" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "->" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "slim", 0, 0) +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match literal "=>" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "=>" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "fat", 0, 0) +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +end +end +if parser.success then +do -- choice with 2 alternatives + rules["Body"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + -- match literal "" +if sub(parser.input, parser.pos + 1, parser.pos + 0) == "" then + parser.pos = parser.pos + 0 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["If"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 9 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "if", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "if" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "if" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +rules["IfCond"](parser) +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 3 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match literal "then" +if sub(parser.input, parser.pos + 1, parser.pos + 4) == "then" then + parser.pos = parser.pos + 4 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end +if parser.success then +rules["Body"](parser) +end +if parser.success then +do -- zero or more repetitions + while true do + rules["IfElseIf"](parser) + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + rules["IfElse"](parser) + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["IfCond"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- transform capture (Cfn id=0) + local fn_cap_start = parser.cap_n + cap_push(parser, CAP_FN_OPEN, cmt_fns[0], parser.pos, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Exp"](parser) +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + rules["Assign"](parser) + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_FN_CLOSE, nil, parser.pos, 0) + else + parser.cap_n = fn_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["IfElse"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 6 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "else", 0, 0) +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Break"](parser) +if parser.success then +do -- zero or more repetitions + while true do + rules["EmptyLine"](parser) + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end +if parser.success then +rules["CheckIndent"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "else" +if sub(parser.input, parser.pos + 1, parser.pos + 4) == "else" then + parser.pos = parser.pos + 4 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +rules["Body"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["IfElseIf"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 8 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "elseif", 0, 0) +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Break"](parser) +if parser.success then +do -- zero or more repetitions + while true do + rules["EmptyLine"](parser) + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end +if parser.success then +rules["CheckIndent"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "elseif" +if sub(parser.input, parser.pos + 1, parser.pos + 6) == "elseif" then + parser.pos = parser.pos + 6 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +do -- transform capture (Cfn id=2) + local fn_cap_start = parser.cap_n + cap_push(parser, CAP_FN_OPEN, cmt_fns[2], parser.pos, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + cap_push(parser, CAP_POS, nil, parser.pos, 0) -- position capture +if parser.success then +rules["IfCond"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_FN_CLOSE, nil, parser.pos, 0) + else + parser.cap_n = fn_cap_start + end +end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 3 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match literal "then" +if sub(parser.input, parser.pos + 1, parser.pos + 4) == "then" then + parser.pos = parser.pos + 4 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end +if parser.success then +rules["Body"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Import"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 10 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "import", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "import" +if sub(parser.input, parser.pos + 1, parser.pos + 6) == "import" then + parser.pos = parser.pos + 6 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + rules["ImportNameList"](parser) + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if parser.success then +do -- zero or more repetitions + while true do + rules["SpaceBreak"](parser) + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "from" +if sub(parser.input, parser.pos + 1, parser.pos + 4) == "from" then + parser.pos = parser.pos + 4 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["ImportName"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- choice with 2 alternatives + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "\\" +if byte(parser.input, parser.pos + 1) == 92 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "colon", 0, 0) +end +if parser.success then +rules["Name"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +if not parser.success and not parser.throw_label then + parser.success = true + rules["Name"](parser) +end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["ImportNameList"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- zero or more repetitions + while true do + rules["SpaceBreak"](parser) + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +if parser.success then +rules["ImportName"](parser) +end +if parser.success then +do -- zero or more repetitions + while true do + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- choice with 2 alternatives + do -- at least 1 repetitions + local pp_pos = parser.pos + local rep_count = 0 + while true do + rules["SpaceBreak"](parser) + if not parser.success then + break + end + rep_count = rep_count + 1 + end + if parser.throw_label then + -- Keep failure state, propagate labeled failure + elseif rep_count >= 1 then + parser.success = true + else + parser.pos = pp_pos + + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- sequence with 3 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +do -- zero or more repetitions + while true do + rules["SpaceBreak"](parser) + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end +end +end +if parser.success then +rules["ImportName"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["InBlock"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Advance"](parser) +if parser.success then +rules["Block"](parser) +end +if parser.success then +rules["PopIndent"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Invoke"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- FIRST-byte dispatched ordered choice + local dd = disp[4] + local db = byte(parser.input, parser.pos + 1) + local dm = db and dd.bytes[db] or dd.eof + parser.success = false + if not parser.success and not parser.throw_label and dm[1] then + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "call", 0, 0) +end +if parser.success then +rules["FnArgs"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if not parser.success and not parser.throw_label and dm[2] then + if dm.s[2] then record_furthest(parser) end + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "call", 0, 0) +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + rules["SingleString"](parser) + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if not parser.success and not parser.throw_label and dm[3] then + if dm.s[3] then record_furthest(parser) end + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "call", 0, 0) +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + rules["DoubleString"](parser) + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if not parser.success and not parser.throw_label and dm[4] then + if dm.s[4] then record_furthest(parser) end + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- lookahead (match without consuming input) + local pp_pos = parser.pos + -- match single character "[" +if byte(parser.input, parser.pos + 1) == 91 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end + if parser.success then + parser.pos = pp_pos + end +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "call", 0, 0) +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + rules["LuaString"](parser) + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end + if not parser.success and not parser.throw_label then + record_furthest(parser) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["InvokeArgs"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- negate (only match if pattern fails) + local pp_pos = parser.pos + -- match single character "-" +if byte(parser.input, parser.pos + 1) == 45 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +if parser.success then +do -- choice with 2 alternatives + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["ExpList"](parser) +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- choice with 2 alternatives + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +do -- choice with 2 alternatives + rules["TableBlock"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + do -- sequence with 4 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["SpaceBreak"](parser) +if parser.success then +rules["Advance"](parser) +end +if parser.success then +rules["ArgBlock"](parser) +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + rules["TableBlock"](parser) + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +if not parser.success and not parser.throw_label then + parser.success = true + rules["TableBlock"](parser) +end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +if not parser.success and not parser.throw_label then + parser.success = true + rules["TableBlock"](parser) +end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["KeyName"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- choice with 2 alternatives + rules["SelfName"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "key_literal", 0, 0) +end +if parser.success then +rules["NameRaw"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["KeyValue"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- choice with 2 alternatives + do -- transform capture (Cfn id=4) + local fn_cap_start = parser.cap_n + cap_push(parser, CAP_FN_OPEN, cmt_fns[4], parser.pos, 0) + do -- sequence with 5 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character ":" +if byte(parser.input, parser.pos + 1) == 58 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + rules["SomeSpace"](parser) + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +rules["Name"](parser) +end +if parser.success then +cap_push(parser, CAP_POS, nil, parser.pos, 0) -- position capture +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_FN_CLOSE, nil, parser.pos, 0) + else + parser.cap_n = fn_cap_start + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- FIRST-byte dispatched ordered choice + local dd = disp[5] + local db = byte(parser.input, parser.pos + 1) + local dm = db and dd.bytes[db] or dd.eof + parser.success = false + if not parser.success and not parser.throw_label and dm[1] then + parser.success = true + rules["KeyName"](parser) +end +if not parser.success and not parser.throw_label and dm[2] then + if dm.s[2] then record_furthest(parser) end + parser.success = true + do -- sequence with 5 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "[" +if byte(parser.input, parser.pos + 1) == 91 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["Exp"](parser) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character "]" +if byte(parser.input, parser.pos + 1) == 93 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +if not parser.success and not parser.throw_label and dm[3] then + if dm.s[3] then record_furthest(parser) end + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +rules["DoubleString"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +if not parser.success and not parser.throw_label and dm[4] then + if dm.s[4] then record_furthest(parser) end + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +rules["SingleString"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end + if not parser.success and not parser.throw_label then + record_furthest(parser) + end +end +if parser.success then +-- match single character ":" +if byte(parser.input, parser.pos + 1) == 58 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +do -- choice with 3 alternatives + rules["Exp"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + rules["TableBlock"](parser) +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- at least 1 repetitions + local pp_pos = parser.pos + local rep_count = 0 + while true do + rules["SpaceBreak"](parser) + if not parser.success then + break + end + rep_count = rep_count + 1 + end + if parser.throw_label then + -- Keep failure state, propagate labeled failure + elseif rep_count >= 1 then + parser.success = true + else + parser.pos = pp_pos + + end +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["KeyValueLine"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["CheckIndent"](parser) +if parser.success then +rules["KeyValueList"](parser) +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 2 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["KeyValueList"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["KeyValue"](parser) +if parser.success then +do -- zero or more repetitions + while true do + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["KeyValue"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Line"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- choice with 2 alternatives + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["CheckIndent"](parser) +if parser.success then +rules["Statement"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- sequence with 2 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +do -- lookahead (match without consuming input) + local pp_pos = parser.pos + rules["Stop"](parser) + if parser.success then + parser.pos = pp_pos + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end +end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Local"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 4 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match literal "local" +if sub(parser.input, parser.pos + 1, parser.pos + 5) == "local" then + parser.pos = parser.pos + 5 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +do -- choice with 2 alternatives + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "declare_glob", 0, 0) +end +if parser.success then +do -- choice with 2 alternatives + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- capture + local cap_start_pos = parser.pos + -- match single character "*" +if byte(parser.input, parser.pos + 1) == 42 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- capture + local cap_start_pos = parser.pos + -- match single character "^" +if byte(parser.input, parser.pos + 1) == 94 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "declare_with_shadows", 0, 0) +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + rules["NameList"](parser) + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["LuaString"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- transform capture (Cfn id=7) + local fn_cap_start = parser.cap_n + cap_push(parser, CAP_FN_OPEN, cmt_fns[7], parser.pos, 0) + do -- sequence with 9 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "[" +if byte(parser.input, parser.pos + 1) == 91 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +cap_push(parser, CAP_POS, nil, parser.pos, 0) -- position capture +end +if parser.success then +do -- capture group "lua_eq" + local cg_cap_start = parser.cap_n + cap_push(parser, CAP_GROUP_OPEN, "lua_eq", parser.pos, 0) + do -- zero or more repetitions + while true do + -- match single character "=" +if byte(parser.input, parser.pos + 1) == 61 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end + if parser.success then + cap_push(parser, CAP_GROUP_CLOSE, "lua_eq", parser.pos, 0) + else + parser.cap_n = cg_cap_start + end +end +end +if parser.success then +cap_push(parser, CAP_POS, nil, parser.pos, 0) -- position capture +end +if parser.success then +-- match single character "[" +if byte(parser.input, parser.pos + 1) == 91 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + rules["Break"](parser) + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end +if parser.success then +do -- capture + local cap_start_pos = parser.pos + do -- zero or more repetitions + while true do + do -- sequence with 2 patterns + local pp_pos = parser.pos + do -- negate (only match if pattern fails) + local pp_pos = parser.pos + rules["LuaStringClose"](parser) + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +if parser.success then +-- match any 1 characters +if parser.pos + 1 <= parser.input_len then + parser.pos = parser.pos + 1 +else + parser.success = false + record_furthest(parser) + +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end +if parser.success then +rules["LuaStringClose"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_FN_CLOSE, nil, parser.pos, 0) + else + parser.cap_n = fn_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["LuaStringClose"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 3 patterns + local pp_pos = parser.pos + -- match single character "]" +if byte(parser.input, parser.pos + 1) == 93 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +if parser.success then +-- capture match back "lua_eq" +parser.success = cap_match_back(parser, "lua_eq") +if not parser.success then + record_furthest(parser) + +end +end +if parser.success then +-- match single character "]" +if byte(parser.input, parser.pos + 1) == 93 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Name"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- sequence with 2 patterns + local pp_pos = parser.pos + do -- trie match for: "continue", "extends", "elseif", "export", "import", "return", "switch", "unless", "break", "class", "local", "using", "while", "else", "from", "then", "when", "with", "and", "for", "not", "do", "if", "in", "or" + local trie_pos = parser.pos + local last_terminal_pos = 0 + local has_terminal = false + local tb = byte(parser.input, parser.pos + 1) +if tb == 97 then -- "a" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 110 then -- "n" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 100 then -- "d" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 98 then -- "b" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 114 then -- "r" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 101 then -- "e" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 97 then -- "a" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 107 then -- "k" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 99 then -- "c" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 108 then -- "l" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 97 then -- "a" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 115 then -- "s" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 115 then -- "s" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 111 then -- "o" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 110 then -- "n" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 116 then -- "t" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 105 then -- "i" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 110 then -- "n" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 117 then -- "u" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 101 then -- "e" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 100 then -- "d" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 111 then -- "o" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 101 then -- "e" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 108 then -- "l" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 115 then -- "s" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 101 then -- "e" + parser.pos = parser.pos + 1 +if not has_terminal or parser.pos > last_terminal_pos then + last_terminal_pos = parser.pos + has_terminal = true +end +local tb = byte(parser.input, parser.pos + 1) +if tb == 105 then -- "i" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 102 then -- "f" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +if not parser.success then + -- partial match is valid: "else" + parser.success = true +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 120 then -- "x" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 112 then -- "p" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 111 then -- "o" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 114 then -- "r" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 116 then -- "t" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 116 then -- "t" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 101 then -- "e" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 110 then -- "n" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 100 then -- "d" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 115 then -- "s" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 102 then -- "f" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 111 then -- "o" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 114 then -- "r" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 114 then -- "r" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 111 then -- "o" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 109 then -- "m" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 105 then -- "i" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 102 then -- "f" + parser.pos = parser.pos + 1 +elseif tb == 109 then -- "m" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 112 then -- "p" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 111 then -- "o" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 114 then -- "r" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 116 then -- "t" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 110 then -- "n" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 108 then -- "l" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 111 then -- "o" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 99 then -- "c" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 97 then -- "a" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 108 then -- "l" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 110 then -- "n" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 111 then -- "o" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 116 then -- "t" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 111 then -- "o" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 114 then -- "r" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 114 then -- "r" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 101 then -- "e" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 116 then -- "t" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 117 then -- "u" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 114 then -- "r" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 110 then -- "n" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 115 then -- "s" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 119 then -- "w" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 105 then -- "i" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 116 then -- "t" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 99 then -- "c" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 104 then -- "h" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 116 then -- "t" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 104 then -- "h" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 101 then -- "e" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 110 then -- "n" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 117 then -- "u" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 110 then -- "n" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 108 then -- "l" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 101 then -- "e" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 115 then -- "s" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 115 then -- "s" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 115 then -- "s" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 105 then -- "i" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 110 then -- "n" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 103 then -- "g" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 119 then -- "w" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 104 then -- "h" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 101 then -- "e" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 110 then -- "n" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 105 then -- "i" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 108 then -- "l" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 101 then -- "e" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 105 then -- "i" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 116 then -- "t" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 104 then -- "h" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end + if not parser.success then + parser.pos = trie_pos + end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +rules["NameRaw"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["NameList"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Name"](parser) +if parser.success then +do -- zero or more repetitions + while true do + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["Name"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["NameOrDestructure"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- choice with 2 alternatives + rules["Name"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + rules["TableLit"](parser) +end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["NameRaw"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture + local cap_start_pos = parser.pos + do -- sequence with 2 patterns + local pp_pos = parser.pos + do -- match character range "az,AZ,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end +if parser.success then +do -- zero or more repetitions + while true do + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Num"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "number", 0, 0) +end +if parser.success then +do -- capture + local cap_start_pos = parser.pos + do -- choice with 3 alternatives + do -- sequence with 3 patterns + local pp_pos = parser.pos + -- match literal "0x" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "0x" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end +if parser.success then +do -- at least 1 repetitions + local pp_pos = parser.pos + local rep_count = 0 + while true do + do -- match character range "09,af,AF" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 48 and rb <= 57) or (rb >= 97 and rb <= 102) or (rb >= 65 and rb <= 70)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if not parser.success then + break + end + rep_count = rep_count + 1 + end + if parser.throw_label then + -- Keep failure state, propagate labeled failure + elseif rep_count >= 1 then + parser.success = true + else + parser.pos = pp_pos + + end +end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 2 patterns + local pp_pos = parser.pos + do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- match character set "uU" + local sb = byte(parser.input, parser.pos + 1) + if sb and sets[4][sb] then + parser.pos = parser.pos + 1 + else + parser.success = false + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +if parser.success then +do -- at least 2 repetitions + local pp_pos = parser.pos + local rep_count = 0 + while true do + do -- match character set "lL" + local sb = byte(parser.input, parser.pos + 1) + if sb and sets[5][sb] then + parser.pos = parser.pos + 1 + else + parser.success = false + end +end + if not parser.success then + break + end + rep_count = rep_count + 1 + end + if parser.throw_label then + -- Keep failure state, propagate labeled failure + elseif rep_count >= 2 then + parser.success = true + else + parser.pos = pp_pos + + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- sequence with 3 patterns + local pp_pos = parser.pos + do -- at least 1 repetitions + local pp_pos = parser.pos + local rep_count = 0 + while true do + do -- match character range "09" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 48 and rb <= 57)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if not parser.success then + break + end + rep_count = rep_count + 1 + end + if parser.throw_label then + -- Keep failure state, propagate labeled failure + elseif rep_count >= 1 then + parser.success = true + else + parser.pos = pp_pos + + end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- match character set "uU" + local sb = byte(parser.input, parser.pos + 1) + if sb and sets[4][sb] then + parser.pos = parser.pos + 1 + else + parser.success = false + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end +if parser.success then +do -- at least 2 repetitions + local pp_pos = parser.pos + local rep_count = 0 + while true do + do -- match character set "lL" + local sb = byte(parser.input, parser.pos + 1) + if sb and sets[5][sb] then + parser.pos = parser.pos + 1 + else + parser.success = false + end +end + if not parser.success then + break + end + rep_count = rep_count + 1 + end + if parser.throw_label then + -- Keep failure state, propagate labeled failure + elseif rep_count >= 2 then + parser.success = true + else + parser.pos = pp_pos + + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- sequence with 2 patterns + local pp_pos = parser.pos + do -- choice with 2 alternatives + do -- sequence with 2 patterns + local pp_pos = parser.pos + do -- at least 1 repetitions + local pp_pos = parser.pos + local rep_count = 0 + while true do + do -- match character range "09" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 48 and rb <= 57)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if not parser.success then + break + end + rep_count = rep_count + 1 + end + if parser.throw_label then + -- Keep failure state, propagate labeled failure + elseif rep_count >= 1 then + parser.success = true + else + parser.pos = pp_pos + + end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 2 patterns + local pp_pos = parser.pos + -- match single character "." +if byte(parser.input, parser.pos + 1) == 46 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +if parser.success then +do -- at least 1 repetitions + local pp_pos = parser.pos + local rep_count = 0 + while true do + do -- match character range "09" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 48 and rb <= 57)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if not parser.success then + break + end + rep_count = rep_count + 1 + end + if parser.throw_label then + -- Keep failure state, propagate labeled failure + elseif rep_count >= 1 then + parser.success = true + else + parser.pos = pp_pos + + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- sequence with 2 patterns + local pp_pos = parser.pos + -- match single character "." +if byte(parser.input, parser.pos + 1) == 46 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +if parser.success then +do -- at least 1 repetitions + local pp_pos = parser.pos + local rep_count = 0 + while true do + do -- match character range "09" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 48 and rb <= 57)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if not parser.success then + break + end + rep_count = rep_count + 1 + end + if parser.throw_label then + -- Keep failure state, propagate labeled failure + elseif rep_count >= 1 then + parser.success = true + else + parser.pos = pp_pos + + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end +end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 3 patterns + local pp_pos = parser.pos + do -- match character set "eE" + local sb = byte(parser.input, parser.pos + 1) + if sb and sets[6][sb] then + parser.pos = parser.pos + 1 + else + parser.success = false + end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + -- match single character "-" +if byte(parser.input, parser.pos + 1) == 45 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end +if parser.success then +do -- at least 1 repetitions + local pp_pos = parser.pos + local rep_count = 0 + while true do + do -- match character range "09" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 48 and rb <= 57)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if not parser.success then + break + end + rep_count = rep_count + 1 + end + if parser.throw_label then + -- Keep failure state, propagate labeled failure + elseif rep_count >= 1 then + parser.success = true + else + parser.pos = pp_pos + + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end +end +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Parens"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 7 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "(" +if byte(parser.input, parser.pos + 1) == 40 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +do -- zero or more repetitions + while true do + rules["SpaceBreak"](parser) + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end +if parser.success then +rules["Exp"](parser) +end +if parser.success then +do -- zero or more repetitions + while true do + rules["SpaceBreak"](parser) + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character ")" +if byte(parser.input, parser.pos + 1) == 41 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["PopIndent"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + -- indenter pop (stack 0) +if not ind_pop(parser, 1) then + parser.success = false + record_furthest(parser) + +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["PreventIndent"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + -- indenter prevent (stack 0): push sentinel so nested advance fails +ind_push(parser, 1, IND_PREVENT) + + parser.depth = depth - 1 + return parser.success +end + + +rules["PushIndent"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- indenter push (stack 0): consume whitespace, push measured width + local ind_width, ind_end = ind_measure(parser, 4) + ind_push(parser, 1, ind_width) + parser.pos = ind_end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Return"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 5 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "return", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "return" +if sub(parser.input, parser.pos + 1, parser.pos + 6) == "return" then + parser.pos = parser.pos + 6 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +do -- choice with 2 alternatives + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "explist", 0, 0) +end +if parser.success then +rules["ExpListLow"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- capture + local cap_start_pos = parser.pos + -- match literal "" +if sub(parser.input, parser.pos + 1, parser.pos + 0) == "" then + parser.pos = parser.pos + 0 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["SelfName"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "@" +if byte(parser.input, parser.pos + 1) == 64 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +do -- choice with 3 alternatives + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + -- match single character "@" +if byte(parser.input, parser.pos + 1) == 64 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +if parser.success then +do -- choice with 2 alternatives + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "self_class", 0, 0) +end +if parser.success then +rules["NameRaw"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "self.__class", 0, 0) +end +end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "self", 0, 0) +end +if parser.success then +rules["NameRaw"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "self", 0, 0) +end +end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Shebang"] = function(parser) + local start = parser.pos + -- Position-pure rule: a single-slot memo short-circuits the repeated + -- calls that backtracking alternatives make at the same position + if parser.memo_pos[5] == start + 1 then + local memo_end = parser.memo_end[5] + if memo_end == -1 then + parser.success = false + return false + end + parser.pos = memo_end + parser.success = true + return true + end +local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 2 patterns + local pp_pos = parser.pos + -- match literal "#!" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "#!" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end +if parser.success then +do -- zero or more repetitions + while true do + do -- sequence with 2 patterns + local pp_pos = parser.pos + do -- negate (only match if pattern fails) + local pp_pos = parser.pos + rules["Stop"](parser) + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +if parser.success then +-- match any 1 characters +if parser.pos + 1 <= parser.input_len then + parser.pos = parser.pos + 1 +else + parser.success = false + record_furthest(parser) + +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end + parser.memo_pos[5] = start + 1 + parser.memo_end[5] = parser.success and parser.pos or -1 + + parser.depth = depth - 1 + return parser.success +end + + +rules["SimpleValue"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- FIRST-byte dispatched ordered choice + local dd = disp[6] + local db = byte(parser.input, parser.pos + 1) + local dm = db and dd.bytes[db] or dd.eof + parser.success = false + if not parser.success and not parser.throw_label and dm[1] then + parser.success = true + rules["If"](parser) +end +if not parser.success and not parser.throw_label and dm[2] then + if dm.s[2] then record_furthest(parser) end + parser.success = true + rules["Unless"](parser) +end +if not parser.success and not parser.throw_label and dm[3] then + if dm.s[3] then record_furthest(parser) end + parser.success = true + rules["Switch"](parser) +end +if not parser.success and not parser.throw_label and dm[4] then + if dm.s[4] then record_furthest(parser) end + parser.success = true + rules["With"](parser) +end +if not parser.success and not parser.throw_label and dm[5] then + if dm.s[5] then record_furthest(parser) end + parser.success = true + rules["ClassDecl"](parser) +end +if not parser.success and not parser.throw_label and dm[6] then + if dm.s[6] then record_furthest(parser) end + parser.success = true + rules["ForEach"](parser) +end +if not parser.success and not parser.throw_label and dm[7] then + if dm.s[7] then record_furthest(parser) end + parser.success = true + rules["For"](parser) +end +if not parser.success and not parser.throw_label and dm[8] then + if dm.s[8] then record_furthest(parser) end + parser.success = true + rules["While"](parser) +end +if not parser.success and not parser.throw_label and dm[9] then + if dm.s[9] then record_furthest(parser) end + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- indenter ctop (stack 1): top ne 0 + local ind_s = parser.ind_stacks[2] + if not (ind_s.n > 0 and ind_s[ind_s.n] ~= 0) then + parser.success = false + record_furthest(parser) + + end +end +if parser.success then +rules["Do"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +if not parser.success and not parser.throw_label and dm[10] then + if dm.s[10] then record_furthest(parser) end + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 5 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "minus", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character "-" +if byte(parser.input, parser.pos + 1) == 45 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + rules["SomeSpace"](parser) + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if not parser.success and not parser.throw_label and dm[11] then + if dm.s[11] then record_furthest(parser) end + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 4 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "length", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character "#" +if byte(parser.input, parser.pos + 1) == 35 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if not parser.success and not parser.throw_label and dm[12] then + if dm.s[12] then record_furthest(parser) end + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 4 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "bitnot", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character "~" +if byte(parser.input, parser.pos + 1) == 126 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if not parser.success and not parser.throw_label and dm[13] then + if dm.s[13] then record_furthest(parser) end + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 5 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "not", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "not" +if sub(parser.input, parser.pos + 1, parser.pos + 3) == "not" then + parser.pos = parser.pos + 3 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if not parser.success and not parser.throw_label and dm[14] then + if dm.s[14] then record_furthest(parser) end + parser.success = true + rules["TblComprehension"](parser) +end +if not parser.success and not parser.throw_label and dm[15] then + if dm.s[15] then record_furthest(parser) end + parser.success = true + rules["TableLit"](parser) +end +if not parser.success and not parser.throw_label and dm[16] then + if dm.s[16] then record_furthest(parser) end + parser.success = true + rules["Comprehension"](parser) +end +if not parser.success and not parser.throw_label and dm[17] then + if dm.s[17] then record_furthest(parser) end + parser.success = true + rules["FunLit"](parser) +end +if not parser.success and not parser.throw_label and dm[18] then + if dm.s[18] then record_furthest(parser) end + parser.success = true + rules["Num"](parser) +end + if not parser.success and not parser.throw_label then + record_furthest(parser) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["SingleString"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 4 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "string", 0, 0) +end +if parser.success then +do -- capture + local cap_start_pos = parser.pos + -- match single character "'" +if byte(parser.input, parser.pos + 1) == 39 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end +if parser.success then +do -- capture + local cap_start_pos = parser.pos + do -- zero or more repetitions + while true do + do -- choice with 3 alternatives + -- match literal "\\'" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "\\'" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end +if not parser.success and not parser.throw_label then + parser.success = true + -- match literal "\\\\" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "\\\\" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- sequence with 2 patterns + local pp_pos = parser.pos + do -- negate (only match if pattern fails) + local pp_pos = parser.pos + -- match single character "'" +if byte(parser.input, parser.pos + 1) == 39 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +if parser.success then +-- match any 1 characters +if parser.pos + 1 <= parser.input_len then + parser.pos = parser.pos + 1 +else + parser.success = false + record_furthest(parser) + +end +end + if not parser.success then + parser.pos = pp_pos + end +end +end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end +if parser.success then +-- match single character "'" +if byte(parser.input, parser.pos + 1) == 39 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Slice"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 9 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "slice", 0, 0) +end +if parser.success then +-- match single character "[" +if byte(parser.input, parser.pos + 1) == 91 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +do -- choice with 2 alternatives + rules["SliceValue"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, 1, 0, 0) +end +end +end +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +do -- choice with 2 alternatives + rules["SliceValue"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "", 0, 0) +end +end +end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["SliceValue"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character "]" +if byte(parser.input, parser.pos + 1) == 93 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["SliceValue"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + rules["Exp"](parser) + + parser.depth = depth - 1 + return parser.success +end + + +rules["SomeSpace"] = function(parser) + local start = parser.pos + -- Position-pure rule: a single-slot memo short-circuits the repeated + -- calls that backtracking alternatives make at the same position + if parser.memo_pos[6] == start + 1 then + local memo_end = parser.memo_end[6] + if memo_end == -1 then + parser.success = false + return false + end + parser.pos = memo_end + parser.success = true + return true + end +local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 2 patterns + local pp_pos = parser.pos + do -- at least 1 repetitions + local pp_pos = parser.pos + local rep_count = 0 + while true do + do -- match character set " \t" + local sb = byte(parser.input, parser.pos + 1) + if sb and sets[7][sb] then + parser.pos = parser.pos + 1 + else + parser.success = false + end +end + if not parser.success then + break + end + rep_count = rep_count + 1 + end + if parser.throw_label then + -- Keep failure state, propagate labeled failure + elseif rep_count >= 1 then + parser.success = true + else + parser.pos = pp_pos + + end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + rules["Comment"](parser) + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end + parser.memo_pos[6] = start + 1 + parser.memo_end[6] = parser.success and parser.pos or -1 + + parser.depth = depth - 1 + return parser.success +end + + +rules["Space"] = function(parser) + local start = parser.pos + -- Position-pure rule: a single-slot memo short-circuits the repeated + -- calls that backtracking alternatives make at the same position + if parser.memo_pos[7] == start + 1 then + local memo_end = parser.memo_end[7] + if memo_end == -1 then + parser.success = false + return false + end + parser.pos = memo_end + parser.success = true + return true + end +local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 2 patterns + local pp_pos = parser.pos + do -- zero or more repetitions + while true do + do -- match character set " \t" + local sb = byte(parser.input, parser.pos + 1) + if sb and sets[7][sb] then + parser.pos = parser.pos + 1 + else + parser.success = false + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + rules["Comment"](parser) + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end + parser.memo_pos[7] = start + 1 + parser.memo_end[7] = parser.success and parser.pos or -1 + + parser.depth = depth - 1 + return parser.success +end + + +rules["SpaceBreak"] = function(parser) + local start = parser.pos + -- Position-pure rule: a single-slot memo short-circuits the repeated + -- calls that backtracking alternatives make at the same position + if parser.memo_pos[8] == start + 1 then + local memo_end = parser.memo_end[8] + if memo_end == -1 then + parser.success = false + return false + end + parser.pos = memo_end + parser.success = true + return true + end +local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 2 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +rules["Break"](parser) +end + if not parser.success then + parser.pos = pp_pos + end +end + parser.memo_pos[8] = start + 1 + parser.memo_end[8] = parser.success and parser.pos or -1 + + parser.depth = depth - 1 + return parser.success +end + + +rules["Statement"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- transform capture (Cfn id=1) + local fn_cap_start = parser.cap_n + cap_push(parser, CAP_FN_OPEN, cmt_fns[1], parser.pos, 0) + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- transform capture (Cfn id=2) + local fn_cap_start = parser.cap_n + cap_push(parser, CAP_FN_OPEN, cmt_fns[2], parser.pos, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + cap_push(parser, CAP_POS, nil, parser.pos, 0) -- position capture +if parser.success then +do -- FIRST-byte dispatched ordered choice + local dd = disp[7] + local db = byte(parser.input, parser.pos + 1) + local dm = db and dd.bytes[db] or dd.eof + parser.success = false + if not parser.success and not parser.throw_label and dm[1] then + parser.success = true + rules["Import"](parser) +end +if not parser.success and not parser.throw_label and dm[2] then + if dm.s[2] then record_furthest(parser) end + parser.success = true + rules["While"](parser) +end +if not parser.success and not parser.throw_label and dm[3] then + if dm.s[3] then record_furthest(parser) end + parser.success = true + rules["With"](parser) +end +if not parser.success and not parser.throw_label and dm[4] then + if dm.s[4] then record_furthest(parser) end + parser.success = true + rules["For"](parser) +end +if not parser.success and not parser.throw_label and dm[5] then + if dm.s[5] then record_furthest(parser) end + parser.success = true + rules["ForEach"](parser) +end +if not parser.success and not parser.throw_label and dm[6] then + if dm.s[6] then record_furthest(parser) end + parser.success = true + rules["Switch"](parser) +end +if not parser.success and not parser.throw_label and dm[7] then + if dm.s[7] then record_furthest(parser) end + parser.success = true + rules["Return"](parser) +end +if not parser.success and not parser.throw_label and dm[8] then + if dm.s[8] then record_furthest(parser) end + parser.success = true + rules["Local"](parser) +end +if not parser.success and not parser.throw_label and dm[9] then + if dm.s[9] then record_furthest(parser) end + parser.success = true + rules["Export"](parser) +end +if not parser.success and not parser.throw_label and dm[10] then + if dm.s[10] then record_furthest(parser) end + parser.success = true + rules["BreakLoop"](parser) +end +if not parser.success and not parser.throw_label and dm[11] then + if dm.s[11] then record_furthest(parser) end + parser.success = true + do -- transform capture (Cfn id=3) + local fn_cap_start = parser.cap_n + cap_push(parser, CAP_FN_OPEN, cmt_fns[3], parser.pos, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + rules["ExpList"](parser) + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- choice with 2 alternatives + rules["Update"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + rules["Assign"](parser) +end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_FN_CLOSE, nil, parser.pos, 0) + else + parser.cap_n = fn_cap_start + end +end +end + if not parser.success and not parser.throw_label then + record_furthest(parser) + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_FN_CLOSE, nil, parser.pos, 0) + else + parser.cap_n = fn_cap_start + end +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- choice with 3 alternatives + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 7 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "if", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "if" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "if" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +rules["Exp"](parser) +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 4 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match literal "else" +if sub(parser.input, parser.pos + 1, parser.pos + 4) == "else" then + parser.pos = parser.pos + 4 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end +if parser.success then +rules["Space"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 5 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "unless", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "unless" +if sub(parser.input, parser.pos + 1, parser.pos + 6) == "unless" then + parser.pos = parser.pos + 6 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "comprehension", 0, 0) +end +if parser.success then +rules["CompInner"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +end +if parser.success then +rules["Space"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_FN_CLOSE, nil, parser.pos, 0) + else + parser.cap_n = fn_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Stop"] = function(parser) + local start = parser.pos + -- Position-pure rule: a single-slot memo short-circuits the repeated + -- calls that backtracking alternatives make at the same position + if parser.memo_pos[9] == start + 1 then + local memo_end = parser.memo_end[9] + if memo_end == -1 then + parser.success = false + return false + end + parser.pos = memo_end + parser.success = true + return true + end +local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- choice with 2 alternatives + rules["Break"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + do -- negate (only match if pattern fails) + local pp_pos = parser.pos + -- match any 1 characters +if parser.pos + 1 <= parser.input_len then + parser.pos = parser.pos + 1 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +end + parser.memo_pos[9] = start + 1 + parser.memo_end[9] = parser.success and parser.pos or -1 + + parser.depth = depth - 1 + return parser.success +end + + +rules["String"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- choice with 3 alternatives + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +rules["DoubleString"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +if not parser.success and not parser.throw_label then + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +rules["SingleString"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +if not parser.success and not parser.throw_label then + parser.success = true + rules["LuaString"](parser) +end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Switch"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 11 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "switch", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "switch" +if sub(parser.input, parser.pos + 1, parser.pos + 6) == "switch" then + parser.pos = parser.pos + 6 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +-- indenter cpush (stack 1): push constant 0 +ind_push(parser, 2, 0) +end +if parser.success then +rules["Exp"](parser) +end +if parser.success then +-- indenter pop (stack 1) +if not ind_pop(parser, 2) then + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 3 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match literal "do" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "do" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +rules["Break"](parser) +end +if parser.success then +rules["SwitchBlock"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["SwitchBlock"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 4 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- zero or more repetitions + while true do + rules["EmptyLine"](parser) + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +if parser.success then +rules["Advance"](parser) +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["SwitchCase"](parser) +if parser.success then +do -- zero or more repetitions + while true do + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- at least 1 repetitions + local pp_pos = parser.pos + local rep_count = 0 + while true do + rules["Break"](parser) + if not parser.success then + break + end + rep_count = rep_count + 1 + end + if parser.throw_label then + -- Keep failure state, propagate labeled failure + elseif rep_count >= 1 then + parser.success = true + else + parser.pos = pp_pos + + end +end +if parser.success then +rules["SwitchCase"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- at least 1 repetitions + local pp_pos = parser.pos + local rep_count = 0 + while true do + rules["Break"](parser) + if not parser.success then + break + end + rep_count = rep_count + 1 + end + if parser.throw_label then + -- Keep failure state, propagate labeled failure + elseif rep_count >= 1 then + parser.success = true + else + parser.pos = pp_pos + + end +end +if parser.success then +rules["SwitchElse"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if parser.success then +rules["PopIndent"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["SwitchCase"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 7 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "case", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "when" +if sub(parser.input, parser.pos + 1, parser.pos + 4) == "when" then + parser.pos = parser.pos + 4 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + rules["ExpList"](parser) + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 3 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match literal "then" +if sub(parser.input, parser.pos + 1, parser.pos + 4) == "then" then + parser.pos = parser.pos + 4 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end +if parser.success then +rules["Body"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["SwitchElse"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 5 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "else", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "else" +if sub(parser.input, parser.pos + 1, parser.pos + 4) == "else" then + parser.pos = parser.pos + 4 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +rules["Body"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["TableBlock"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 5 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "table", 0, 0) +end +if parser.success then +do -- at least 1 repetitions + local pp_pos = parser.pos + local rep_count = 0 + while true do + rules["SpaceBreak"](parser) + if not parser.success then + break + end + rep_count = rep_count + 1 + end + if parser.throw_label then + -- Keep failure state, propagate labeled failure + elseif rep_count >= 1 then + parser.success = true + else + parser.pos = pp_pos + + end +end +end +if parser.success then +rules["Advance"](parser) +end +if parser.success then +rules["TableBlockInner"](parser) +end +if parser.success then +rules["PopIndent"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["TableBlockInner"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["KeyValueLine"](parser) +if parser.success then +do -- zero or more repetitions + while true do + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- at least 1 repetitions + local pp_pos = parser.pos + local rep_count = 0 + while true do + rules["SpaceBreak"](parser) + if not parser.success then + break + end + rep_count = rep_count + 1 + end + if parser.throw_label then + -- Keep failure state, propagate labeled failure + elseif rep_count >= 1 then + parser.success = true + else + parser.pos = pp_pos + + end +end +if parser.success then +rules["KeyValueLine"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["TableLit"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 7 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "table", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character "{" +if byte(parser.input, parser.pos + 1) == 123 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + rules["TableValueList"](parser) + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 2 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 4 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["SpaceBreak"](parser) +if parser.success then +rules["TableLitLine"](parser) +end +if parser.success then +do -- zero or more repetitions + while true do + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 2 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +if parser.success then +rules["SpaceBreak"](parser) +end +if parser.success then +rules["TableLitLine"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 2 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if parser.success then +rules["White"](parser) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character "}" +if byte(parser.input, parser.pos + 1) == 125 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["TableLitLine"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- choice with 2 alternatives + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["PushIndent"](parser) +if parser.success then +rules["TableValueList"](parser) +end +if parser.success then +rules["PopIndent"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +if not parser.success and not parser.throw_label then + parser.success = true + rules["Space"](parser) +end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["TableValue"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- choice with 2 alternatives + rules["KeyValue"](parser) +if not parser.success and not parser.throw_label then + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + rules["Exp"](parser) + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["TableValueList"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["TableValue"](parser) +if parser.success then +do -- zero or more repetitions + while true do + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["TableValue"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["TblComprehension"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 7 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "tblcomprehension", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character "{" +if byte(parser.input, parser.pos + 1) == 123 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Exp"](parser) +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +-- match single character "," +if byte(parser.input, parser.pos + 1) == 44 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if parser.success then +rules["CompInner"](parser) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match single character "}" +if byte(parser.input, parser.pos + 1) == 125 then + parser.pos = parser.pos + 1 +else + parser.success = false + +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Unless"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 9 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "unless", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "unless" +if sub(parser.input, parser.pos + 1, parser.pos + 6) == "unless" then + parser.pos = parser.pos + 6 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +rules["IfCond"](parser) +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 3 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match literal "then" +if sub(parser.input, parser.pos + 1, parser.pos + 4) == "then" then + parser.pos = parser.pos + 4 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end +if parser.success then +rules["Body"](parser) +end +if parser.success then +do -- zero or more repetitions + while true do + rules["IfElseIf"](parser) + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + rules["IfElse"](parser) + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Update"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 4 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "update", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +do -- capture + local cap_start_pos = parser.pos + do -- trie match for: "..=", "+=", "-=", "*=", "/=", "%=", "or=", "and=", "&=", "|=", ">>=", "<<=" + local trie_pos = parser.pos + local last_terminal_pos = 0 + local has_terminal = false + local tb = byte(parser.input, parser.pos + 1) +if tb == 37 then -- "%" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 61 then -- "=" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 38 then -- "&" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 61 then -- "=" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 42 then -- "*" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 61 then -- "=" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 43 then -- "+" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 61 then -- "=" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 45 then -- "-" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 61 then -- "=" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 46 then -- "." + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 46 then -- "." + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 61 then -- "=" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 47 then -- "/" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 61 then -- "=" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 60 then -- "<" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 60 then -- "<" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 61 then -- "=" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 62 then -- ">" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 62 then -- ">" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 61 then -- "=" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 97 then -- "a" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 110 then -- "n" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 100 then -- "d" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 61 then -- "=" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 111 then -- "o" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 114 then -- "r" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 61 then -- "=" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +elseif tb == 124 then -- "|" + parser.pos = parser.pos + 1 +local tb = byte(parser.input, parser.pos + 1) +if tb == 61 then -- "=" + parser.pos = parser.pos + 1 +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end +else + parser.success = false + if has_terminal then + parser.pos = last_terminal_pos + parser.success = true + else + record_furthest(parser) + end +end + if not parser.success then + parser.pos = trie_pos + end +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end +if parser.success then +rules["Exp"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["Value"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- transform capture (Cfn id=2) + local fn_cap_start = parser.cap_n + cap_push(parser, CAP_FN_OPEN, cmt_fns[2], parser.pos, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + cap_push(parser, CAP_POS, nil, parser.pos, 0) -- position capture +if parser.success then +do -- FIRST-byte dispatched ordered choice + local dd = disp[8] + local db = byte(parser.input, parser.pos + 1) + local dm = db and dd.bytes[db] or dd.eof + parser.success = false + if not parser.success and not parser.throw_label and dm[1] then + parser.success = true + rules["SimpleValue"](parser) +end +if not parser.success and not parser.throw_label and dm[2] then + if dm.s[2] then record_furthest(parser) end + parser.success = true + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "table", 0, 0) +end +if parser.success then +do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + rules["KeyValueList"](parser) + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +end +if not parser.success and not parser.throw_label and dm[3] then + if dm.s[3] then record_furthest(parser) end + parser.success = true + rules["ChainValue"](parser) +end +if not parser.success and not parser.throw_label and dm[4] then + if dm.s[4] then record_furthest(parser) end + parser.success = true + rules["String"](parser) +end + if not parser.success and not parser.throw_label then + record_furthest(parser) + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_FN_CLOSE, nil, parser.pos, 0) + else + parser.cap_n = fn_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["VarArg"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- capture + local cap_start_pos = parser.pos + -- match literal "..." +if sub(parser.input, parser.pos + 1, parser.pos + 3) == "..." then + parser.pos = parser.pos + 3 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["While"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 9 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "while", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "while" +if sub(parser.input, parser.pos + 1, parser.pos + 5) == "while" then + parser.pos = parser.pos + 5 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +-- indenter cpush (stack 1): push constant 0 +ind_push(parser, 2, 0) +end +if parser.success then +rules["Exp"](parser) +end +if parser.success then +-- indenter pop (stack 1) +if not ind_pop(parser, 2) then + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 3 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match literal "do" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "do" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end +if parser.success then +rules["Body"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["White"] = function(parser) + local start = parser.pos + -- Position-pure rule: a single-slot memo short-circuits the repeated + -- calls that backtracking alternatives make at the same position + if parser.memo_pos[10] == start + 1 then + local memo_end = parser.memo_end[10] + if memo_end == -1 then + parser.success = false + return false + end + parser.pos = memo_end + parser.success = true + return true + end +local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- zero or more repetitions + while true do + do -- match character set " \t\r\n" + local sb = byte(parser.input, parser.pos + 1) + if sb and sets[8][sb] then + parser.pos = parser.pos + 1 + else + parser.success = false + end +end + if not parser.success then + break + end + end + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end +end + parser.memo_pos[10] = start + 1 + parser.memo_end[10] = parser.success and parser.pos or -1 + + parser.depth = depth - 1 + return parser.success +end + + +rules["With"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + do -- sequence with 9 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- constant capture (1 values) + cap_push(parser, CAP_CONST, "with", 0, 0) +end +if parser.success then +rules["Space"](parser) +end +if parser.success then +-- match literal "with" +if sub(parser.input, parser.pos + 1, parser.pos + 4) == "with" then + parser.pos = parser.pos + 4 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end +if parser.success then +-- indenter cpush (stack 1): push constant 0 +ind_push(parser, 2, 0) +end +if parser.success then +rules["WithExp"](parser) +end +if parser.success then +-- indenter pop (stack 1) +if not ind_pop(parser, 2) then + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + do -- sequence with 3 patterns + local pp_pos = parser.pos + rules["Space"](parser) +if parser.success then +-- match literal "do" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "do" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end + if not parser.success then + parser.pos = pp_pos + end +end + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end +if parser.success then +rules["Body"](parser) +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["WithExp"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- transform capture (Cfn id=3) + local fn_cap_start = parser.cap_n + cap_push(parser, CAP_FN_OPEN, cmt_fns[3], parser.pos, 0) + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + do -- capture table + local ct_cap_start = parser.cap_n + cap_push(parser, CAP_TBL_OPEN, nil, 0, 0) + rules["ExpList"](parser) + if parser.success then + cap_push(parser, CAP_TBL_CLOSE, nil, 0, 0) + else + parser.cap_n = ct_cap_start + end +end +if parser.success then +do -- at most 1 repetitions + local rep_count = 0 + while rep_count < 1 do + local before_pos = parser.pos + rules["Assign"](parser) + if not parser.success or before_pos == parser.pos then + -- Break on failure or zero-width match + -- Only recover from ordinary failure, not labeled failure from T() + if not parser.throw_label then + parser.success = true + end + break + end + rep_count = rep_count + 1 + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end + if parser.success then + cap_push(parser, CAP_FN_CLOSE, nil, parser.pos, 0) + else + parser.cap_n = fn_cap_start + end +end + + parser.depth = depth - 1 + return parser.success +end + + +rules["WordOperators"] = function(parser) + local depth = parser.depth + 1 + parser.depth = depth + if depth > MAX_DEPTH then + -- A hard Lua error (rather than a match failure) so the overflow can't + -- be silently converted into a successful parse by a predicate or choice + error("pgen: max recursion depth (" .. MAX_DEPTH .. ") exceeded at position " .. (parser.pos + 1)) + end + + do -- FIRST-byte dispatched ordered choice + local dd = disp[9] + local db = byte(parser.input, parser.pos + 1) + local dm = db and dd.bytes[db] or dd.eof + parser.success = false + if not parser.success and not parser.throw_label and dm[1] then + parser.success = true + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- capture + local cap_start_pos = parser.pos + -- match literal "or" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "or" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +if not parser.success and not parser.throw_label and dm[2] then + if dm.s[2] then record_furthest(parser) end + parser.success = true + do -- sequence with 3 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- capture + local cap_start_pos = parser.pos + -- match literal "and" +if sub(parser.input, parser.pos + 1, parser.pos + 3) == "and" then + parser.pos = parser.pos + 3 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end +if parser.success then +do -- negate (only match if pattern fails) + local pp_pos = parser.pos + do -- match character range "az,AZ,09,__" + local rb = byte(parser.input, parser.pos + 1) + if rb and ((rb >= 97 and rb <= 122) or (rb >= 65 and rb <= 90) or (rb >= 48 and rb <= 57) or (rb >= 95 and rb <= 95)) then + parser.pos = parser.pos + 1 + else + parser.success = false + + end +end + if parser.success then + -- Pattern matched, so negate fails + parser.pos = pp_pos + parser.success = false + record_furthest(parser) + + else + -- Pattern failed, so negate succeeds + parser.success = true + -- Swallow labeled failures inside predicates (LPegLabel behavior) + if parser.throw_label then + parser.throw_label = nil + parser.throw_pos = 0 + end + parser.pos = pp_pos + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +if not parser.success and not parser.throw_label and dm[3] then + if dm.s[3] then record_furthest(parser) end + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- capture + local cap_start_pos = parser.pos + -- match literal "<=" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "<=" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +if not parser.success and not parser.throw_label and dm[4] then + if dm.s[4] then record_furthest(parser) end + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- capture + local cap_start_pos = parser.pos + -- match literal ">=" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == ">=" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +if not parser.success and not parser.throw_label and dm[5] then + if dm.s[5] then record_furthest(parser) end + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- capture + local cap_start_pos = parser.pos + -- match literal "~=" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "~=" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +if not parser.success and not parser.throw_label and dm[6] then + if dm.s[6] then record_furthest(parser) end + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- capture + local cap_start_pos = parser.pos + -- match literal "!=" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "!=" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +if not parser.success and not parser.throw_label and dm[7] then + if dm.s[7] then record_furthest(parser) end + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- capture + local cap_start_pos = parser.pos + -- match literal "==" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "==" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +if not parser.success and not parser.throw_label and dm[8] then + if dm.s[8] then record_furthest(parser) end + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- capture + local cap_start_pos = parser.pos + -- match literal ".." +if sub(parser.input, parser.pos + 1, parser.pos + 2) == ".." then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +if not parser.success and not parser.throw_label and dm[9] then + if dm.s[9] then record_furthest(parser) end + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- capture + local cap_start_pos = parser.pos + -- match literal "<<" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "<<" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +if not parser.success and not parser.throw_label and dm[10] then + if dm.s[10] then record_furthest(parser) end + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- capture + local cap_start_pos = parser.pos + -- match literal ">>" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == ">>" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end +if not parser.success and not parser.throw_label and dm[11] then + if dm.s[11] then record_furthest(parser) end + parser.success = true + do -- sequence with 2 patterns + local pp_pos, pp_cap, pp_trail = parser.pos, parser.cap_n, parser.trail_n + rules["Space"](parser) +if parser.success then +do -- capture + local cap_start_pos = parser.pos + -- match literal "//" +if sub(parser.input, parser.pos + 1, parser.pos + 2) == "//" then + parser.pos = parser.pos + 2 +else + parser.success = false + record_furthest(parser) + +end + if parser.success then + cap_push(parser, CAP_STR, nil, cap_start_pos, parser.pos - cap_start_pos) + end +end +end + if not parser.success then + parser.pos = pp_pos parser.cap_n = pp_cap ind_trail_rewind(parser, pp_trail) + end +end +end + if not parser.success and not parser.throw_label then + record_furthest(parser) + end +end + + parser.depth = depth - 1 + return parser.success +end + + +local function new_parser(input) + return { + input = input, + input_len = #input, + pos = 0, -- 0-based like the C target; converted at the API boundary + success = true, + throw_label = nil, -- label from T() or nil for ordinary failure + throw_pos = 0, + furthest_fail = 0, + depth = 0, + cap_kind = {}, cap_aux = {}, cap_start = {}, cap_size = {}, + cap_n = 0, + values = {}, values_n = 0, + memo_pos = {}, memo_end = {}, + ind_stacks = { { 0, n = 1 }, { 1, n = 1 } }, + trail_id = {}, trail_op = {}, trail_val = {}, trail_n = 0, + } +end + +local function parse(input) + if type(input) == "number" then + input = tostring(input) + end + if type(input) ~= "string" then + error("Expected string argument for parsing") + end + + local parser = new_parser(input) + + rules["Root"](parser) + + -- Return nil and error info on failure + if not parser.success then + if parser.throw_label then + -- Labeled failure: return nil, label, position + return nil, parser.throw_label, parser.throw_pos + 1 + end + -- Ordinary failure: return nil, message (pgen_errors builds only) and + -- the furthest input position a match attempt failed at (1-indexed) + return nil, nil, parser.furthest_fail + 1 + end + + -- Materialize the capture log into return values. Named groups produce + -- no top-level values (they only matter inside Ct). + local out = {n = 0} + local i = 1 + local ck = parser.cap_kind + while i <= parser.cap_n do + if ck[i] == CAP_GROUP_OPEN then + i = cap_skip(parser, i) + else + i = cap_eval(parser, i, out) + end + end + + if out.n > 0 then + -- Probe large result lists first: unpack past the runtime's stack limit + -- must surface as a clean, recognizable error + if out.n >= 1000 and not pcall(unpack, out, 1, out.n) then + error("pgen: Lua stack overflow while building captures") + end + return unpack(out, 1, out.n) + end + + -- Success case with no captures: return position of consumed input + return parser.pos + 1 +end + +return { + parse = parse +} diff --git a/moonscript/parse/tree.lua b/moonscript/parse/tree.lua new file mode 100644 index 00000000..8527dabd --- /dev/null +++ b/moonscript/parse/tree.lua @@ -0,0 +1,105 @@ +local unpack = unpack or table.unpack +local ntype +ntype = function(node) + if type(node) == "table" then + return node[1] + end + return "value" +end +local chain_assignable = { + index = true, + dot = true, + slice = true +} +local is_assignable +is_assignable = function(node) + if node == "..." then + return false + end + local _exp_0 = ntype(node) + if "ref" == _exp_0 or "self" == _exp_0 or "value" == _exp_0 or "self_class" == _exp_0 or "table" == _exp_0 then + return true + elseif "chain" == _exp_0 then + return chain_assignable[ntype(node[#node])] + else + return false + end +end +local flatten_or_mark +flatten_or_mark = function(name) + return function(tbl) + if #tbl == 1 then + return tbl[1] + end + table.insert(tbl, 1, name) + return tbl + end +end +local flatten_explist = flatten_or_mark("explist") +local format_assign +format_assign = function(lhs_exps, assign) + if not (assign) then + return flatten_explist(lhs_exps) + end + for _index_0 = 1, #lhs_exps do + local assign_exp = lhs_exps[_index_0] + if not (is_assignable(assign_exp)) then + error({ + assign_exp, + "left hand expression is not assignable" + }) + end + end + local t = ntype(assign) + local _exp_0 = t + if "assign" == _exp_0 then + return { + "assign", + lhs_exps, + unpack(assign, 2) + } + elseif "update" == _exp_0 then + return { + "update", + lhs_exps[1], + unpack(assign, 2) + } + else + return error("unknown assign expression: " .. tostring(t)) + end +end +local format_single_assign +format_single_assign = function(lhs, assign) + if assign then + return format_assign({ + lhs + }, assign) + end + return lhs +end +local join_chain +join_chain = function(callee, args) + if #args == 0 then + return callee + end + args = { + "call", + args + } + if ntype(callee) == "chain" then + table.insert(callee, args) + return callee + end + return { + "chain", + callee, + args + } +end +return { + ntype = ntype, + is_assignable = is_assignable, + format_assign = format_assign, + format_single_assign = format_single_assign, + join_chain = join_chain +} diff --git a/moonscript/parse/tree.moon b/moonscript/parse/tree.moon new file mode 100644 index 00000000..170e8360 --- /dev/null +++ b/moonscript/parse/tree.moon @@ -0,0 +1,71 @@ +-- Transform helpers for the grammar's Cfn callbacks. The callbacks +-- themselves live as code strings in grammar.moon and require this module +-- at parser load. +-- +-- format_assign raises error({node, msg}) for invalid assignment targets; +-- it propagates out of parse() and is formatted by moonscript/parse.moon. + +unpack = unpack or table.unpack + +-- never called with nil: that would report "value" +ntype = (node) -> + if type(node) == "table" + return node[1] + + "value" + +chain_assignable = {index: true, dot: true, slice: true} + +is_assignable = (node) -> + return false if node == "..." + + switch ntype node + when "ref", "self", "value", "self_class", "table" + true + when "chain" + chain_assignable[ntype node[#node]] + else + false + +flatten_or_mark = (name) -> + (tbl) -> + return tbl[1] if #tbl == 1 + table.insert tbl, 1, name + tbl + +flatten_explist = flatten_or_mark "explist" + +format_assign = (lhs_exps, assign) -> + unless assign + return flatten_explist lhs_exps + + for assign_exp in *lhs_exps + unless is_assignable assign_exp + error {assign_exp, "left hand expression is not assignable"} + + t = ntype assign + switch t + when "assign" + {"assign", lhs_exps, unpack assign, 2} + when "update" + {"update", lhs_exps[1], unpack assign, 2} + else + error "unknown assign expression: #{t}" + +format_single_assign = (lhs, assign) -> + if assign + return format_assign {lhs}, assign + + lhs + +join_chain = (callee, args) -> + return callee if #args == 0 + args = {"call", args} + + if ntype(callee) == "chain" + table.insert callee, args + return callee + + {"chain", callee, args} + +{ :ntype, :is_assignable, :format_assign, :format_single_assign, :join_chain } diff --git a/moonscript/transform.lua b/moonscript/transform.lua index 2d28877c..c95f14d3 100644 --- a/moonscript/transform.lua +++ b/moonscript/transform.lua @@ -1,1583 +1,4 @@ -module("moonscript.transform", package.seeall) -local types = require("moonscript.types") -local util = require("moonscript.util") -local data = require("moonscript.data") -local reversed = util.reversed -local ntype, build, smart_node, is_slice, value_is_singular = types.ntype, types.build, types.smart_node, types.is_slice, types.value_is_singular -local insert = table.insert -LocalName = (function() - local _parent_0 = nil - local _base_0 = { - get_name = function(self) - return self.name - end - } - _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self, name) - self.name = name - self[1] = "temp_name" - end, - __base = _base_0, - __name = "LocalName", - __parent = _parent_0 - }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, - __call = function(cls, ...) - local _self_0 = setmetatable({}, _base_0) - cls.__init(_self_0, ...) - return _self_0 - end - }) - _base_0.__class = _class_0 - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -NameProxy = (function() - local _parent_0 = nil - local _base_0 = { - get_name = function(self, scope) - if not self.name then - self.name = scope:free_name(self.prefix, true) - end - return self.name - end, - chain = function(self, ...) - local items = { - ... - } - items = (function() - local _accum_0 = { } - local _len_0 = 0 - local _list_0 = items - for _index_0 = 1, #_list_0 do - local i = _list_0[_index_0] - local _value_0 - if type(i) == "string" then - _value_0 = { - "dot", - i - } - else - _value_0 = i - end - if _value_0 ~= nil then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = _value_0 - end - end - return _accum_0 - end)() - return build.chain({ - base = self, - unpack(items) - }) - end, - index = function(self, key) - return build.chain({ - base = self, - { - "index", - key - } - }) - end, - __tostring = function(self) - if self.name then - return ("name<%s>"):format(self.name) - else - return ("name"):format(self.prefix) - end - end - } - _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self, prefix) - self.prefix = prefix - self[1] = "temp_name" - end, - __base = _base_0, - __name = "NameProxy", - __parent = _parent_0 - }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, - __call = function(cls, ...) - local _self_0 = setmetatable({}, _base_0) - cls.__init(_self_0, ...) - return _self_0 - end - }) - _base_0.__class = _class_0 - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -Run = (function() - local _parent_0 = nil - local _base_0 = { - call = function(self, state) - return self.fn(state) - end - } - _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self, fn) - self.fn = fn - self[1] = "run" - end, - __base = _base_0, - __name = "Run", - __parent = _parent_0 - }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, - __call = function(cls, ...) - local _self_0 = setmetatable({}, _base_0) - cls.__init(_self_0, ...) - return _self_0 - end - }) - _base_0.__class = _class_0 - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -local apply_to_last -apply_to_last = function(stms, fn) - local last_exp_id = 0 - for i = #stms, 1, -1 do - local stm = stms[i] - if stm and util.moon.type(stm) ~= Run then - last_exp_id = i - break - end - end - return (function() - local _accum_0 = { } - local _len_0 = 0 - for i, stm in ipairs(stms) do - local _value_0 - if i == last_exp_id then - _value_0 = fn(stm) - else - _value_0 = stm - end - if _value_0 ~= nil then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = _value_0 - end - end - return _accum_0 - end)() -end -local is_singular -is_singular = function(body) - if #body ~= 1 then - return false - end - if "group" == ntype(body) then - return is_singular(body[2]) - else - return true - end -end -local find_assigns -find_assigns = function(body, out) - if out == nil then - out = { } - end - local _list_0 = body - for _index_0 = 1, #_list_0 do - local thing = _list_0[_index_0] - local _exp_0 = thing[1] - if "group" == _exp_0 then - find_assigns(thing[2], out) - elseif "assign" == _exp_0 then - table.insert(out, thing[2]) - end - end - return out -end -local hoist_declarations -hoist_declarations = function(body, rules) - local assigns = { } - local _list_0 = find_assigns(body) - for _index_0 = 1, #_list_0 do - local names = _list_0[_index_0] - local _list_1 = names - for _index_1 = 1, #_list_1 do - local name = _list_1[_index_1] - if type(name) == "string" then - table.insert(assigns, name) - end - end - end - return table.insert(body, 1, { - "declare", - assigns - }) -end -local expand_elseif_assign -expand_elseif_assign = function(ifstm) - for i = 4, #ifstm do - local case = ifstm[i] - if ntype(case) == "elseif" and ntype(case[2]) == "assign" then - local split = { - unpack(ifstm, 1, i - 1) - } - insert(split, { - "else", - { - { - "if", - case[2], - case[3], - unpack(ifstm, i + 1) - } - } - }) - return split - end - end - return ifstm -end -local constructor_name = "new" -local with_continue_listener -with_continue_listener = function(body) - local continue_name = nil - return { - Run(function(self) - return self:listen("continue", function() - if not (continue_name) then - continue_name = NameProxy("continue") - self:put_name(continue_name) - end - return continue_name - end) - end), - build.group(body), - Run(function(self) - if not (continue_name) then - return - end - self:put_name(continue_name, nil) - return self:splice(function(lines) - return { - { - "assign", - { - continue_name - }, - { - "false" - } - }, - { - "repeat", - "true", - { - lines, - { - "assign", - { - continue_name - }, - { - "true" - } - } - } - }, - { - "if", - { - "not", - continue_name - }, - { - { - "break" - } - } - } - } - end) - end) - } -end -local Transformer -Transformer = (function() - local _parent_0 = nil - local _base_0 = { - transform = function(self, scope, node, ...) - if self.seen_nodes[node] then - return node - end - self.seen_nodes[node] = true - while true do - local transformer = self.transformers[ntype(node)] - local res - if transformer then - res = transformer(scope, node, ...) or node - else - res = node - end - if res == node then - return node - end - node = res - end - return node - end, - bind = function(self, scope) - return function(...) - return self:transform(scope, ...) - end - end, - __call = function(self, ...) - return self:transform(...) - end, - can_transform = function(self, node) - return self.transformers[ntype(node)] ~= nil - end - } - _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self, transformers) - self.transformers = transformers - self.seen_nodes = setmetatable({ }, { - __mode = "k" - }) - end, - __base = _base_0, - __name = "Transformer", - __parent = _parent_0 - }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, - __call = function(cls, ...) - local _self_0 = setmetatable({}, _base_0) - cls.__init(_self_0, ...) - return _self_0 - end - }) - _base_0.__class = _class_0 - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -local construct_comprehension -construct_comprehension = function(inner, clauses) - local current_stms = inner - for _, clause in reversed(clauses) do - local t = clause[1] - if t == "for" then - local names, iter - _, names, iter = unpack(clause) - current_stms = { - "foreach", - names, - { - iter - }, - current_stms - } - elseif t == "when" then - local cond - _, cond = unpack(clause) - current_stms = { - "if", - cond, - current_stms - } - else - current_stms = error("Unknown comprehension clause: " .. t) - end - current_stms = { - current_stms - } - end - return current_stms[1] -end -Statement = Transformer({ - assign = function(self, node) - local names, values = unpack(node, 2) - local transformed - if #values == 1 then - local value = values[1] - local t = ntype(value) - if t == "decorated" then - value = self.transform.statement(value) - t = ntype(value) - end - if types.cascading[t] then - transformed = build.group({ - { - "declare", - names - }, - self.transform.statement(value, function(stm) - if types.is_value(stm) then - return { - "assign", - names, - { - stm - } - } - else - return stm - end - end) - }) - end - end - return transformed or node - end, - continue = function(self, node) - local continue_name = self:send("continue") - if not (continue_name) then - error("continue must be inside of a loop") - end - return build.group({ - build.assign_one(continue_name, "true"), - { - "break" - } - }) - end, - export = function(self, node) - if #node > 2 then - if node[2] == "class" then - local cls = smart_node(node[3]) - return build.group({ - { - "export", - { - cls.name - } - }, - cls - }) - else - return build.group({ - node, - build.assign({ - names = node[2], - values = node[3] - }) - }) - end - else - return nil - end - end, - update = function(self, node) - local _, name, op, exp = unpack(node) - local op_final = op:match("^(.+)=$") - if not op_final then - error("Unknown op: " .. op) - end - if not (value_is_singular(exp)) then - exp = { - "parens", - exp - } - end - return build.assign_one(name, { - "exp", - name, - op_final, - exp - }) - end, - import = function(self, node) - local _, names, source = unpack(node) - local stubs = (function() - local _accum_0 = { } - local _len_0 = 0 - local _list_0 = names - for _index_0 = 1, #_list_0 do - local name = _list_0[_index_0] - local _value_0 - if type(name) == "table" then - _value_0 = name - else - _value_0 = { - "dot", - name - } - end - if _value_0 ~= nil then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = _value_0 - end - end - return _accum_0 - end)() - local real_names = (function() - local _accum_0 = { } - local _len_0 = 0 - local _list_0 = names - for _index_0 = 1, #_list_0 do - local name = _list_0[_index_0] - local _value_0 = type(name) == "table" and name[2] or name - if _value_0 ~= nil then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = _value_0 - end - end - return _accum_0 - end)() - if type(source) == "string" then - return build.assign({ - names = real_names, - values = (function() - local _accum_0 = { } - local _len_0 = 0 - local _list_0 = stubs - for _index_0 = 1, #_list_0 do - local stub = _list_0[_index_0] - _len_0 = _len_0 + 1 - _accum_0[_len_0] = build.chain({ - base = source, - stub - }) - end - return _accum_0 - end)() - }) - else - local source_name = NameProxy("table") - return build.group({ - { - "declare", - real_names - }, - build["do"]({ - build.assign_one(source_name, source), - build.assign({ - names = real_names, - values = (function() - local _accum_0 = { } - local _len_0 = 0 - local _list_0 = stubs - for _index_0 = 1, #_list_0 do - local stub = _list_0[_index_0] - _len_0 = _len_0 + 1 - _accum_0[_len_0] = build.chain({ - base = source_name, - stub - }) - end - return _accum_0 - end)() - }) - }) - }) - end - end, - comprehension = function(self, node, action) - local _, exp, clauses = unpack(node) - action = action or function(exp) - return { - exp - } - end - return construct_comprehension(action(exp), clauses) - end, - ["do"] = function(self, node, ret) - if ret then - node[2] = apply_to_last(node[2], ret) - end - return node - end, - decorated = function(self, node) - local stm, dec = unpack(node, 2) - local wrapped - local _exp_0 = dec[1] - if "if" == _exp_0 then - local cond, fail = unpack(dec, 2) - if fail then - fail = { - "else", - { - fail - } - } - end - wrapped = { - "if", - cond, - { - stm - }, - fail - } - elseif "unless" == _exp_0 then - wrapped = { - "unless", - dec[2], - { - stm - } - } - elseif "comprehension" == _exp_0 then - wrapped = { - "comprehension", - stm, - dec[2] - } - else - wrapped = error("Unknown decorator " .. dec[1]) - end - if ntype(stm) == "assign" then - wrapped = build.group({ - build.declare({ - names = (function() - local _accum_0 = { } - local _len_0 = 0 - local _list_0 = stm[2] - for _index_0 = 1, #_list_0 do - local name = _list_0[_index_0] - if type(name) == "string" then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = name - end - end - return _accum_0 - end)() - }), - wrapped - }) - end - return wrapped - end, - unless = function(self, node) - return { - "if", - { - "not", - { - "parens", - node[2] - } - }, - unpack(node, 3) - } - end, - ["if"] = function(self, node, ret) - if ntype(node[2]) == "assign" then - local _, assign, body = unpack(node) - local name = assign[2][1] - return build["do"]({ - assign, - { - "if", - name, - unpack(node, 3) - } - }) - end - node = expand_elseif_assign(node) - if ret then - smart_node(node) - node['then'] = apply_to_last(node['then'], ret) - for i = 4, #node do - local case = node[i] - local body_idx = #node[i] - case[body_idx] = apply_to_last(case[body_idx], ret) - end - end - return node - end, - with = function(self, node, ret) - local _, exp, block = unpack(node) - local scope_name = NameProxy("with") - return build["do"]({ - build.assign_one(scope_name, exp), - Run(function(self) - return self:set("scope_var", scope_name) - end), - build.group(block), - (function() - if ret then - return ret(scope_name) - end - end)() - }) - end, - foreach = function(self, node) - smart_node(node) - local source = unpack(node.iter) - if ntype(source) == "unpack" then - local list = source[2] - local index_name = NameProxy("index") - local list_name = NameProxy("list") - local slice_var = nil - local bounds - if is_slice(list) then - local slice = list[#list] - table.remove(list) - table.remove(slice, 1) - if slice[2] and slice[2] ~= "" then - local max_tmp_name = NameProxy("max") - slice_var = build.assign_one(max_tmp_name, slice[2]) - slice[2] = { - "exp", - max_tmp_name, - "<", - 0, - "and", - { - "length", - list_name - }, - "+", - max_tmp_name, - "or", - max_tmp_name - } - else - slice[2] = { - "length", - list_name - } - end - bounds = slice - else - bounds = { - 1, - { - "length", - list_name - } - } - end - return build.group({ - build.assign_one(list_name, list), - slice_var, - build["for"]({ - name = index_name, - bounds = bounds, - body = { - { - "assign", - node.names, - { - list_name:index(index_name) - } - }, - build.group(node.body) - } - }) - }) - end - node.body = with_continue_listener(node.body) - end, - ["while"] = function(self, node) - smart_node(node) - node.body = with_continue_listener(node.body) - end, - ["for"] = function(self, node) - smart_node(node) - node.body = with_continue_listener(node.body) - end, - switch = function(self, node, ret) - local _, exp, conds = unpack(node) - local exp_name = NameProxy("exp") - local convert_cond - convert_cond = function(cond) - local t, case_exp, body = unpack(cond) - local out = { } - insert(out, t == "case" and "elseif" or "else") - if t ~= "else" then - if t ~= "else" then - insert(out, { - "exp", - case_exp, - "==", - exp_name - }) - end - else - body = case_exp - end - if ret then - body = apply_to_last(body, ret) - end - insert(out, body) - return out - end - local first = true - local if_stm = { - "if" - } - local _list_0 = conds - for _index_0 = 1, #_list_0 do - local cond = _list_0[_index_0] - local if_cond = convert_cond(cond) - if first then - first = false - insert(if_stm, if_cond[2]) - insert(if_stm, if_cond[3]) - else - insert(if_stm, if_cond) - end - end - return build.group({ - build.assign_one(exp_name, exp), - if_stm - }) - end, - class = function(self, node) - local _, name, parent_val, body = unpack(node) - local statements = { } - local properties = { } - local _list_0 = body - for _index_0 = 1, #_list_0 do - local item = _list_0[_index_0] - local _exp_0 = item[1] - if "stm" == _exp_0 then - insert(statements, item[2]) - elseif "props" == _exp_0 then - local _list_1 = item - for _index_1 = 2, #_list_1 do - local tuple = _list_1[_index_1] - if ntype(tuple[1]) == "self" then - insert(statements, build.assign_one(unpack(tuple))) - else - insert(properties, tuple) - end - end - end - end - local constructor = nil - properties = (function() - local _accum_0 = { } - local _len_0 = 0 - local _list_1 = properties - for _index_0 = 1, #_list_1 do - local tuple = _list_1[_index_0] - local key = tuple[1] - local _value_0 - if key[1] == "key_literal" and key[2] == constructor_name then - constructor = tuple[2] - _value_0 = nil - else - _value_0 = tuple - end - if _value_0 ~= nil then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = _value_0 - end - end - return _accum_0 - end)() - local parent_cls_name = NameProxy("parent") - local base_name = NameProxy("base") - local self_name = NameProxy("self") - local cls_name = NameProxy("class") - if not constructor then - constructor = build.fndef({ - args = { - { - "..." - } - }, - arrow = "fat", - body = { - build["if"]({ - cond = parent_cls_name, - ["then"] = { - build.chain({ - base = "super", - { - "call", - { - "..." - } - } - }) - } - }) - } - }) - else - smart_node(constructor) - constructor.arrow = "fat" - end - local cls = build.table({ - { - "__init", - constructor - }, - { - "__base", - base_name - }, - { - "__name", - { - "string", - '"', - name - } - }, - { - "__parent", - parent_cls_name - } - }) - local class_lookup = build["if"]({ - cond = { - "exp", - "val", - "==", - "nil", - "and", - parent_cls_name - }, - ["then"] = { - parent_cls_name:index("name") - } - }) - insert(class_lookup, { - "else", - { - "val" - } - }) - local cls_mt = build.table({ - { - "__index", - build.fndef({ - args = { - { - "cls" - }, - { - "name" - } - }, - body = { - build.assign_one(LocalName("val"), build.chain({ - base = "rawget", - { - "call", - { - base_name, - "name" - } - } - })), - class_lookup - } - }) - }, - { - "__call", - build.fndef({ - args = { - { - "cls" - }, - { - "..." - } - }, - body = { - build.assign_one(self_name, build.chain({ - base = "setmetatable", - { - "call", - { - "{}", - base_name - } - } - })), - build.chain({ - base = "cls.__init", - { - "call", - { - self_name, - "..." - } - } - }), - self_name - } - }) - } - }) - cls = build.chain({ - base = "setmetatable", - { - "call", - { - cls, - cls_mt - } - } - }) - local value = nil - do - local _with_0 = build - local out_body = { - Run(function(self) - return self:set("super", function(block, chain) - if chain then - local slice = (function() - local _accum_0 = { } - local _len_0 = 0 - local _list_1 = chain - for _index_0 = 3, #_list_1 do - local item = _list_1[_index_0] - _len_0 = _len_0 + 1 - _accum_0[_len_0] = item - end - return _accum_0 - end)() - local new_chain = { - "chain", - parent_cls_name - } - local head = slice[1] - if head == nil then - return parent_cls_name - end - local _exp_0 = head[1] - if "call" == _exp_0 then - local calling_name = block:get("current_block") - slice[1] = { - "call", - { - "self", - unpack(head[2]) - } - } - if ntype(calling_name) == "key_literal" then - insert(new_chain, { - "dot", - calling_name[2] - }) - else - insert(new_chain, { - "index", - calling_name - }) - end - elseif "colon" == _exp_0 then - local call = head[3] - insert(new_chain, { - "dot", - head[2] - }) - slice[1] = { - "call", - { - "self", - unpack(call[2]) - } - } - end - local _list_1 = slice - for _index_0 = 1, #_list_1 do - local item = _list_1[_index_0] - insert(new_chain, item) - end - return new_chain - else - return parent_cls_name - end - end) - end), - _with_0.assign_one(parent_cls_name, parent_val == "" and "nil" or parent_val), - _with_0.assign_one(base_name, { - "table", - properties - }), - _with_0.assign_one(base_name:chain("__index"), base_name), - build["if"]({ - cond = parent_cls_name, - ["then"] = { - _with_0.chain({ - base = "setmetatable", - { - "call", - { - base_name, - _with_0.chain({ - base = parent_cls_name, - { - "dot", - "__base" - } - }) - } - } - }) - } - }), - _with_0.assign_one(cls_name, cls), - _with_0.assign_one(base_name:chain("__class"), cls_name), - _with_0.group((function() - if #statements > 0 then - return { - _with_0.assign_one(LocalName("self"), cls_name), - _with_0.group(statements) - } - else - return { } - end - end)()), - build["if"]({ - cond = { - "exp", - parent_cls_name, - "and", - parent_cls_name:chain("__inherited") - }, - ["then"] = { - parent_cls_name:chain("__inherited", { - "call", - { - parent_cls_name, - cls_name - } - }) - } - }), - cls_name - } - hoist_declarations(out_body) - value = _with_0.group({ - _with_0.declare({ - names = { - name - } - }), - _with_0.assign({ - names = { - name - }, - values = { - _with_0.block_exp(out_body) - } - }) - }) - end - return value - end -}) -local Accumulator -Accumulator = (function() - local _parent_0 = nil - local _base_0 = { - body_idx = { - ["for"] = 4, - ["while"] = 3, - foreach = 4 - }, - convert = function(self, node) - local index = self.body_idx[ntype(node)] - node[index] = self:mutate_body(node[index]) - return self:wrap(node) - end, - wrap = function(self, node) - return build.block_exp({ - build.assign_one(self.accum_name, build.table()), - build.assign_one(self.len_name, 0), - node, - self.accum_name - }) - end, - mutate_body = function(self, body, skip_nil) - if skip_nil == nil then - skip_nil = true - end - local val - if not skip_nil and is_singular(body) then - do - local _with_0 = body[1] - body = { } - val = _with_0 - end - else - body = apply_to_last(body, function(n) - return build.assign_one(self.value_name, n) - end) - val = self.value_name - end - local update = { - { - "update", - self.len_name, - "+=", - 1 - }, - build.assign_one(self.accum_name:index(self.len_name), val) - } - if skip_nil then - table.insert(body, build["if"]({ - cond = { - "exp", - self.value_name, - "!=", - "nil" - }, - ["then"] = update - })) - else - table.insert(body, build.group(update)) - end - return body - end - } - _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self) - self.accum_name = NameProxy("accum") - self.value_name = NameProxy("value") - self.len_name = NameProxy("len") - end, - __base = _base_0, - __name = "Accumulator", - __parent = _parent_0 - }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, - __call = function(cls, ...) - local _self_0 = setmetatable({}, _base_0) - cls.__init(_self_0, ...) - return _self_0 - end - }) - _base_0.__class = _class_0 - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -local default_accumulator -default_accumulator = function(self, node) - return Accumulator():convert(node) -end -local implicitly_return -implicitly_return = function(scope) - local is_top = true - local fn - fn = function(stm) - local t = ntype(stm) - if t == "decorated" then - stm = scope.transform.statement(stm) - t = ntype(stm) - end - if types.manual_return[t] or not types.is_value(stm) then - if is_top and t == "return" and stm[2] == "" then - return nil - else - return stm - end - elseif types.cascading[t] then - is_top = false - return scope.transform.statement(stm, fn) - else - if t == "comprehension" and not types.comprehension_has_value(stm) then - return stm - else - return { - "return", - stm - } - end - end - end - return fn -end -Value = Transformer({ - ["for"] = default_accumulator, - ["while"] = default_accumulator, - foreach = default_accumulator, - decorated = function(self, node) - return self.transform.statement(node) - end, - string = function(self, node) - local delim = node[2] - local convert_part - convert_part = function(part) - if type(part) == "string" or part == nil then - return { - "string", - delim, - part or "" - } - else - return build.chain({ - base = "tostring", - { - "call", - { - part[2] - } - } - }) - end - end - if #node <= 3 then - return (function() - if type(node[3]) == "string" then - return node - else - return convert_part(node[3]) - end - end)() - end - local e = { - "exp", - convert_part(node[3]) - } - for i = 4, #node do - insert(e, "..") - insert(e, convert_part(node[i])) - end - return e - end, - comprehension = function(self, node) - local a = Accumulator() - node = self.transform.statement(node, function(exp) - return a:mutate_body({ - exp - }, false) - end) - return a:wrap(node) - end, - tblcomprehension = function(self, node) - local _, explist, clauses = unpack(node) - local key_exp, value_exp = unpack(explist) - local accum = NameProxy("tbl") - local inner - if value_exp then - local dest = build.chain({ - base = accum, - { - "index", - key_exp - } - }) - inner = { - build.assign_one(dest, value_exp) - } - else - local key_name, val_name = NameProxy("key"), NameProxy("val") - local dest = build.chain({ - base = accum, - { - "index", - key_name - } - }) - inner = { - build.assign({ - names = { - key_name, - val_name - }, - values = { - key_exp - } - }), - build.assign_one(dest, val_name) - } - end - return build.block_exp({ - build.assign_one(accum, build.table()), - construct_comprehension(inner, clauses), - accum - }) - end, - fndef = function(self, node) - smart_node(node) - node.body = apply_to_last(node.body, implicitly_return(self)) - node.body = { - Run(function(self) - return self:listen("varargs", function() end) - end), - unpack(node.body) - } - return node - end, - ["if"] = function(self, node) - return build.block_exp({ - node - }) - end, - unless = function(self, node) - return build.block_exp({ - node - }) - end, - with = function(self, node) - return build.block_exp({ - node - }) - end, - switch = function(self, node) - return build.block_exp({ - node - }) - end, - chain = function(self, node) - local stub = node[#node] - for i = 3, #node do - local part = node[i] - if ntype(part) == "dot" and data.lua_keywords[part[2]] then - node[i] = { - "index", - { - "string", - '"', - part[2] - } - } - end - end - if ntype(node[2]) == "string" then - node[2] = { - "parens", - node[2] - } - elseif type(stub) == "table" and stub[1] == "colon_stub" then - table.remove(node, #node) - local base_name = NameProxy("base") - local fn_name = NameProxy("fn") - local is_super = node[2] == "super" - return self.transform.value(build.block_exp({ - build.assign({ - names = { - base_name - }, - values = { - node - } - }), - build.assign({ - names = { - fn_name - }, - values = { - build.chain({ - base = base_name, - { - "dot", - stub[2] - } - }) - } - }), - build.fndef({ - args = { - { - "..." - } - }, - body = { - build.chain({ - base = fn_name, - { - "call", - { - is_super and "self" or base_name, - "..." - } - } - }) - } - }) - })) - end - end, - block_exp = function(self, node) - local _, body = unpack(node) - local fn = nil - local arg_list = { } - fn = smart_node(build.fndef({ - body = { - Run(function(self) - return self:listen("varargs", function() - insert(arg_list, "...") - insert(fn.args, { - "..." - }) - return self:unlisten("varargs") - end) - end), - unpack(body) - } - })) - return build.chain({ - base = { - "parens", - fn - }, - { - "call", - arg_list - } - }) - end -}) +return { + Statement = require("moonscript.transform.statement"), + Value = require("moonscript.transform.value") +} diff --git a/moonscript/transform.moon b/moonscript/transform.moon index ca295ff6..29a93ad2 100644 --- a/moonscript/transform.moon +++ b/moonscript/transform.moon @@ -1,843 +1,5 @@ -module "moonscript.transform", package.seeall - -types = require "moonscript.types" -util = require "moonscript.util" -data = require "moonscript.data" - -import reversed from util -import ntype, build, smart_node, is_slice, value_is_singular from types -import insert from table - -export Statement, Value, NameProxy, LocalName, Run - --- always declares as local -class LocalName - new: (@name) => self[1] = "temp_name" - get_name: => @name - -class NameProxy - new: (@prefix) => - self[1] = "temp_name" - - get_name: (scope) => - if not @name - @name = scope\free_name @prefix, true - @name - - chain: (...) => - items = {...} -- todo: fix ... propagation - items = for i in *items - if type(i) == "string" - {"dot", i} - else - i - - build.chain { - base: self - unpack items - } - - index: (key) => - build.chain { - base: self, {"index", key} - } - - __tostring: => - if @name - ("name<%s>")\format @name - else - ("name")\format @prefix - -class Run - new: (@fn) => - self[1] = "run" - - call: (state) => - self.fn state - --- transform the last stm is a list of stms --- will puke on group -apply_to_last = (stms, fn) -> - -- find last (real) exp - last_exp_id = 0 - for i = #stms, 1, -1 - stm = stms[i] - if stm and util.moon.type(stm) != Run - last_exp_id = i - break - - return for i, stm in ipairs stms - if i == last_exp_id - fn stm - else - stm - --- is a body a sindle expression/statement -is_singular = (body) -> - return false if #body != 1 - if "group" == ntype body - is_singular body[2] - else - true - -find_assigns = (body, out={}) -> - for thing in *body - switch thing[1] - when "group" - find_assigns thing[2], out - when "assign" - table.insert out, thing[2] -- extract names - out - -hoist_declarations = (body, rules) -> - assigns = {} - - -- hoist the plain old assigns - for names in *find_assigns body - for name in *names - table.insert assigns, name if type(name) == "string" - - table.insert body, 1, {"declare", assigns} - -expand_elseif_assign = (ifstm) -> - for i = 4, #ifstm - case = ifstm[i] - if ntype(case) == "elseif" and ntype(case[2]) == "assign" - split = { unpack ifstm, 1, i - 1 } - insert split, { - "else", { - {"if", case[2], case[3], unpack ifstm, i + 1} - } - } - return split - - ifstm - -constructor_name = "new" - -with_continue_listener = (body) -> - continue_name = nil - { - Run => - @listen "continue", -> - unless continue_name - continue_name = NameProxy"continue" - @put_name continue_name - continue_name - - build.group body - - Run => - return unless continue_name - @put_name continue_name, nil - @splice (lines) -> { - {"assign", {continue_name}, {"false"}} - {"repeat", "true", { - lines - {"assign", {continue_name}, {"true"}} - }} - {"if", {"not", continue_name}, { - {"break"} - }} - } - } - - -class Transformer - new: (@transformers) => - @seen_nodes = setmetatable {}, __mode: "k" - - transform: (scope, node, ...) => - return node if @seen_nodes[node] - @seen_nodes[node] = true - while true - transformer = @transformers[ntype node] - res = if transformer - transformer(scope, node, ...) or node - else - node - return node if res == node - node = res - node - - bind: (scope) => - (...) -> @transform scope, ... - - __call: (...) => @transform ... - - can_transform: (node) => - @transformers[ntype node] != nil - -construct_comprehension = (inner, clauses) -> - current_stms = inner - for _, clause in reversed clauses - t = clause[1] - current_stms = if t == "for" - _, names, iter = unpack clause - {"foreach", names, {iter}, current_stms} - elseif t == "when" - _, cond = unpack clause - {"if", cond, current_stms} - else - error "Unknown comprehension clause: "..t - current_stms = {current_stms} - - current_stms[1] - -Statement = Transformer { - assign: (node) => - names, values = unpack node, 2 - -- bubble cascading assigns - transformed = if #values == 1 - value = values[1] - t = ntype value - - if t == "decorated" - value = @transform.statement value - t = ntype value - - if types.cascading[t] - build.group { - {"declare", names} - @transform.statement value, (stm) -> - if types.is_value stm - {"assign", names, {stm}} - else - stm - } - - transformed or node - - continue: (node) => - continue_name = @send "continue" - error "continue must be inside of a loop" unless continue_name - build.group { - build.assign_one continue_name, "true" - {"break"} - } - - export: (node) => - -- assign values if they are included - if #node > 2 - if node[2] == "class" - cls = smart_node node[3] - build.group { - {"export", {cls.name}} - cls - } - else - build.group { - node - build.assign { - names: node[2] - values: node[3] - } - } - else - nil - - update: (node) => - _, name, op, exp = unpack node - op_final = op\match "^(.+)=$" - error "Unknown op: "..op if not op_final - exp = {"parens", exp} unless value_is_singular exp - build.assign_one name, {"exp", name, op_final, exp} - - import: (node) => - _, names, source = unpack node - - stubs = for name in *names - if type(name) == "table" - name - else - {"dot", name} - - real_names = for name in *names - type(name) == "table" and name[2] or name - - if type(source) == "string" - build.assign { - names: real_names - values: [build.chain { base: source, stub} for stub in *stubs] - } - else - source_name = NameProxy "table" - build.group { - {"declare", real_names} - build["do"] { - build.assign_one source_name, source - build.assign { - names: real_names - values: [build.chain { base: source_name, stub} for stub in *stubs] - } - } - } - - comprehension: (node, action) => - _, exp, clauses = unpack node - - action = action or (exp) -> {exp} - construct_comprehension action(exp), clauses - - do: (node, ret) => - node[2] = apply_to_last node[2], ret if ret - node - - decorated: (node) => - stm, dec = unpack node, 2 - - wrapped = switch dec[1] - when "if" - cond, fail = unpack dec, 2 - fail = { "else", { fail } } if fail - { "if", cond, { stm }, fail } - when "unless" - { "unless", dec[2], { stm } } - when "comprehension" - { "comprehension", stm, dec[2] } - else - error "Unknown decorator " .. dec[1] - - if ntype(stm) == "assign" - wrapped = build.group { - build.declare names: [name for name in *stm[2] when type(name) == "string"] - wrapped - } - - wrapped - - unless: (node) => - { "if", {"not", {"parens", node[2]}}, unpack node, 3 } - - if: (node, ret) => - -- expand assign in cond - if ntype(node[2]) == "assign" - _, assign, body = unpack node - name = assign[2][1] - return build["do"] { - assign - {"if", name, unpack node, 3} - } - - node = expand_elseif_assign node - - -- apply cascading return decorator - if ret - smart_node node - -- mutate all the bodies - node['then'] = apply_to_last node['then'], ret - for i = 4, #node - case = node[i] - body_idx = #node[i] - case[body_idx] = apply_to_last case[body_idx], ret - - node - - with: (node, ret) => - _, exp, block = unpack node - scope_name = NameProxy "with" - build["do"] { - build.assign_one scope_name, exp - Run => @set "scope_var", scope_name - build.group block - if ret - ret scope_name - } - - foreach: (node) => - smart_node node - source = unpack node.iter - - if ntype(source) == "unpack" - list = source[2] - - index_name = NameProxy "index" - list_name = NameProxy "list" - - slice_var = nil - bounds = if is_slice list - slice = list[#list] - table.remove list - table.remove slice, 1 - - slice[2] = if slice[2] and slice[2] != "" - max_tmp_name = NameProxy "max" - slice_var = build.assign_one max_tmp_name, slice[2] - {"exp", max_tmp_name, "<", 0 - "and", {"length", list_name}, "+", max_tmp_name - "or", max_tmp_name } - else - {"length", list_name} - - slice - else - {1, {"length", list_name}} - - return build.group { - build.assign_one list_name, list - slice_var - build["for"] { - name: index_name - bounds: bounds - body: { - {"assign", node.names, {list_name\index index_name}} - build.group node.body - } - } - } - - node.body = with_continue_listener node.body - - while: (node) => - smart_node node - node.body = with_continue_listener node.body - - for: (node) => - smart_node node - node.body = with_continue_listener node.body - - switch: (node, ret) => - _, exp, conds = unpack node - exp_name = NameProxy "exp" - - -- convert switch conds into if statment conds - convert_cond = (cond) -> - t, case_exp, body = unpack cond - out = {} - insert out, t == "case" and "elseif" or "else" - if t != "else" - insert out, {"exp", case_exp, "==", exp_name} if t != "else" - else - body = case_exp - - if ret - body = apply_to_last body, ret - - insert out, body - - out - - first = true - if_stm = {"if"} - for cond in *conds - if_cond = convert_cond cond - if first - first = false - insert if_stm, if_cond[2] - insert if_stm, if_cond[3] - else - insert if_stm, if_cond - - build.group { - build.assign_one exp_name, exp - if_stm - } - - class: (node) => - _, name, parent_val, body = unpack node - - -- split apart properties and statements - statements = {} - properties = {} - for item in *body - switch item[1] - when "stm" - insert statements, item[2] - when "props" - for tuple in *item[2,] - if ntype(tuple[1]) == "self" - insert statements, build.assign_one unpack tuple - else - insert properties, tuple - - -- find constructor - constructor = nil - properties = for tuple in *properties - key = tuple[1] - if key[1] == "key_literal" and key[2] == constructor_name - constructor = tuple[2] - nil - else - tuple - - parent_cls_name = NameProxy "parent" - base_name = NameProxy "base" - self_name = NameProxy "self" - cls_name = NameProxy "class" - - if not constructor - constructor = build.fndef { - args: {{"..."}} - arrow: "fat" - body: { - build["if"] { - cond: parent_cls_name - then: { - build.chain { base: "super", {"call", {"..."}} } - } - } - } - } - else - smart_node constructor - constructor.arrow = "fat" - - cls = build.table { - {"__init", constructor} - {"__base", base_name} - {"__name", {"string", '"', name}} -- "quote the string" - {"__parent", parent_cls_name} - } - - -- look up a name in the class object - class_lookup = build["if"] { - cond: {"exp", "val", "==", "nil", "and", parent_cls_name} - then: { - parent_cls_name\index"name" - } - } - insert class_lookup, {"else", {"val"}} - - cls_mt = build.table { - {"__index", build.fndef { - args: {{"cls"}, {"name"}} - body: { - build.assign_one LocalName"val", build.chain { - base: "rawget", {"call", {base_name, "name"}} - } - class_lookup - } - }} - {"__call", build.fndef { - args: {{"cls"}, {"..."}} - body: { - build.assign_one self_name, build.chain { - base: "setmetatable" - {"call", {"{}", base_name}} - } - build.chain { - base: "cls.__init" - {"call", {self_name, "..."}} - } - self_name - } - }} - } - - cls = build.chain { - base: "setmetatable" - {"call", {cls, cls_mt}} - } - - value = nil - with build - out_body = { - Run => - @set "super", (block, chain) -> - if chain - slice = [item for item in *chain[3,]] - new_chain = {"chain", parent_cls_name} - - head = slice[1] - - if head == nil - return parent_cls_name - - switch head[1] - -- calling super, inject calling name and self into chain - when "call" - calling_name = block\get"current_block" - slice[1] = {"call", {"self", unpack head[2]}} - - if ntype(calling_name) == "key_literal" - insert new_chain, {"dot", calling_name[2]} - else - insert new_chain, {"index", calling_name} - - -- colon call on super, replace class with self as first arg - when "colon" - call = head[3] - insert new_chain, {"dot", head[2]} - slice[1] = { "call", { "self", unpack call[2] } } - - insert new_chain, item for item in *slice - - new_chain - else - parent_cls_name - - .assign_one parent_cls_name, parent_val == "" and "nil" or parent_val - .assign_one base_name, {"table", properties} - .assign_one base_name\chain"__index", base_name - - build["if"] { - cond: parent_cls_name - then: { - .chain { - base: "setmetatable" - {"call", { - base_name, - .chain { base: parent_cls_name, {"dot", "__base"}} - }} - } - } - } - - .assign_one cls_name, cls - .assign_one base_name\chain"__class", cls_name - - .group if #statements > 0 { - .assign_one LocalName"self", cls_name - .group statements - } else {} - - -- run the inherited callback - build["if"] { - cond: {"exp", - parent_cls_name, "and", parent_cls_name\chain "__inherited" - } - then: { - parent_cls_name\chain "__inherited", {"call", { - parent_cls_name, cls_name - }} - } - } - - cls_name - } - - hoist_declarations out_body - - value = .group { - .declare names: {name} - .assign { - names: {name} - values: {.block_exp out_body} - } - } - - value +{ + Statement: require "moonscript.transform.statement" + Value: require "moonscript.transform.value" } - -class Accumulator - body_idx: { for: 4, while: 3, foreach: 4 } - - new: => - @accum_name = NameProxy "accum" - @value_name = NameProxy "value" - @len_name = NameProxy "len" - - -- wraps node and mutates body - convert: (node) => - index = @body_idx[ntype node] - node[index] = @mutate_body node[index] - @wrap node - - -- wrap the node into a block_exp - wrap: (node) => - build.block_exp { - build.assign_one @accum_name, build.table! - build.assign_one @len_name, 0 - node - @accum_name - } - - -- mutates the body of a loop construct to save last value into accumulator - -- can optionally skip nil results - mutate_body: (body, skip_nil=true) => - val = if not skip_nil and is_singular body - with body[1] - body = {} - else - body = apply_to_last body, (n) -> - build.assign_one @value_name, n - @value_name - - update = { - {"update", @len_name, "+=", 1} - build.assign_one @accum_name\index(@len_name), val - } - - if skip_nil - table.insert body, build["if"] { - cond: {"exp", @value_name, "!=", "nil"} - then: update - } - else - table.insert body, build.group update - - body - -default_accumulator = (node) => - Accumulator!\convert node - - -implicitly_return = (scope) -> - is_top = true - fn = (stm) -> - t = ntype stm - - -- expand decorated - if t == "decorated" - stm = scope.transform.statement stm - t = ntype stm - - if types.manual_return[t] or not types.is_value stm - -- remove blank return statement - if is_top and t == "return" and stm[2] == "" - nil - else - stm - elseif types.cascading[t] - is_top = false - scope.transform.statement stm, fn - else - if t == "comprehension" and not types.comprehension_has_value stm - stm - else - {"return", stm} - - fn - -Value = Transformer { - for: default_accumulator - while: default_accumulator - foreach: default_accumulator - - decorated: (node) => - @transform.statement node - - string: (node) => - delim = node[2] - - convert_part = (part) -> - if type(part) == "string" or part == nil - {"string", delim, part or ""} - else - build.chain { base: "tostring", {"call", {part[2]}} } - - -- reduced to single item - if #node <= 3 - return if type(node[3]) == "string" - node - else - convert_part node[3] - - e = {"exp", convert_part node[3]} - - for i=4, #node - insert e, ".." - insert e, convert_part node[i] - e - - comprehension: (node) => - a = Accumulator! - node = @transform.statement node, (exp) -> - a\mutate_body {exp}, false - a\wrap node - - tblcomprehension: (node) => - _, explist, clauses = unpack node - key_exp, value_exp = unpack explist - - accum = NameProxy "tbl" - - inner = if value_exp - dest = build.chain { base: accum, {"index", key_exp} } - { build.assign_one dest, value_exp } - else - -- If we only have single expression then - -- unpack the result into key and value - key_name, val_name = NameProxy"key", NameProxy"val" - dest = build.chain { base: accum, {"index", key_name} } - { - build.assign names: {key_name, val_name}, values: {key_exp} - build.assign_one dest, val_name - } - - build.block_exp { - build.assign_one accum, build.table! - construct_comprehension inner, clauses - accum - } - - fndef: (node) => - smart_node node - node.body = apply_to_last node.body, implicitly_return self - node.body = { - Run => @listen "varargs", -> -- capture event - unpack node.body - } - - node - - if: (node) => build.block_exp { node } - unless: (node) =>build.block_exp { node } - with: (node) => build.block_exp { node } - switch: (node) => - build.block_exp { node } - - -- pull out colon chain - chain: (node) => - stub = node[#node] - - -- escape lua keywords used in dot accessors - for i=3,#node - part = node[i] - if ntype(part) == "dot" and data.lua_keywords[part[2]] - node[i] = { "index", {"string", '"', part[2]} } - - if ntype(node[2]) == "string" - -- add parens if callee is raw string - node[2] = {"parens", node[2] } - elseif type(stub) == "table" and stub[1] == "colon_stub" - -- convert colon stub into code - table.remove node, #node - - base_name = NameProxy "base" - fn_name = NameProxy "fn" - - is_super = node[2] == "super" - @transform.value build.block_exp { - build.assign { - names: {base_name} - values: {node} - } - - build.assign { - names: {fn_name} - values: { - build.chain { base: base_name, {"dot", stub[2]} } - } - } - - build.fndef { - args: {{"..."}} - body: { - build.chain { - base: fn_name, {"call", {is_super and "self" or base_name, "..."}} - } - } - } - } - - block_exp: (node) => - _, body = unpack node - - fn = nil - arg_list = {} - - fn = smart_node build.fndef body: { - Run => - @listen "varargs", -> - insert arg_list, "..." - insert fn.args, {"..."} - @unlisten "varargs" - - unpack body - } - - build.chain { base: {"parens", fn}, {"call", arg_list} } -} - diff --git a/moonscript/transform/accumulator.lua b/moonscript/transform/accumulator.lua new file mode 100644 index 00000000..539c65fd --- /dev/null +++ b/moonscript/transform/accumulator.lua @@ -0,0 +1,110 @@ +local types = require("moonscript.types") +local build, ntype, NOOP +build, ntype, NOOP = types.build, types.ntype, types.NOOP +local NameProxy +NameProxy = require("moonscript.transform.names").NameProxy +local insert +insert = table.insert +local is_singular +is_singular = function(body) + if #body ~= 1 then + return false + end + if "group" == ntype(body) then + return is_singular(body[2]) + else + return body[1] + end +end +local transform_last_stm +transform_last_stm = require("moonscript.transform.statements").transform_last_stm +local Accumulator +do + local _class_0 + local _base_0 = { + body_idx = { + ["for"] = 4, + ["while"] = 3, + foreach = 4 + }, + convert = function(self, node) + local index = self.body_idx[ntype(node)] + node[index] = self:mutate_body(node[index]) + return self:wrap(node) + end, + wrap = function(self, node, group_type) + if group_type == nil then + group_type = "block_exp" + end + return build[group_type]({ + build.assign_one(self.accum_name, build.table()), + build.assign_one(self.len_name, 1), + node, + group_type == "block_exp" and self.accum_name or NOOP + }) + end, + mutate_body = function(self, body) + local single_stm = is_singular(body) + local val + if single_stm and types.is_value(single_stm) then + body = { } + val = single_stm + else + body = transform_last_stm(body, function(n) + if types.is_value(n) then + return build.assign_one(self.value_name, n) + else + return build.group({ + { + "declare", + { + self.value_name + } + }, + n + }) + end + end) + val = self.value_name + end + local update = { + build.assign_one(NameProxy.index(self.accum_name, self.len_name), val), + { + "update", + self.len_name, + "+=", + 1 + } + } + insert(body, build.group(update)) + return body + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function(self, accum_name) + self.accum_name = NameProxy("accum") + self.value_name = NameProxy("value") + self.len_name = NameProxy("len") + end, + __base = _base_0, + __name = "Accumulator" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + Accumulator = _class_0 +end +local default_accumulator +default_accumulator = function(self, node) + return Accumulator():convert(node) +end +return { + Accumulator = Accumulator, + default_accumulator = default_accumulator +} diff --git a/moonscript/transform/accumulator.moon b/moonscript/transform/accumulator.moon new file mode 100644 index 00000000..62ff015c --- /dev/null +++ b/moonscript/transform/accumulator.moon @@ -0,0 +1,71 @@ +types = require "moonscript.types" + +import build, ntype, NOOP from types +import NameProxy from require "moonscript.transform.names" + +import insert from table + +-- is a body a single expression/statement +is_singular = (body) -> + return false if #body != 1 + if "group" == ntype body + is_singular body[2] + else + body[1] + +import transform_last_stm from require "moonscript.transform.statements" + +class Accumulator + body_idx: { for: 4, while: 3, foreach: 4 } + + new: (accum_name) => + @accum_name = NameProxy "accum" + @value_name = NameProxy "value" + @len_name = NameProxy "len" + + -- wraps node and mutates body + convert: (node) => + index = @body_idx[ntype node] + node[index] = @mutate_body node[index] + @wrap node + + -- wrap the node into a block_exp + wrap: (node, group_type="block_exp") => + build[group_type] { + build.assign_one @accum_name, build.table! + build.assign_one @len_name, 1 + node + group_type == "block_exp" and @accum_name or NOOP + } + + -- mutates the body of a loop construct to save last value into accumulator + mutate_body: (body) => + -- shortcut to write simpler code if body is a single expression + single_stm = is_singular body + val = if single_stm and types.is_value single_stm + body = {} + single_stm + else + body = transform_last_stm body, (n) -> + if types.is_value n + build.assign_one @value_name, n + else + -- just ignore it + build.group { + {"declare", {@value_name}} + n + } + @value_name + + update = { + build.assign_one NameProxy.index(@accum_name, @len_name), val + {"update", @len_name, "+=", 1} + } + + insert body, build.group update + body + +default_accumulator = (node) => + Accumulator!\convert node + +{ :Accumulator, :default_accumulator } diff --git a/moonscript/transform/class.lua b/moonscript/transform/class.lua new file mode 100644 index 00000000..6c71a2f2 --- /dev/null +++ b/moonscript/transform/class.lua @@ -0,0 +1,497 @@ +local NameProxy, LocalName +do + local _obj_0 = require("moonscript.transform.names") + NameProxy, LocalName = _obj_0.NameProxy, _obj_0.LocalName +end +local Run +Run = require("moonscript.transform.statements").Run +local CONSTRUCTOR_NAME = "new" +local insert +insert = table.insert +local build, ntype, NOOP +do + local _obj_0 = require("moonscript.types") + build, ntype, NOOP = _obj_0.build, _obj_0.ntype, _obj_0.NOOP +end +local unpack +unpack = require("moonscript.util").unpack +local transform_super +transform_super = function(cls_name, on_base, block, chain) + if on_base == nil then + on_base = true + end + local relative_parent = { + "chain", + cls_name, + { + "dot", + "__parent" + } + } + if not (chain) then + return relative_parent + end + local chain_tail = { + unpack(chain, 3) + } + local head = chain_tail[1] + if head == nil then + return relative_parent + end + local new_chain = relative_parent + local _exp_0 = head[1] + if "call" == _exp_0 then + if on_base then + insert(new_chain, { + "dot", + "__base" + }) + end + local calling_name = block:get("current_method") + assert(calling_name, "missing calling name") + chain_tail[1] = { + "call", + { + "self", + unpack(head[2]) + } + } + if ntype(calling_name) == "key_literal" then + insert(new_chain, { + "dot", + calling_name[2] + }) + else + insert(new_chain, { + "index", + calling_name + }) + end + elseif "colon" == _exp_0 then + local call = chain_tail[2] + if call and call[1] == "call" then + chain_tail[1] = { + "dot", + head[2] + } + chain_tail[2] = { + "call", + { + "self", + unpack(call[2]) + } + } + end + end + for _index_0 = 1, #chain_tail do + local item = chain_tail[_index_0] + insert(new_chain, item) + end + return new_chain +end +local super_scope +super_scope = function(value, t, key) + local prev_method + return { + "scoped", + Run(function(self) + prev_method = self:get("current_method") + self:set("current_method", key) + return self:set("super", t) + end), + value, + Run(function(self) + return self:set("current_method", prev_method) + end) + } +end +return function(self, node, ret, parent_assign) + local name, parent_val, body = unpack(node, 2) + if parent_val == "" then + parent_val = nil + end + local parent_cls_name = NameProxy("parent") + local base_name = NameProxy("base") + local self_name = NameProxy("self") + local cls_name = NameProxy("class") + local cls_instance_super + cls_instance_super = function(...) + return transform_super(cls_name, true, ...) + end + local cls_super + cls_super = function(...) + return transform_super(cls_name, false, ...) + end + local statements = { } + local properties = { } + local hoisted_locals = { } + for _index_0 = 1, #body do + local item = body[_index_0] + local _exp_0 = item[1] + if "stm" == _exp_0 then + local stm = item[2] + if ntype(stm) == "declare_with_shadows" then + insert(hoisted_locals, stm) + else + insert(statements, stm) + end + elseif "props" == _exp_0 then + for _index_1 = 2, #item do + local tuple = item[_index_1] + if ntype(tuple[1]) == "self" then + local k, v + k, v = tuple[1], tuple[2] + v = super_scope(v, cls_super, { + "key_literal", + k[2] + }) + insert(statements, build.assign_one(k, v)) + else + insert(properties, tuple) + end + end + end + end + local constructor + do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #properties do + local _continue_0 = false + repeat + local tuple = properties[_index_0] + local key = tuple[1] + local _value_0 + if key[1] == "key_literal" and key[2] == CONSTRUCTOR_NAME then + constructor = tuple[2] + _continue_0 = true + break + else + local val + key, val = tuple[1], tuple[2] + _value_0 = { + key, + super_scope(val, cls_instance_super, key) + } + end + _accum_0[_len_0] = _value_0 + _len_0 = _len_0 + 1 + _continue_0 = true + until true + if not _continue_0 then + break + end + end + properties = _accum_0 + end + if not (constructor) then + if parent_val then + constructor = build.fndef({ + args = { + { + "..." + } + }, + arrow = "fat", + body = { + build.chain({ + base = "super", + { + "call", + { + "..." + } + } + }) + } + }) + else + constructor = build.fndef() + end + end + local real_name = name or parent_assign and parent_assign[2][1] + local _exp_0 = ntype(real_name) + if "chain" == _exp_0 then + local last = real_name[#real_name] + local _exp_1 = ntype(last) + if "dot" == _exp_1 then + real_name = { + "string", + '"', + last[2] + } + elseif "index" == _exp_1 then + real_name = last[2] + else + real_name = "nil" + end + elseif "nil" == _exp_0 then + real_name = "nil" + else + local name_t = type(real_name) + local flattened_name + if name_t == "string" then + flattened_name = real_name + elseif name_t == "table" and real_name[1] == "ref" then + flattened_name = real_name[2] + else + flattened_name = error("don't know how to extract name from " .. tostring(name_t)) + end + real_name = { + "string", + '"', + flattened_name + } + end + local cls = build.table({ + { + "__init", + super_scope(constructor, cls_super, { + "key_literal", + "__init" + }) + }, + { + "__base", + base_name + }, + { + "__name", + real_name + }, + parent_val and { + "__parent", + parent_cls_name + } or nil + }) + local class_index + if parent_val then + local class_lookup = build["if"]({ + cond = { + "exp", + { + "ref", + "val" + }, + "==", + "nil" + }, + ["then"] = { + build.assign_one(LocalName("parent"), build.chain({ + base = "rawget", + { + "call", + { + { + "ref", + "cls" + }, + { + "string", + '"', + "__parent" + } + } + } + })), + build["if"]({ + cond = LocalName("parent"), + ["then"] = { + build.chain({ + base = LocalName("parent"), + { + "index", + "name" + } + }) + } + }) + } + }) + insert(class_lookup, { + "else", + { + "val" + } + }) + class_index = build.fndef({ + args = { + { + "cls" + }, + { + "name" + } + }, + body = { + build.assign_one(LocalName("val"), build.chain({ + base = "rawget", + { + "call", + { + base_name, + { + "ref", + "name" + } + } + } + })), + class_lookup + } + }) + else + class_index = base_name + end + local cls_mt = build.table({ + { + "__index", + class_index + }, + { + "__call", + build.fndef({ + args = { + { + "cls" + }, + { + "..." + } + }, + body = { + build.assign_one(self_name, build.chain({ + base = "setmetatable", + { + "call", + { + "{}", + base_name + } + } + })), + build.chain({ + base = "cls.__init", + { + "call", + { + self_name, + "..." + } + } + }), + self_name + } + }) + } + }) + cls = build.chain({ + base = "setmetatable", + { + "call", + { + cls, + cls_mt + } + } + }) + local value = nil + do + local out_body = { + Run(function(self) + if name then + return self:put_name(name) + end + end), + { + "declare", + { + cls_name + } + }, + parent_val and build.assign_one(parent_cls_name, parent_val) or NOOP, + build.group((function() + if #hoisted_locals > 0 then + return hoisted_locals + end + end)()), + { + "declare_glob", + "*" + }, + build.assign_one(base_name, { + "table", + properties + }), + build.assign_one(base_name:chain("__index"), base_name), + parent_val and build.chain({ + base = "setmetatable", + { + "call", + { + base_name, + build.chain({ + base = parent_cls_name, + { + "dot", + "__base" + } + }) + } + } + }) or NOOP, + build.assign_one(cls_name, cls), + build.assign_one(base_name:chain("__class"), cls_name), + build.group((function() + if #statements > 0 then + return { + build.assign_one(LocalName("self"), cls_name), + build.group(statements) + } + end + end)()), + parent_val and build["if"]({ + cond = { + "exp", + parent_cls_name:chain("__inherited") + }, + ["then"] = { + parent_cls_name:chain("__inherited", { + "call", + { + parent_cls_name, + cls_name + } + }) + } + }) or NOOP, + build.group((function() + if name then + return { + build.assign_one(name, cls_name) + } + end + end)()), + (function() + if ret then + return ret(cls_name) + end + end)() + } + value = build.group({ + build.group((function() + if ntype(name) == "value" then + return { + build.declare({ + names = { + name + } + }) + } + end + end)()), + build["do"](out_body) + }) + end + return value +end diff --git a/moonscript/transform/class.moon b/moonscript/transform/class.moon new file mode 100644 index 00000000..e0278431 --- /dev/null +++ b/moonscript/transform/class.moon @@ -0,0 +1,299 @@ +import NameProxy, LocalName from require "moonscript.transform.names" +import Run from require "moonscript.transform.statements" + +CONSTRUCTOR_NAME = "new" + +import insert from table +import build, ntype, NOOP from require "moonscript.types" +import unpack from require "moonscript.util" + +transform_super = (cls_name, on_base=true, block, chain) -> + relative_parent = { + "chain", + cls_name + {"dot", "__parent"} + } + + return relative_parent unless chain + + chain_tail = { unpack chain, 3 } + head = chain_tail[1] + + if head == nil + return relative_parent + + new_chain = relative_parent + + switch head[1] + -- calling super, inject calling name and self into chain + when "call" + if on_base + insert new_chain, {"dot", "__base"} + + calling_name = block\get "current_method" + assert calling_name, "missing calling name" + chain_tail[1] = {"call", {"self", unpack head[2]}} + + if ntype(calling_name) == "key_literal" + insert new_chain, {"dot", calling_name[2]} + else + insert new_chain, {"index", calling_name} + + -- colon call on super, replace class with self as first arg + when "colon" + call = chain_tail[2] + -- calling chain tail + if call and call[1] == "call" + chain_tail[1] = { + "dot" + head[2] + } + + chain_tail[2] = { + "call" + { + "self" + unpack call[2] + } + } + + insert new_chain, item for item in *chain_tail + new_chain + + +super_scope = (value, t, key) -> + local prev_method + + { + "scoped", + Run => + prev_method = @get "current_method" + @set "current_method", key + @set "super", t + value + Run => + @set "current_method", prev_method + } + +(node, ret, parent_assign) => + name, parent_val, body = unpack node, 2 + parent_val = nil if parent_val == "" + + parent_cls_name = NameProxy "parent" + base_name = NameProxy "base" + self_name = NameProxy "self" + cls_name = NameProxy "class" + + -- super call on instance + cls_instance_super = (...) -> transform_super cls_name, true, ... + + -- super call on parent class + cls_super = (...) -> transform_super cls_name, false, ... + + -- split apart properties and statements + statements = {} + properties = {} + + -- local declarations are hoisted to the top of the class scope so methods + -- can close over the names + hoisted_locals = {} + + for item in *body + switch item[1] + when "stm" + stm = item[2] + if ntype(stm) == "declare_with_shadows" + insert hoisted_locals, stm + else + insert statements, stm + when "props" + for tuple in *item[2,] + if ntype(tuple[1]) == "self" + {k,v} = tuple + v = super_scope v, cls_super, {"key_literal", k[2]} + insert statements, build.assign_one k, v + else + insert properties, tuple + + -- find constructor + local constructor + properties = for tuple in *properties + key = tuple[1] + if key[1] == "key_literal" and key[2] == CONSTRUCTOR_NAME + constructor = tuple[2] + continue + else + {key, val} = tuple + {key, super_scope val, cls_instance_super, key} + + + unless constructor + constructor = if parent_val + build.fndef { + args: {{"..."}} + arrow: "fat" + body: { + build.chain { base: "super", {"call", {"..."}} } + } + } + else + build.fndef! + + real_name = name or parent_assign and parent_assign[2][1] + real_name = switch ntype real_name + when "chain" + last = real_name[#real_name] + switch ntype last + when "dot" + {"string", '"', last[2]} + when "index" + last[2] + else + "nil" + when "nil" + "nil" + else + name_t = type real_name + -- TODO: don't use string literal as ref + flattened_name = if name_t == "string" + real_name + elseif name_t == "table" and real_name[1] == "ref" + real_name[2] + else + error "don't know how to extract name from #{name_t}" + + {"string", '"', flattened_name} + + cls = build.table { + {"__init", super_scope constructor, cls_super, {"key_literal", "__init"}} + {"__base", base_name} + {"__name", real_name} -- "quote the string" + parent_val and {"__parent", parent_cls_name} or nil + } + + -- looking up a name in the class object + class_index = if parent_val + class_lookup = build["if"] { + cond: { "exp", {"ref", "val"}, "==", "nil" } + then: { + build.assign_one LocalName"parent", build.chain { + base: "rawget" + { + "call", { + {"ref", "cls"} + {"string", '"', "__parent"} + } + } + } + + build.if { + cond: LocalName "parent" + then: { + build.chain { + base: LocalName "parent" + {"index", "name"} + } + } + } + } + } + insert class_lookup, {"else", {"val"}} + + build.fndef { + args: {{"cls"}, {"name"}} + body: { + build.assign_one LocalName"val", build.chain { + base: "rawget", {"call", {base_name, {"ref", "name"}}} + } + class_lookup + } + } + else + base_name + + cls_mt = build.table { + {"__index", class_index} + {"__call", build.fndef { + args: {{"cls"}, {"..."}} + body: { + build.assign_one self_name, build.chain { + base: "setmetatable" + {"call", {"{}", base_name}} + } + build.chain { + base: "cls.__init" + {"call", {self_name, "..."}} + } + self_name + } + }} + } + + cls = build.chain { + base: "setmetatable" + {"call", {cls, cls_mt}} + } + + value = nil + with build + out_body = { + Run => + -- make sure we don't assign the class to a local inside the do + @put_name name if name + + {"declare", { cls_name }} + + -- the parent expression is evaluated before the local shadows are + -- installed so it can reference a name a class body local overrides + parent_val and .assign_one(parent_cls_name, parent_val) or NOOP + + .group if #hoisted_locals > 0 then hoisted_locals + + {"declare_glob", "*"} + + .assign_one base_name, {"table", properties} + .assign_one base_name\chain"__index", base_name + + parent_val and .chain({ + base: "setmetatable" + {"call", { + base_name, + .chain { base: parent_cls_name, {"dot", "__base"}} + }} + }) or NOOP + + .assign_one cls_name, cls + .assign_one base_name\chain"__class", cls_name + + .group if #statements > 0 then { + .assign_one LocalName"self", cls_name + .group statements + } + + -- run the inherited callback + parent_val and .if({ + cond: {"exp", parent_cls_name\chain "__inherited" } + then: { + parent_cls_name\chain "__inherited", {"call", { + parent_cls_name, cls_name + }} + } + }) or NOOP + + .group if name then { + .assign_one name, cls_name + } + + if ret + ret cls_name + } + + value = .group { + .group if ntype(name) == "value" then { + .declare names: {name} + } + + .do out_body + } + + value diff --git a/moonscript/transform/comprehension.lua b/moonscript/transform/comprehension.lua new file mode 100644 index 00000000..da33892d --- /dev/null +++ b/moonscript/transform/comprehension.lua @@ -0,0 +1,54 @@ +local is_value +is_value = require("moonscript.types").is_value +local construct_comprehension +construct_comprehension = function(inner, clauses) + local current_stms = inner + for i = #clauses, 1, -1 do + local clause = clauses[i] + local t = clause[1] + local _exp_0 = t + if "for" == _exp_0 then + local _, name, bounds + _, name, bounds = clause[1], clause[2], clause[3] + current_stms = { + "for", + name, + bounds, + current_stms + } + elseif "foreach" == _exp_0 then + local _, names, iter + _, names, iter = clause[1], clause[2], clause[3] + current_stms = { + "foreach", + names, + { + iter + }, + current_stms + } + elseif "when" == _exp_0 then + local _, cond + _, cond = clause[1], clause[2] + current_stms = { + "if", + cond, + current_stms + } + else + current_stms = error("Unknown comprehension clause: " .. t) + end + current_stms = { + current_stms + } + end + return current_stms[1] +end +local comprehension_has_value +comprehension_has_value = function(comp) + return is_value(comp[2]) +end +return { + construct_comprehension = construct_comprehension, + comprehension_has_value = comprehension_has_value +} diff --git a/moonscript/transform/comprehension.moon b/moonscript/transform/comprehension.moon new file mode 100644 index 00000000..a181a891 --- /dev/null +++ b/moonscript/transform/comprehension.moon @@ -0,0 +1,30 @@ + +import is_value from require "moonscript.types" + +construct_comprehension = (inner, clauses) -> + current_stms = inner + for i=#clauses,1,-1 + clause = clauses[i] + t = clause[1] + + current_stms = switch t + when "for" + {_, name, bounds} = clause + {"for", name, bounds, current_stms} + when "foreach" + {_, names, iter} = clause + {"foreach", names, {iter}, current_stms} + when "when" + {_, cond} = clause + {"if", cond, current_stms} + else + error "Unknown comprehension clause: "..t + + current_stms = {current_stms} + + current_stms[1] + +comprehension_has_value = (comp) -> + is_value comp[2] + +{:construct_comprehension, :comprehension_has_value} diff --git a/moonscript/transform/destructure.lua b/moonscript/transform/destructure.lua new file mode 100644 index 00000000..5f1e35a0 --- /dev/null +++ b/moonscript/transform/destructure.lua @@ -0,0 +1,364 @@ +local ntype, mtype, build, is_assignable +do + local _obj_0 = require("moonscript.types") + ntype, mtype, build, is_assignable = _obj_0.ntype, _obj_0.mtype, _obj_0.build, _obj_0.is_assignable +end +local NameProxy +NameProxy = require("moonscript.transform.names").NameProxy +local insert +insert = table.insert +local unpack +unpack = require("moonscript.util").unpack +local user_error +user_error = require("moonscript.errors").user_error +local join +join = function(...) + do + local out = { } + local i = 1 + local _list_0 = { + ... + } + for _index_0 = 1, #_list_0 do + local tbl = _list_0[_index_0] + for _index_1 = 1, #tbl do + local v = tbl[_index_1] + out[i] = v + i = i + 1 + end + end + return out + end +end +local has_destructure +has_destructure = function(names) + for _index_0 = 1, #names do + local n = names[_index_0] + if ntype(n) == "table" then + return true + end + end + return false +end +local is_multi_return +is_multi_return = function(node) + if node == "..." then + return true + end + if not (type(node) == "table") then + return false + end + local _exp_0 = node[1] + if "chain" == _exp_0 then + return ntype(node[#node]) == "call" + elseif "explist" == _exp_0 then + return true + else + return false + end +end +local extract_assign_names +extract_assign_names = function(name, accum, prefix) + if accum == nil then + accum = { } + end + if prefix == nil then + prefix = { } + end + local i = 1 + local _list_0 = name[2] + for _index_0 = 1, #_list_0 do + local tuple = _list_0[_index_0] + local value, suffix + if #tuple == 1 then + local s = { + "index", + { + "number", + i + } + } + i = i + 1 + value, suffix = tuple[1], s + else + local key = tuple[1] + local s + if ntype(key) == "key_literal" then + local key_name = key[2] + if ntype(key_name) == "colon" then + s = key_name + else + s = { + "dot", + key_name + } + end + else + s = { + "index", + key + } + end + value, suffix = tuple[2], s + end + suffix = join(prefix, { + suffix + }) + if ntype(value) == "table" then + extract_assign_names(value, accum, suffix) + elseif is_assignable(value) then + insert(accum, { + value, + suffix + }) + else + local pos = type(value) == "table" and value[-1] or name[-1] + if value == "..." then + user_error("Can't destructure into '...'", pos) + elseif ntype(value) == "chain" then + user_error("Can't destructure into chain ending in " .. tostring(ntype(value[#value])), pos) + else + user_error("Can't destructure value of type: " .. tostring(ntype(value)), pos) + end + end + end + return accum +end +local keyword_refs = { + ["nil"] = true, + ["true"] = true, + ["false"] = true +} +local chainable_receiver +chainable_receiver = function(node) + local _exp_0 = type(node) + if "string" == _exp_0 then + return node ~= "..." and not keyword_refs[node] + elseif "table" == _exp_0 then + local _exp_1 = node[1] + if "ref" == _exp_1 then + return not keyword_refs[node[2]] + elseif "chain" == _exp_1 or "self" == _exp_1 or "self_class" == _exp_1 or "temp_name" == _exp_1 or "parens" == _exp_1 or "exp" == _exp_1 or "table" == _exp_1 or "string" == _exp_1 then + return true + else + return false + end + else + return false + end +end +local build_assign +build_assign = function(scope, destruct_literal, receiver, opts) + assert(receiver, "attempting to build destructure assign with no receiver") + local extracted_names = extract_assign_names(destruct_literal) + local names = { } + local values = { } + local inner = { + "assign", + names, + values + } + local obj + if chainable_receiver(receiver) and (scope:is_local(receiver) or #extracted_names == 1) then + obj = receiver + else + do + obj = NameProxy("obj") + inner = build["do"]({ + build.assign_one(obj, receiver), + { + "assign", + names, + values + } + }) + obj = obj + end + end + for _index_0 = 1, #extracted_names do + local tuple = extracted_names[_index_0] + insert(names, tuple[1]) + local chain + if obj then + chain = NameProxy.chain(obj, unpack(tuple[2])) + else + chain = "nil" + end + insert(values, chain) + end + local group = { } + if opts and opts.shadow then + local shadow_names + do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #names do + local name = names[_index_0] + if ntype(name) == "ref" or type(name) == "string" then + _accum_0[_len_0] = name + _len_0 = _len_0 + 1 + end + end + shadow_names = _accum_0 + end + if next(shadow_names) then + insert(group, { + "declare_with_shadows", + shadow_names + }) + end + else + insert(group, { + "declare", + names + }) + end + insert(group, inner) + return build.group(group) +end +local split_assign +split_assign = function(scope, assign) + local names, values = unpack(assign, 2) + local g = { } + local total_names = #names + local total_values = #values + local suffix_start + if total_names > total_values and is_multi_return(values[total_values]) then + for i = total_values, total_names do + if ntype(names[i]) == "table" then + suffix_start = total_values + break + end + end + end + local start = 1 + local stop = suffix_start and suffix_start - 1 or total_names + for i = 1, stop do + local n = names[i] + if ntype(n) == "table" then + if i > start then + insert(g, { + "assign", + (function() + local _accum_0 = { } + local _len_0 = 1 + for j = start, i - 1 do + _accum_0[_len_0] = names[j] + _len_0 = _len_0 + 1 + end + return _accum_0 + end)(), + (function() + local _accum_0 = { } + local _len_0 = 1 + for j = start, i - 1 do + _accum_0[_len_0] = values[j] + _len_0 = _len_0 + 1 + end + return _accum_0 + end)() + }) + end + insert(g, build_assign(scope, n, values[i])) + start = i + 1 + end + end + if suffix_start then + if start <= stop then + insert(g, { + "assign", + (function() + local _accum_0 = { } + local _len_0 = 1 + for j = start, stop do + _accum_0[_len_0] = names[j] + _len_0 = _len_0 + 1 + end + return _accum_0 + end)(), + (function() + local _accum_0 = { } + local _len_0 = 1 + for j = start, stop do + _accum_0[_len_0] = values[j] + _len_0 = _len_0 + 1 + end + return _accum_0 + end)() + }) + end + local suffix_names = { } + local destructures = { } + for i = suffix_start, total_names do + local name = names[i] + if ntype(name) == "table" then + local proxy = NameProxy("destruct") + insert(suffix_names, proxy) + insert(destructures, { + name, + proxy + }) + else + insert(suffix_names, name) + end + end + insert(g, { + "assign", + suffix_names, + { + values[total_values] + } + }) + for _index_0 = 1, #destructures do + local _des_0 = destructures[_index_0] + local literal, proxy + literal, proxy = _des_0[1], _des_0[2] + insert(g, build_assign(scope, literal, proxy)) + end + elseif total_names >= start or total_values >= start then + local name_slice + if total_names < start then + name_slice = { + scope:discard_name() + } + else + do + local _accum_0 = { } + local _len_0 = 1 + for i = start, total_names do + _accum_0[_len_0] = names[i] + _len_0 = _len_0 + 1 + end + name_slice = _accum_0 + end + end + local value_slice + if total_values < start then + value_slice = { + "nil" + } + else + do + local _accum_0 = { } + local _len_0 = 1 + for i = start, total_values do + _accum_0[_len_0] = values[i] + _len_0 = _len_0 + 1 + end + value_slice = _accum_0 + end + end + insert(g, { + "assign", + name_slice, + value_slice + }) + end + return build.group(g) +end +return { + has_destructure = has_destructure, + split_assign = split_assign, + build_assign = build_assign, + extract_assign_names = extract_assign_names +} diff --git a/moonscript/transform/destructure.moon b/moonscript/transform/destructure.moon new file mode 100644 index 00000000..89af26f0 --- /dev/null +++ b/moonscript/transform/destructure.moon @@ -0,0 +1,211 @@ + +import ntype, mtype, build, is_assignable from require "moonscript.types" +import NameProxy from require "moonscript.transform.names" +import insert from table +import unpack from require "moonscript.util" + +import user_error from require "moonscript.errors" + +join = (...) -> + with out = {} + i = 1 + for tbl in *{...} + for v in *tbl + out[i] = v + i += 1 + +has_destructure = (names) -> + for n in *names + return true if ntype(n) == "table" + false + +-- can this value provide more than one return value +is_multi_return = (node) -> + return true if node == "..." + return false unless type(node) == "table" + switch node[1] + when "chain" + ntype(node[#node]) == "call" + when "explist" + true + else + false + +extract_assign_names = (name, accum={}, prefix={}) -> + + i = 1 + for tuple in *name[2] + value, suffix = if #tuple == 1 + s = {"index", {"number", i}} + i += 1 + tuple[1], s + else + key = tuple[1] + + s = if ntype(key) == "key_literal" + key_name = key[2] + if ntype(key_name) == "colon" + key_name + else + {"dot", key_name} + else + {"index", key} + + tuple[2], s + + suffix = join prefix, {suffix} + + if ntype(value) == "table" + extract_assign_names value, accum, suffix + elseif is_assignable value + insert accum, {value, suffix} + else + pos = type(value) == "table" and value[-1] or name[-1] + if value == "..." + user_error "Can't destructure into '...'", pos + elseif ntype(value) == "chain" + user_error "Can't destructure into chain ending in #{ntype value[#value]}", pos + else + user_error "Can't destructure value of type: #{ntype value}", pos + + accum + +-- can the receiver be referenced directly as the base of a chain? anything +-- not recognized here is copied to a temporary name before destructuring. +-- exp, table and string are chainable because the compiler wraps them in +-- parentheses +keyword_refs = {nil: true, true: true, false: true} + +chainable_receiver = (node) -> + switch type node + when "string" + -- legacy transformers use plain strings for names + node != "..." and not keyword_refs[node] + when "table" + switch node[1] + when "ref" + not keyword_refs[node[2]] + when "chain", "self", "self_class", "temp_name", "parens", "exp", "table", "string" + true + else + false + else + false + +build_assign = (scope, destruct_literal, receiver, opts) -> + assert receiver, "attempting to build destructure assign with no receiver" + + extracted_names = extract_assign_names destruct_literal + + names = {} + values = {} + + inner = {"assign", names, values} + + obj = if chainable_receiver(receiver) and (scope\is_local(receiver) or #extracted_names == 1) + receiver + else + with obj = NameProxy "obj" + inner = build.do { + build.assign_one obj, receiver + {"assign", names, values} + } + + for tuple in *extracted_names + insert names, tuple[1] + chain = if obj + NameProxy.chain obj, unpack tuple[2] + else + "nil" + insert values, chain + + group = {} + + if opts and opts.shadow + -- force new locals so the targets never assign to an enclosing scope, + -- only plain names can be declared (chain and self targets can't) + shadow_names = [name for name in *names when ntype(name) == "ref" or type(name) == "string"] + if next shadow_names + insert group, {"declare_with_shadows", shadow_names} + else + insert group, {"declare", names} + + insert group, inner + build.group group + +-- applies to destructuring to a assign node +split_assign = (scope, assign) -> + names, values = unpack assign, 2 + + g = {} + total_names = #names + total_values = #values + + -- if the final value can return multiple values then the names consuming + -- its returns must be assigned in a single statement. destructuring + -- literals among them receive a temporary that is unpacked afterwards + local suffix_start + if total_names > total_values and is_multi_return values[total_values] + for i=total_values, total_names + if ntype(names[i]) == "table" + suffix_start = total_values + break + + -- We have to break apart the assign into groups of regular + -- assigns, and then the destructuring assignments + start = 1 + stop = suffix_start and suffix_start - 1 or total_names + for i=1, stop + n = names[i] + if ntype(n) == "table" + if i > start + insert g, { + "assign" + for j=start,i-1 do names[j] + for j=start,i-1 do values[j] + } + + insert g, build_assign scope, n, values[i] + + start = i + 1 + + if suffix_start + -- plain names left over between the last destructure and the suffix + if start <= stop + insert g, { + "assign" + for j=start,stop do names[j] + for j=start,stop do values[j] + } + + suffix_names = {} + destructures = {} + for i=suffix_start, total_names + name = names[i] + if ntype(name) == "table" + proxy = NameProxy "destruct" + insert suffix_names, proxy + insert destructures, {name, proxy} + else + insert suffix_names, name + + insert g, {"assign", suffix_names, {values[total_values]}} + + for {literal, proxy} in *destructures + insert g, build_assign scope, literal, proxy + elseif total_names >= start or total_values >= start + name_slice = if total_names < start + {scope\discard_name!} + else + for i=start,total_names do names[i] + + value_slice = if total_values < start + {"nil"} + else + for i=start,total_values do values[i] + + insert g, {"assign", name_slice, value_slice} + + build.group g + +{ :has_destructure, :split_assign, :build_assign, :extract_assign_names } diff --git a/moonscript/transform/names.lua b/moonscript/transform/names.lua new file mode 100644 index 00000000..f6af4c46 --- /dev/null +++ b/moonscript/transform/names.lua @@ -0,0 +1,118 @@ +local build +build = require("moonscript.types").build +local unpack +unpack = require("moonscript.util").unpack +local LocalName +do + local _class_0 + local _base_0 = { + get_name = function(self) + return self.name + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function(self, name) + self.name = name + self[1] = "temp_name" + end, + __base = _base_0, + __name = "LocalName" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + LocalName = _class_0 +end +local NameProxy +do + local _class_0 + local _base_0 = { + get_name = function(self, scope, dont_put) + if dont_put == nil then + dont_put = true + end + if not self.name then + self.name = scope:free_name(self.prefix, dont_put) + end + return self.name + end, + chain = function(self, ...) + local items = { + base = self, + ... + } + for k, v in ipairs(items) do + if type(v) == "string" then + items[k] = { + "dot", + v + } + else + items[k] = v + end + end + return build.chain(items) + end, + index = function(self, key) + if type(key) == "string" then + key = { + "ref", + key + } + end + return build.chain({ + base = self, + { + "index", + key + } + }) + end, + __tostring = function(self) + if self.name then + return ("name<%s>"):format(self.name) + else + return ("name"):format(self.prefix) + end + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function(self, prefix) + self.prefix = prefix + self[1] = "temp_name" + end, + __base = _base_0, + __name = "NameProxy" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + NameProxy = _class_0 +end +local is_name_proxy +is_name_proxy = function(v) + if not (type(v) == "table") then + return false + end + local _exp_0 = v.__class + if LocalName == _exp_0 or NameProxy == _exp_0 then + return true + end +end +return { + NameProxy = NameProxy, + LocalName = LocalName, + is_name_proxy = is_name_proxy +} diff --git a/moonscript/transform/names.moon b/moonscript/transform/names.moon new file mode 100644 index 00000000..4af6aa0d --- /dev/null +++ b/moonscript/transform/names.moon @@ -0,0 +1,51 @@ + +import build from require "moonscript.types" +import unpack from require "moonscript.util" + +-- always declares as local +class LocalName + new: (@name) => self[1] = "temp_name" + get_name: => @name + +-- creates a unique name when used +class NameProxy + new: (@prefix) => + self[1] = "temp_name" + + get_name: (scope, dont_put=true) => + if not @name + @name = scope\free_name @prefix, dont_put + @name + + chain: (...) => + items = { base: @, ... } + for k,v in ipairs items + items[k] = if type(v) == "string" + {"dot", v} + else + v + + build.chain items + + index: (key) => + if type(key) == "string" + key = {"ref", key} + + build.chain { + base: self, {"index", key} + } + + __tostring: => + if @name + ("name<%s>")\format @name + else + ("name")\format @prefix + +is_name_proxy = (v) -> + return false unless type(v) == "table" + + switch v.__class + when LocalName, NameProxy + true + +{ :NameProxy, :LocalName, :is_name_proxy } diff --git a/moonscript/transform/statement.lua b/moonscript/transform/statement.lua new file mode 100644 index 00000000..c6bfeafd --- /dev/null +++ b/moonscript/transform/statement.lua @@ -0,0 +1,865 @@ +local Transformer +Transformer = require("moonscript.transform.transformer").Transformer +local NameProxy, LocalName, is_name_proxy +do + local _obj_0 = require("moonscript.transform.names") + NameProxy, LocalName, is_name_proxy = _obj_0.NameProxy, _obj_0.LocalName, _obj_0.is_name_proxy +end +local Run, transform_last_stm, implicitly_return, last_stm, find_continues +do + local _obj_0 = require("moonscript.transform.statements") + Run, transform_last_stm, implicitly_return, last_stm, find_continues = _obj_0.Run, _obj_0.transform_last_stm, _obj_0.implicitly_return, _obj_0.last_stm, _obj_0.find_continues +end +local types = require("moonscript.types") +local build, ntype, is_value, smart_node, value_is_singular, is_slice, NOOP +build, ntype, is_value, smart_node, value_is_singular, is_slice, NOOP = types.build, types.ntype, types.is_value, types.smart_node, types.value_is_singular, types.is_slice, types.NOOP +local insert +insert = table.insert +local destructure = require("moonscript.transform.destructure") +local construct_comprehension +construct_comprehension = require("moonscript.transform.comprehension").construct_comprehension +local unpack +unpack = require("moonscript.util").unpack +local user_error +user_error = require("moonscript.errors").user_error +local apply_continue +apply_continue = function(body) + local continues = find_continues(body) + if not (continues[1]) then + return body + end + local continue_name = NameProxy("continue") + for _index_0 = 1, #continues do + local node = continues[_index_0] + node[2] = continue_name + end + local last_type = ntype(last_stm(body)) + local repeat_body + if types.terminating[last_type] or last_type == "continue" then + repeat_body = { + { + "do", + body + } + } + else + repeat_body = body + end + insert(repeat_body, { + "assign", + { + continue_name + }, + { + "true" + } + }) + return { + { + "assign", + { + continue_name + }, + { + "false" + } + }, + { + "repeat", + "true", + repeat_body + }, + { + "if", + { + "not", + continue_name + }, + { + { + "break" + } + } + } + } +end +local extract_declarations +extract_declarations = function(self, body, start, out) + if body == nil then + body = self.current_stms + end + if start == nil then + start = self.current_stm_i + 1 + end + if out == nil then + out = { } + end + for i = start, #body do + local _continue_0 = false + repeat + local stm = body[i] + if stm == nil then + _continue_0 = true + break + end + stm = self.transform.statement(stm) + body[i] = stm + local _exp_0 = stm[1] + if "assign" == _exp_0 or "declare" == _exp_0 or "declare_constants" == _exp_0 then + local _list_0 = stm[2] + for _index_0 = 1, #_list_0 do + local name = _list_0[_index_0] + if ntype(name) == "ref" then + insert(out, name) + elseif type(name) == "string" then + insert(out, name) + end + end + elseif "group" == _exp_0 then + extract_declarations(self, stm[2], 1, out) + end + _continue_0 = true + until true + if not _continue_0 then + break + end + end + return out +end +local extract_declare_names +extract_declare_names = function(names) + local out = { } + for _index_0 = 1, #names do + local name = names[_index_0] + if ntype(name) == "table" then + local _list_0 = destructure.extract_assign_names(name) + for _index_1 = 1, #_list_0 do + local tuple = _list_0[_index_1] + insert(out, tuple[1]) + end + else + insert(out, name) + end + end + return out +end +local expand_elseif_assign +expand_elseif_assign = function(ifstm) + for i = 4, #ifstm do + local case = ifstm[i] + if ntype(case) == "elseif" and ntype(case[2]) == "assign" then + local split = { + unpack(ifstm, 1, i - 1) + } + insert(split, { + "else", + { + { + "if", + case[2], + case[3], + unpack(ifstm, i + 1) + } + } + }) + return split + end + end + return ifstm +end +return Transformer({ + transform = function(self, tuple) + local _, node, fn + _, node, fn = tuple[1], tuple[2], tuple[3] + return fn(node) + end, + root_stms = function(self, body) + return transform_last_stm(body, implicitly_return(self)) + end, + ["return"] = function(self, node) + local ret_val = node[2] + local ret_val_type = ntype(ret_val) + if ret_val_type == "explist" and #ret_val == 2 then + ret_val = ret_val[2] + ret_val_type = ntype(ret_val) + end + if types.cascading[ret_val_type] then + return implicitly_return(self)(ret_val) + end + if ret_val_type == "chain" or ret_val_type == "comprehension" or ret_val_type == "tblcomprehension" then + local Value = require("moonscript.transform.value") + ret_val = Value:transform_once(self, ret_val) + if ntype(ret_val) == "block_exp" then + return build.group(transform_last_stm(ret_val[2], function(stm) + return { + "return", + stm + } + end)) + end + end + node[2] = ret_val + return node + end, + declare_glob = function(self, node) + local names = extract_declarations(self) + if node[2] == "^" then + do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #names do + local _continue_0 = false + repeat + local name = names[_index_0] + local str_name + if ntype(name) == "ref" then + str_name = name[2] + else + str_name = name + end + if not (str_name:match("^%u")) then + _continue_0 = true + break + end + local _value_0 = name + _accum_0[_len_0] = _value_0 + _len_0 = _len_0 + 1 + _continue_0 = true + until true + if not _continue_0 then + break + end + end + names = _accum_0 + end + end + return { + "declare", + names + } + end, + assign = function(self, node) + local names, values = unpack(node, 2) + local num_values = #values + local num_names = #names + if num_names == 1 and num_values == 1 then + local first_value = values[1] + local first_name = names[1] + local first_type = ntype(first_value) + if first_type == "chain" then + local Value = require("moonscript.transform.value") + first_value = Value:transform_once(self, first_value) + first_type = ntype(first_value) + end + local _exp_0 = ntype(first_value) + if "block_exp" == _exp_0 then + local block_body = first_value[2] + local idx = #block_body + block_body[idx] = build.assign_one(first_name, block_body[idx]) + return build.group({ + { + "declare", + extract_declare_names({ + first_name + }) + }, + { + "do", + block_body + } + }) + elseif "comprehension" == _exp_0 or "tblcomprehension" == _exp_0 or "foreach" == _exp_0 or "for" == _exp_0 or "while" == _exp_0 then + local Value = require("moonscript.transform.value") + return build.assign_one(first_name, Value:transform_once(self, first_value)) + else + values[1] = first_value + end + end + local transformed + if num_values == 1 then + local value = values[1] + local t = ntype(value) + if t == "decorated" then + value = self.transform.statement(value) + t = ntype(value) + end + if types.cascading[t] then + local ret + ret = function(stm) + if is_value(stm) then + return { + "assign", + names, + { + stm + } + } + else + return stm + end + end + transformed = build.group({ + { + "declare", + extract_declare_names(names) + }, + self.transform.statement(value, ret, node) + }) + end + end + if transformed then + return transformed + end + if destructure.has_destructure(names) then + return destructure.split_assign(self, node) + end + return node + end, + continue = function(self, node) + local continue_name = node[2] + if not (continue_name) then + user_error("continue must be inside of a loop", node[-1]) + end + return build.group({ + build.assign_one(continue_name, "true"), + { + "break" + } + }) + end, + export = function(self, node) + if #node > 2 then + if node[2] == "class" then + local cls = smart_node(node[3]) + return build.group({ + { + "export", + { + cls.name + } + }, + cls + }) + else + return build.group({ + { + "export", + node[2] + }, + build.assign({ + names = node[2], + values = node[3] + }) + }) + end + else + return nil + end + end, + update = function(self, node) + local name, op, exp = unpack(node, 2) + local op_final = op:match("^(.+)=$") + if not op_final then + error("Unknown op: " .. op) + end + local lifted + if ntype(name) == "chain" then + lifted = { } + local new_chain + do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 3, #name do + local part = name[_index_0] + if ntype(part) == "index" then + local proxy = NameProxy("update") + table.insert(lifted, { + proxy, + part[2] + }) + _accum_0[_len_0] = { + "index", + proxy + } + else + _accum_0[_len_0] = part + end + _len_0 = _len_0 + 1 + end + new_chain = _accum_0 + end + if next(lifted) then + name = { + name[1], + name[2], + unpack(new_chain) + } + end + end + if not (value_is_singular(exp)) then + exp = { + "parens", + exp + } + end + local out = build.assign_one(name, { + "exp", + name, + op_final, + exp + }) + if lifted and next(lifted) then + local names + do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #lifted do + local l = lifted[_index_0] + _accum_0[_len_0] = l[1] + _len_0 = _len_0 + 1 + end + names = _accum_0 + end + local values + do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #lifted do + local l = lifted[_index_0] + _accum_0[_len_0] = l[2] + _len_0 = _len_0 + 1 + end + values = _accum_0 + end + out = build.group({ + { + "assign", + names, + values + }, + out + }) + end + return out + end, + import = function(self, node) + local names, source = unpack(node, 2) + local dest_names = { } + local table_values + do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #names do + local name = names[_index_0] + local dest_name + if ntype(name) == "colon" then + dest_name = name[2] + else + dest_name = name + end + insert(dest_names, dest_name) + local _value_0 = { + { + "key_literal", + name + }, + dest_name + } + _accum_0[_len_0] = _value_0 + _len_0 = _len_0 + 1 + end + table_values = _accum_0 + end + local dest = { + "table", + table_values + } + return build.group({ + { + "assign", + { + dest + }, + { + source + }, + [-1] = node[-1] + }, + { + "declare_constants", + dest_names, + [-1] = node[-1] + } + }) + end, + comprehension = function(self, node, action) + local exp, clauses = unpack(node, 2) + action = action or function(exp) + return { + exp + } + end + return construct_comprehension(action(exp), clauses) + end, + ["do"] = function(self, node, ret) + if ret then + node[2] = transform_last_stm(node[2], ret) + end + return node + end, + decorated = function(self, node) + local stm, dec = unpack(node, 2) + local wrapped + local _exp_0 = dec[1] + if "if" == _exp_0 then + local cond, fail = unpack(dec, 2) + if fail then + fail = { + "else", + { + fail + } + } + end + wrapped = { + "if", + cond, + { + stm + }, + fail + } + elseif "unless" == _exp_0 then + wrapped = { + "unless", + dec[2], + { + stm + } + } + elseif "comprehension" == _exp_0 then + wrapped = { + "comprehension", + stm, + dec[2] + } + else + wrapped = error("Unknown decorator " .. dec[1]) + end + if ntype(stm) == "assign" then + wrapped = build.group({ + build.declare({ + names = (function() + local _accum_0 = { } + local _len_0 = 1 + local _list_0 = extract_declare_names(stm[2]) + for _index_0 = 1, #_list_0 do + local name = _list_0[_index_0] + if ntype(name) == "ref" then + _accum_0[_len_0] = name + _len_0 = _len_0 + 1 + end + end + return _accum_0 + end)() + }), + wrapped + }) + end + return wrapped + end, + unless = function(self, node) + local clause = node[2] + if ntype(clause) == "assign" then + if destructure.has_destructure(clause[2]) then + error("destructure not allowed in unless assignment") + end + return build["do"]({ + clause, + { + "if", + { + "not", + clause[2][1] + }, + unpack(node, 3) + } + }) + else + return { + "if", + { + "not", + { + "parens", + clause + } + }, + unpack(node, 3) + } + end + end, + ["if"] = function(self, node, ret) + if ntype(node[2]) == "assign" then + local assign, body = unpack(node, 2) + if destructure.has_destructure(assign[2]) then + local name = NameProxy("des") + body = { + destructure.build_assign(self, assign[2][1], name), + build.group(node[3]) + } + return build["do"]({ + build.assign_one(name, assign[3][1]), + { + "if", + name, + body, + unpack(node, 4) + } + }) + else + local name = assign[2][1] + return build["do"]({ + assign, + { + "if", + name, + unpack(node, 3) + } + }) + end + end + node = expand_elseif_assign(node) + if ret then + smart_node(node) + node['then'] = transform_last_stm(node['then'], ret) + for i = 4, #node do + local case = node[i] + local body_idx = #node[i] + case[body_idx] = transform_last_stm(case[body_idx], ret) + end + end + return node + end, + with = function(self, node, ret) + local exp, block = unpack(node, 2) + local copy_scope = true + local scope_name, named_assign + do + local last = last_stm(block) + if last then + if types.terminating[ntype(last)] then + ret = false + end + end + end + if ntype(exp) == "assign" then + local names, values = unpack(exp, 2) + local first_name = names[1] + if ntype(first_name) == "ref" then + scope_name = first_name + named_assign = exp + exp = values[1] + copy_scope = false + else + scope_name = NameProxy("with") + exp = values[1] + values[1] = scope_name + named_assign = { + "assign", + names, + values + } + end + elseif self:is_local(exp) then + scope_name = exp + copy_scope = false + end + scope_name = scope_name or NameProxy("with") + local out = build["do"]({ + copy_scope and build.assign_one(scope_name, exp) or NOOP, + named_assign or NOOP, + Run(function(self) + return self:set("scope_var", scope_name) + end), + unpack(block) + }) + if ret then + table.insert(out[2], ret(scope_name)) + end + return out + end, + foreach = function(self, node, _) + smart_node(node) + local source = unpack(node.iter) + local destructures = { } + do + local _accum_0 = { } + local _len_0 = 1 + for i, name in ipairs(node.names) do + if ntype(name) == "table" then + do + local proxy = NameProxy("des") + insert(destructures, destructure.build_assign(self, name, proxy)) + _accum_0[_len_0] = proxy + end + else + _accum_0[_len_0] = name + end + _len_0 = _len_0 + 1 + end + node.names = _accum_0 + end + if next(destructures) then + insert(destructures, build.group(node.body)) + node.body = destructures + end + if ntype(source) == "unpack" then + local list = source[2] + local index_name = NameProxy("index") + local list_name = self:is_local(list) and list or NameProxy("list") + local slice_var = nil + local bounds + if is_slice(list) then + local slice = list[#list] + table.remove(list) + table.remove(slice, 1) + if self:is_local(list) then + list_name = list + end + if slice[2] and slice[2] ~= "" then + local max_tmp_name = NameProxy("max") + slice_var = build.assign_one(max_tmp_name, slice[2]) + slice[2] = { + "exp", + max_tmp_name, + "<", + 0, + "and", + { + "length", + list_name + }, + "+", + max_tmp_name, + "or", + max_tmp_name + } + else + slice[2] = { + "length", + list_name + } + end + bounds = slice + else + bounds = { + 1, + { + "length", + list_name + } + } + end + local names + do + local _accum_0 = { } + local _len_0 = 1 + local _list_0 = node.names + for _index_0 = 1, #_list_0 do + local n = _list_0[_index_0] + _accum_0[_len_0] = is_name_proxy(n) and n or LocalName(n) or n + _len_0 = _len_0 + 1 + end + names = _accum_0 + end + return build.group({ + list_name ~= list and build.assign_one(list_name, list) or NOOP, + slice_var or NOOP, + build["for"]({ + name = index_name, + bounds = bounds, + body = { + { + "assign", + names, + { + NameProxy.index(list_name, index_name) + } + }, + build.group(node.body) + } + }) + }) + end + node.body = apply_continue(node.body) + end, + ["while"] = function(self, node) + smart_node(node) + node.body = apply_continue(node.body) + end, + ["for"] = function(self, node) + smart_node(node) + node.body = apply_continue(node.body) + end, + switch = function(self, node, ret) + local exp, conds = unpack(node, 2) + local exp_name = NameProxy("exp") + local convert_cond + convert_cond = function(cond) + local t, case_exps, body = unpack(cond) + local out = { } + insert(out, t == "case" and "elseif" or "else") + if t ~= "else" then + local cond_exp = { } + for i, case in ipairs(case_exps) do + if i == 1 then + insert(cond_exp, "exp") + else + insert(cond_exp, "or") + end + if not (value_is_singular(case)) then + case = { + "parens", + case + } + end + insert(cond_exp, { + "exp", + case, + "==", + exp_name + }) + end + insert(out, cond_exp) + else + body = case_exps + end + if ret then + body = transform_last_stm(body, ret) + end + insert(out, body) + return out + end + local first = true + local if_stm = { + "if" + } + for _index_0 = 1, #conds do + local cond = conds[_index_0] + local if_cond = convert_cond(cond) + if first then + first = false + insert(if_stm, if_cond[2]) + insert(if_stm, if_cond[3]) + else + insert(if_stm, if_cond) + end + end + return build.group({ + build.assign_one(exp_name, exp), + if_stm + }) + end, + class = require("moonscript.transform.class") +}) diff --git a/moonscript/transform/statement.moon b/moonscript/transform/statement.moon new file mode 100644 index 00000000..053993b6 --- /dev/null +++ b/moonscript/transform/statement.moon @@ -0,0 +1,543 @@ +import Transformer from require "moonscript.transform.transformer" + +import NameProxy, LocalName, is_name_proxy from require "moonscript.transform.names" + +import Run, transform_last_stm, implicitly_return, last_stm, find_continues + from require "moonscript.transform.statements" + +types = require "moonscript.types" + +import build, ntype, is_value, smart_node, value_is_singular, is_slice, NOOP + from types + +import insert from table + +destructure = require "moonscript.transform.destructure" +import construct_comprehension from require "moonscript.transform.comprehension" + +import unpack from require "moonscript.util" +import user_error from require "moonscript.errors" + +-- binds the continue statements in a loop body to a loop-local flag, +-- wrapping the body in a repeat block that continue can break out of +apply_continue = (body) -> + continues = find_continues body + return body unless continues[1] + + continue_name = NameProxy "continue" + + -- attach the name in place, the continue transformer expands the node when + -- the compiler reaches it so cascading transforms still see a continue + for node in *continues + node[2] = continue_name + + last_type = ntype last_stm body + + -- a trailing continue expands to a break, which must also be enclosed + repeat_body = if types.terminating[last_type] or last_type == "continue" + { {"do", body} } + else + body + + insert repeat_body, {"assign", {continue_name}, {"true"}} + + { + {"assign", {continue_name}, {"false"}} + {"repeat", "true", repeat_body} + {"if", {"not", continue_name}, { + {"break"} + }} + } + + +-- this mutates body searching for assigns +extract_declarations = (body=@current_stms, start=@current_stm_i + 1, out={}) => + for i=start,#body + stm = body[i] + continue if stm == nil + stm = @transform.statement stm + body[i] = stm + switch stm[1] + when "assign", "declare", "declare_constants" + for name in *stm[2] + if ntype(name) == "ref" + insert out, name + elseif type(name) == "string" + -- TODO: don't use string literal as ref + insert out, name + when "group" + extract_declarations @, stm[2], 1, out + out + +-- the names a declare statement must list for an assignment's names, +-- destructuring literals are replaced by the names they extract so the +-- declaration works when the assignment happens in a deeper scope +extract_declare_names = (names) -> + out = {} + for name in *names + if ntype(name) == "table" + for tuple in *destructure.extract_assign_names name + insert out, tuple[1] + else + insert out, name + out + +expand_elseif_assign = (ifstm) -> + for i = 4, #ifstm + case = ifstm[i] + if ntype(case) == "elseif" and ntype(case[2]) == "assign" + split = { unpack ifstm, 1, i - 1 } + insert split, { + "else", { + {"if", case[2], case[3], unpack ifstm, i + 1} + } + } + return split + + ifstm + + +Transformer { + transform: (tuple) => + {_, node, fn} = tuple + fn node + + root_stms: (body) => + transform_last_stm body, implicitly_return @ + + return: (node) => + ret_val = node[2] + ret_val_type = ntype ret_val + + if ret_val_type == "explist" and #ret_val == 2 + ret_val = ret_val[2] + ret_val_type = ntype ret_val + + if types.cascading[ret_val_type] + return implicitly_return(@) ret_val + + -- flatten things that create block exp + if ret_val_type == "chain" or ret_val_type == "comprehension" or ret_val_type == "tblcomprehension" + -- TODO: clean this up + Value = require "moonscript.transform.value" + ret_val = Value\transform_once @, ret_val + if ntype(ret_val) == "block_exp" + return build.group transform_last_stm ret_val[2], (stm)-> + {"return", stm} + + node[2] = ret_val + node + + declare_glob: (node) => + names = extract_declarations @ + + if node[2] == "^" + names = for name in *names + str_name = if ntype(name) == "ref" + name[2] + else + name + + continue unless str_name\match "^%u" + name + + {"declare", names} + + assign: (node) => + names, values = unpack node, 2 + + num_values = #values + num_names = #names + + -- special code simplifications for single assigns + if num_names == 1 and num_values == 1 + first_value = values[1] + first_name = names[1] + first_type = ntype first_value + + -- reduce colon stub chain to block exp + if first_type == "chain" + -- TODO: clean this up + Value = require "moonscript.transform.value" + first_value = Value\transform_once @, first_value + first_type = ntype first_value + + switch ntype first_value + when "block_exp" + block_body = first_value[2] + idx = #block_body + block_body[idx] = build.assign_one first_name, block_body[idx] + + return build.group { + {"declare", extract_declare_names {first_name}} + {"do", block_body} + } + + when "comprehension", "tblcomprehension", "foreach", "for", "while" + -- TODO: clean this up + Value = require "moonscript.transform.value" + return build.assign_one first_name, Value\transform_once @, first_value + else + values[1] = first_value + + -- bubble cascading assigns + transformed = if num_values == 1 + value = values[1] + t = ntype value + + if t == "decorated" + value = @transform.statement value + t = ntype value + + if types.cascading[t] + ret = (stm) -> + if is_value stm + {"assign", names, {stm}} + else + stm + + build.group { + {"declare", extract_declare_names names} + @transform.statement value, ret, node + } + + -- the assigns bubbled into the value's branches handle their own + -- destructuring when they are transformed + return transformed if transformed + + if destructure.has_destructure names + return destructure.split_assign @, node + + node + + -- apply_continue binds the loop's flag to the node, a continue without one + -- is outside of any loop + continue: (node) => + continue_name = node[2] + user_error "continue must be inside of a loop", node[-1] unless continue_name + build.group { + build.assign_one continue_name, "true" + {"break"} + } + + export: (node) => + -- assign values if they are included + if #node > 2 + if node[2] == "class" + cls = smart_node node[3] + build.group { + {"export", {cls.name}} + cls + } + else + -- pull out vawlues and assign them after the export + build.group { + { "export", node[2] } + build.assign { + names: node[2] + values: node[3] + } + } + else + nil + + update: (node) => + name, op, exp = unpack node, 2 + op_final = op\match "^(.+)=$" + + error "Unknown op: "..op if not op_final + + local lifted + + if ntype(name) == "chain" + lifted = {} + new_chain = for part in *name[3,] + if ntype(part) == "index" + proxy = NameProxy "update" + table.insert lifted, { proxy, part[2] } + { "index", proxy } + else + part + + if next lifted + name = {name[1], name[2], unpack new_chain} + + exp = {"parens", exp} unless value_is_singular exp + out = build.assign_one name, {"exp", name, op_final, exp} + + if lifted and next lifted + names = [l[1] for l in *lifted] + values = [l[2] for l in *lifted] + + out = build.group { + {"assign", names, values} + out + } + + out + + import: (node) => + names, source = unpack node, 2 + + dest_names = {} + table_values = for name in *names + dest_name = if ntype(name) == "colon" + name[2] + else + name + + insert dest_names, dest_name + {{"key_literal", name}, dest_name} + + dest = { "table", table_values } + build.group { + { "assign", {dest}, {source}, [-1]: node[-1] } + { "declare_constants", dest_names, [-1]: node[-1] } + } + + comprehension: (node, action) => + exp, clauses = unpack node, 2 + + action = action or (exp) -> {exp} + construct_comprehension action(exp), clauses + + do: (node, ret) => + node[2] = transform_last_stm node[2], ret if ret + node + + decorated: (node) => + stm, dec = unpack node, 2 + + wrapped = switch dec[1] + when "if" + cond, fail = unpack dec, 2 + fail = { "else", { fail } } if fail + { "if", cond, { stm }, fail } + when "unless" + { "unless", dec[2], { stm } } + when "comprehension" + { "comprehension", stm, dec[2] } + else + error "Unknown decorator " .. dec[1] + + if ntype(stm) == "assign" + wrapped = build.group { + build.declare names: [name for name in *extract_declare_names(stm[2]) when ntype(name) == "ref"] + wrapped + } + + wrapped + + unless: (node) => + clause = node[2] + + if ntype(clause) == "assign" + if destructure.has_destructure clause[2] + error "destructure not allowed in unless assignment" + + build.do { + clause + { "if", {"not", clause[2][1]}, unpack node, 3 } + } + + else + { "if", {"not", {"parens", clause}}, unpack node, 3 } + + if: (node, ret) => + -- expand assign in cond + if ntype(node[2]) == "assign" + assign, body = unpack node, 2 + if destructure.has_destructure assign[2] + name = NameProxy "des" + + body = { + destructure.build_assign @, assign[2][1], name + build.group node[3] + } + + return build.do { + build.assign_one name, assign[3][1] + {"if", name, body, unpack node, 4} + } + else + name = assign[2][1] + return build.do { + assign + {"if", name, unpack node, 3} + } + + node = expand_elseif_assign node + + -- apply cascading return decorator + if ret + smart_node node + -- mutate all the bodies + node['then'] = transform_last_stm node['then'], ret + for i = 4, #node + case = node[i] + body_idx = #node[i] + case[body_idx] = transform_last_stm case[body_idx], ret + + node + + with: (node, ret) => + exp, block = unpack node, 2 + + copy_scope = true + local scope_name, named_assign + + if last = last_stm block + ret = false if types.terminating[ntype(last)] + + if ntype(exp) == "assign" + names, values = unpack exp, 2 + first_name = names[1] + + if ntype(first_name) == "ref" + scope_name = first_name + named_assign = exp + exp = values[1] + copy_scope = false + else + scope_name = NameProxy "with" + exp = values[1] + values[1] = scope_name + named_assign = {"assign", names, values} + + elseif @is_local exp + scope_name = exp + copy_scope = false + + scope_name or= NameProxy "with" + + out = build.do { + copy_scope and build.assign_one(scope_name, exp) or NOOP + named_assign or NOOP + Run => @set "scope_var", scope_name + unpack block + } + + if ret + table.insert out[2], ret scope_name + + out + + foreach: (node, _) => + smart_node node + source = unpack node.iter + + destructures = {} + node.names = for i, name in ipairs node.names + if ntype(name) == "table" + with proxy = NameProxy "des" + insert destructures, destructure.build_assign @, name, proxy + else + name + + if next destructures + insert destructures, build.group node.body + node.body = destructures + + if ntype(source) == "unpack" + list = source[2] + + index_name = NameProxy "index" + + list_name = @is_local(list) and list or NameProxy "list" + + slice_var = nil + bounds = if is_slice list + slice = list[#list] + table.remove list + table.remove slice, 1 + + list_name = list if @is_local list + + slice[2] = if slice[2] and slice[2] != "" + max_tmp_name = NameProxy "max" + slice_var = build.assign_one max_tmp_name, slice[2] + {"exp", max_tmp_name, "<", 0 + "and", {"length", list_name}, "+", max_tmp_name + "or", max_tmp_name } + else + {"length", list_name} + + slice + else + {1, {"length", list_name}} + + names = [is_name_proxy(n) and n or LocalName(n) or n for n in *node.names] + + return build.group { + list_name != list and build.assign_one(list_name, list) or NOOP + slice_var or NOOP + build["for"] { + name: index_name + bounds: bounds + body: { + {"assign", names, { NameProxy.index list_name, index_name }} + build.group node.body + } + } + } + + node.body = apply_continue node.body + + while: (node) => + smart_node node + node.body = apply_continue node.body + + for: (node) => + smart_node node + node.body = apply_continue node.body + + switch: (node, ret) => + exp, conds = unpack node, 2 + exp_name = NameProxy "exp" + + -- convert switch conds into if statment conds + convert_cond = (cond) -> + t, case_exps, body = unpack cond + out = {} + insert out, t == "case" and "elseif" or "else" + if t != "else" + cond_exp = {} + for i, case in ipairs case_exps + if i == 1 + insert cond_exp, "exp" + else + insert cond_exp, "or" + + case = {"parens", case} unless value_is_singular case + insert cond_exp, {"exp", case, "==", exp_name} + + insert out, cond_exp + else + body = case_exps + + if ret + body = transform_last_stm body, ret + + insert out, body + + out + + first = true + if_stm = {"if"} + for cond in *conds + if_cond = convert_cond cond + if first + first = false + insert if_stm, if_cond[2] + insert if_stm, if_cond[3] + else + insert if_stm, if_cond + + build.group { + build.assign_one exp_name, exp + if_stm + } + + class: require "moonscript.transform.class" + +} diff --git a/moonscript/transform/statements.lua b/moonscript/transform/statements.lua new file mode 100644 index 00000000..22380333 --- /dev/null +++ b/moonscript/transform/statements.lua @@ -0,0 +1,181 @@ +local types = require("moonscript.types") +local ntype, mtype, is_value, NOOP +ntype, mtype, is_value, NOOP = types.ntype, types.mtype, types.is_value, types.NOOP +local comprehension_has_value +comprehension_has_value = require("moonscript.transform.comprehension").comprehension_has_value +local insert +insert = table.insert +local Run +do + local _class_0 + local _base_0 = { + call = function(self, state) + return self.fn(state) + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function(self, fn) + self.fn = fn + self[1] = "run" + end, + __base = _base_0, + __name = "Run" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + Run = _class_0 +end +local last_stm +last_stm = function(stms) + local last_exp_id = 0 + for i = #stms, 1, -1 do + local stm = stms[i] + if stm and mtype(stm) ~= Run then + if ntype(stm) == "group" then + return last_stm(stm[2]) + end + last_exp_id = i + break + end + end + return stms[last_exp_id], last_exp_id, stms +end +local transform_last_stm +transform_last_stm = function(stms, fn) + local _, last_idx, _stms = last_stm(stms) + if _stms ~= stms then + error("cannot transform last node in group") + end + return (function() + local _accum_0 = { } + local _len_0 = 1 + for i, stm in ipairs(stms) do + if i == last_idx then + _accum_0[_len_0] = { + "transform", + stm, + fn + } + else + _accum_0[_len_0] = stm + end + _len_0 = _len_0 + 1 + end + return _accum_0 + end)() +end +local chain_is_stub +chain_is_stub = function(chain) + local stub = chain[#chain] + return stub and ntype(stub) == "colon" +end +local continue_boundaries = { + fndef = true, + ["while"] = true, + ["for"] = true, + foreach = true, + comprehension = true, + tblcomprehension = true +} +local find_continues +find_continues = function(tbl, out) + if out == nil then + out = { } + end + for _index_0 = 1, #tbl do + local item = tbl[_index_0] + if type(item) == "table" then + if item[1] == "continue" then + insert(out, item) + elseif not continue_boundaries[item[1]] then + find_continues(item, out) + end + end + end + return out +end +local has_varargs +has_varargs = function(tbl) + for _index_0 = 1, #tbl do + local _continue_0 = false + repeat + local item = tbl[_index_0] + local _exp_0 = type(item) + if "string" == _exp_0 then + if item == "..." then + return true + end + elseif "table" == _exp_0 then + local _exp_1 = item[1] + if "fndef" == _exp_1 then + _continue_0 = true + break + elseif "string" == _exp_1 then + for i = 3, #item do + local part = item[i] + if type(part) == "table" and has_varargs(part) then + return true + end + end + else + if has_varargs(item) then + return true + end + end + end + _continue_0 = true + until true + if not _continue_0 then + break + end + end + return false +end +local implicitly_return +implicitly_return = function(scope) + local is_top = true + local fn + fn = function(stm) + local t = ntype(stm) + if t == "decorated" then + stm = scope.transform.statement(stm) + t = ntype(stm) + end + if types.cascading[t] then + is_top = false + return scope.transform.statement(stm, fn) + elseif types.manual_return[t] or not is_value(stm) then + if is_top and t == "return" and stm[2] == "" then + return NOOP + else + return stm + end + else + if t == "comprehension" and not comprehension_has_value(stm) then + return stm + else + return { + "return", + stm + } + end + end + end + return fn +end +return { + Run = Run, + last_stm = last_stm, + transform_last_stm = transform_last_stm, + chain_is_stub = chain_is_stub, + implicitly_return = implicitly_return, + find_continues = find_continues, + has_varargs = has_varargs +} diff --git a/moonscript/transform/statements.moon b/moonscript/transform/statements.moon new file mode 100644 index 00000000..efd8a2fc --- /dev/null +++ b/moonscript/transform/statements.moon @@ -0,0 +1,122 @@ + +types = require "moonscript.types" +import ntype, mtype, is_value, NOOP from types + +import comprehension_has_value from require "moonscript.transform.comprehension" + +import insert from table + +-- A Run is a special statement node that lets a function run and mutate the +-- state of the compiler +class Run + new: (@fn) => + @[1] = "run" + + call: (state) => + @.fn state + +-- extract the last statment from an array of statements +-- is group aware +-- returns: the last statement, the index, the table it was fetched from +last_stm = (stms) -> + last_exp_id = 0 + for i = #stms, 1, -1 + stm = stms[i] + if stm and mtype(stm) != Run + if ntype(stm) == "group" + return last_stm stm[2] + + last_exp_id = i + break + + stms[last_exp_id], last_exp_id, stms + +-- transform the last stm is a list of stms +-- will puke on group +transform_last_stm = (stms, fn) -> + _, last_idx, _stms = last_stm stms + + if _stms != stms + error "cannot transform last node in group" + + return for i, stm in ipairs stms + if i == last_idx + {"transform", stm, fn} + else + stm + +chain_is_stub = (chain) -> + stub = chain[#chain] + stub and ntype(stub) == "colon" + +-- nodes that a continue statement binds to, a continue found inside one of +-- these belongs to that construct and not the enclosing loop +continue_boundaries = { + fndef: true + while: true + for: true + foreach: true + comprehension: true + tblcomprehension: true +} + +-- collect the continue nodes in a body that bind to the enclosing loop +find_continues = (tbl, out={}) -> + for item in *tbl + if type(item) == "table" + if item[1] == "continue" + insert out, item + elseif not continue_boundaries[item[1]] + find_continues item, out + out + +-- does a body reference the enclosing function's varargs. Nested functions +-- have their own varargs, string nodes hold literal text at string positions +has_varargs = (tbl) -> + for item in *tbl + switch type item + when "string" + return true if item == "..." + when "table" + switch item[1] + when "fndef" + continue + when "string" + for i = 3, #item + part = item[i] + if type(part) == "table" and has_varargs part + return true + else + return true if has_varargs item + false + +implicitly_return = (scope) -> + is_top = true + fn = (stm) -> + t = ntype stm + + -- expand decorated + if t == "decorated" + stm = scope.transform.statement stm + t = ntype stm + + if types.cascading[t] + is_top = false + scope.transform.statement stm, fn + elseif types.manual_return[t] or not is_value stm + -- remove blank return statement + if is_top and t == "return" and stm[2] == "" + NOOP + else + stm + else + if t == "comprehension" and not comprehension_has_value stm + stm + else + {"return", stm} + + fn + +{:Run, :last_stm, :transform_last_stm, :chain_is_stub, :implicitly_return, + :find_continues, :has_varargs } + diff --git a/moonscript/transform/transformer.lua b/moonscript/transform/transformer.lua new file mode 100644 index 00000000..4ea16959 --- /dev/null +++ b/moonscript/transform/transformer.lua @@ -0,0 +1,74 @@ +local ntype +ntype = require("moonscript.types").ntype +local Transformer +do + local _class_0 + local _base_0 = { + transform_once = function(self, scope, node, ...) + if self.seen_nodes[node] then + return node + end + self.seen_nodes[node] = true + local transformer = self.transformers[ntype(node)] + if transformer then + return transformer(scope, node, ...) or node + else + return node + end + end, + transform = function(self, scope, node, ...) + if self.seen_nodes[node] then + return node + end + self.seen_nodes[node] = true + while true do + local transformer = self.transformers[ntype(node)] + local res + if transformer then + res = transformer(scope, node, ...) or node + else + res = node + end + if res == node then + return node + end + node = res + end + return node + end, + bind = function(self, scope) + return function(...) + return self:transform(scope, ...) + end + end, + __call = function(self, ...) + return self:transform(...) + end, + can_transform = function(self, node) + return self.transformers[ntype(node)] ~= nil + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function(self, transformers) + self.transformers = transformers + self.seen_nodes = setmetatable({ }, { + __mode = "k" + }) + end, + __base = _base_0, + __name = "Transformer" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + Transformer = _class_0 +end +return { + Transformer = Transformer +} diff --git a/moonscript/transform/transformer.moon b/moonscript/transform/transformer.moon new file mode 100644 index 00000000..8a40c3c7 --- /dev/null +++ b/moonscript/transform/transformer.moon @@ -0,0 +1,42 @@ +import ntype from require "moonscript.types" + +class Transformer + new: (@transformers) => + @seen_nodes = setmetatable {}, __mode: "k" + + transform_once: (scope, node, ...) => + return node if @seen_nodes[node] + @seen_nodes[node] = true + + transformer = @transformers[ntype node] + if transformer + transformer(scope, node, ...) or node + else + node + + transform: (scope, node, ...) => + return node if @seen_nodes[node] + + @seen_nodes[node] = true + while true + transformer = @transformers[ntype node] + res = if transformer + transformer(scope, node, ...) or node + else + node + + return node if res == node + node = res + + node + + bind: (scope) => + (...) -> @transform scope, ... + + __call: (...) => @transform ... + + can_transform: (node) => + @transformers[ntype node] != nil + + +{ :Transformer } diff --git a/moonscript/transform/value.lua b/moonscript/transform/value.lua new file mode 100644 index 00000000..4b49cd0c --- /dev/null +++ b/moonscript/transform/value.lua @@ -0,0 +1,401 @@ +local Transformer +Transformer = require("moonscript.transform.transformer").Transformer +local build, ntype, smart_node +do + local _obj_0 = require("moonscript.types") + build, ntype, smart_node = _obj_0.build, _obj_0.ntype, _obj_0.smart_node +end +local NameProxy +NameProxy = require("moonscript.transform.names").NameProxy +local Accumulator, default_accumulator +do + local _obj_0 = require("moonscript.transform.accumulator") + Accumulator, default_accumulator = _obj_0.Accumulator, _obj_0.default_accumulator +end +local lua_keywords +lua_keywords = require("moonscript.data").lua_keywords +local user_error +user_error = require("moonscript.errors").user_error +local transform_last_stm, implicitly_return, chain_is_stub, has_varargs +do + local _obj_0 = require("moonscript.transform.statements") + transform_last_stm, implicitly_return, chain_is_stub, has_varargs = _obj_0.transform_last_stm, _obj_0.implicitly_return, _obj_0.chain_is_stub, _obj_0.has_varargs +end +local construct_comprehension +construct_comprehension = require("moonscript.transform.comprehension").construct_comprehension +local destructure = require("moonscript.transform.destructure") +local insert +insert = table.insert +local unpack +unpack = require("moonscript.util").unpack +return Transformer({ + ["for"] = default_accumulator, + ["while"] = default_accumulator, + foreach = default_accumulator, + ["do"] = function(self, node) + return build.block_exp(node[2]) + end, + decorated = function(self, node) + return self.transform.statement(node) + end, + class = function(self, node) + return build.block_exp({ + node + }) + end, + string = function(self, node) + local delim = node[2] + local convert_part + convert_part = function(part) + if type(part) == "string" or part == nil then + return { + "string", + delim, + part or "" + } + else + return build.chain({ + base = "tostring", + { + "call", + { + part[2] + } + } + }) + end + end + if #node <= 3 then + if type(node[3]) == "string" then + return node + else + return convert_part(node[3]) + end + end + local e = { + "exp", + convert_part(node[3]) + } + for i = 4, #node do + insert(e, "..") + insert(e, convert_part(node[i])) + end + return e + end, + comprehension = function(self, node) + local a = Accumulator() + node = self.transform.statement(node, function(exp) + return a:mutate_body({ + exp + }) + end) + return a:wrap(node) + end, + tblcomprehension = function(self, node) + local explist, clauses = unpack(node, 2) + local key_exp, value_exp = unpack(explist) + local accum = NameProxy("tbl") + local inner + if value_exp then + local dest = build.chain({ + base = accum, + { + "index", + key_exp + } + }) + inner = { + build.assign_one(dest, value_exp) + } + else + local key_name, val_name = NameProxy("key"), NameProxy("val") + local dest = build.chain({ + base = accum, + { + "index", + key_name + } + }) + inner = { + build.assign({ + names = { + key_name, + val_name + }, + values = { + key_exp + } + }), + build.assign_one(dest, val_name) + } + end + return build.block_exp({ + build.assign_one(accum, build.table()), + construct_comprehension(inner, clauses), + accum + }) + end, + fndef = function(self, node) + smart_node(node) + node.body = transform_last_stm(node.body, implicitly_return(self)) + local first_destructure + for i, arg in ipairs(node.args) do + if ntype(arg[1]) == "table" then + first_destructure = i + break + end + end + if first_destructure then + local bound_names = { } + if node.arrow == "fat" then + bound_names.self = true + end + local _list_0 = node.args + for _index_0 = 1, #_list_0 do + local arg = _list_0[_index_0] + local _exp_0 = ntype(arg[1]) + if "self" == _exp_0 or "self_class" == _exp_0 then + bound_names[arg[1][2]] = true + elseif "table" == _exp_0 then + local _scrap_0 = nil + else + if type(arg[1]) == "string" then + bound_names[arg[1]] = true + end + end + end + local seen_targets = { } + local _list_1 = node.args + for _index_0 = 1, #_list_1 do + local _continue_0 = false + repeat + local arg = _list_1[_index_0] + if not (ntype(arg[1]) == "table") then + _continue_0 = true + break + end + local targets + do + local _accum_0 = { } + local _len_0 = 1 + local _list_2 = destructure.extract_assign_names(arg[1]) + for _index_1 = 1, #_list_2 do + local _des_0 = _list_2[_index_1] + local t + t = _des_0[1] + if ntype(t) == "ref" then + _accum_0[_len_0] = t + _len_0 = _len_0 + 1 + end + end + targets = _accum_0 + end + for _index_1 = 1, #targets do + local target = targets[_index_1] + local name = target[2] + if bound_names[name] or seen_targets[name] then + user_error("Can't destructure into '" .. tostring(name) .. "': name is bound by another parameter", target[-1]) + end + end + for _index_1 = 1, #targets do + local target = targets[_index_1] + seen_targets[target[2]] = true + end + _continue_0 = true + until true + if not _continue_0 then + break + end + end + local default_check + default_check = function(name, value) + return { + "if", + { + "exp", + name, + "==", + "nil" + }, + { + { + "assign", + { + name + }, + { + value + } + } + } + } + end + local prelude = { } + for i = first_destructure, #node.args do + local arg = node.args[i] + local name, default_value = arg[1], arg[2] + local _exp_0 = ntype(name) + if "table" == _exp_0 then + local proxy = NameProxy("arg") + if default_value then + insert(prelude, default_check(proxy, default_value)) + end + insert(prelude, destructure.build_assign(self, name, proxy, { + shadow = true + })) + node.args[i] = { + proxy + } + elseif "self" == _exp_0 or "self_class" == _exp_0 then + local raw_name = name[2] + if default_value then + insert(prelude, default_check({ + "ref", + raw_name + }, default_value)) + end + insert(prelude, build.assign_one(name, { + "ref", + raw_name + })) + node.args[i] = { + raw_name + } + else + if default_value then + insert(prelude, default_check({ + "ref", + name + }, default_value)) + node.args[i] = { + name + } + end + end + end + insert(prelude, build.group(node.body)) + node.body = prelude + end + return node + end, + ["if"] = function(self, node) + return build.block_exp({ + node + }) + end, + unless = function(self, node) + return build.block_exp({ + node + }) + end, + with = function(self, node) + return build.block_exp({ + node + }) + end, + switch = function(self, node) + return build.block_exp({ + node + }) + end, + chain = function(self, node) + for i = 2, #node do + local part = node[i] + if ntype(part) == "dot" and lua_keywords[part[2]] then + node[i] = { + "index", + { + "string", + '"', + part[2] + } + } + end + end + if ntype(node[2]) == "string" then + node[2] = { + "parens", + node[2] + } + end + if chain_is_stub(node) then + local base_name = NameProxy("base") + local fn_name = NameProxy("fn") + local colon = table.remove(node) + if not (node[2]) then + local scope_var = self:get("scope_var") + if not (scope_var) then + user_error("Short-colon syntax must be called within a with block", node[-1]) + end + node[2] = scope_var + end + local is_super = ntype(node[2]) == "ref" and node[2][2] == "super" + return build.block_exp({ + build.assign({ + names = { + base_name + }, + values = { + node + } + }), + build.assign({ + names = { + fn_name + }, + values = { + build.chain({ + base = base_name, + { + "dot", + colon[2] + } + }) + } + }), + build.fndef({ + args = { + { + "..." + } + }, + body = { + build.chain({ + base = fn_name, + { + "call", + { + is_super and "self" or base_name, + "..." + } + } + }) + } + }) + }) + end + end, + block_exp = function(self, node) + local body = unpack(node, 2) + local arg_list = { } + local fn = smart_node(build.fndef({ + body = body + })) + if has_varargs(body) then + insert(arg_list, "...") + insert(fn.args, { + "..." + }) + end + return build.chain({ + base = { + "parens", + fn + }, + { + "call", + arg_list + } + }) + end +}) diff --git a/moonscript/transform/value.moon b/moonscript/transform/value.moon new file mode 100644 index 00000000..74f4956e --- /dev/null +++ b/moonscript/transform/value.moon @@ -0,0 +1,236 @@ +import Transformer from require "moonscript.transform.transformer" +import build, ntype, smart_node from require "moonscript.types" + +import NameProxy from require "moonscript.transform.names" +import Accumulator, default_accumulator from require "moonscript.transform.accumulator" +import lua_keywords from require "moonscript.data" +import user_error from require "moonscript.errors" + +import transform_last_stm, implicitly_return, chain_is_stub, has_varargs from require "moonscript.transform.statements" + +import construct_comprehension from require "moonscript.transform.comprehension" +destructure = require "moonscript.transform.destructure" + +import insert from table +import unpack from require "moonscript.util" + +Transformer { + for: default_accumulator + while: default_accumulator + foreach: default_accumulator + + do: (node) => + build.block_exp node[2] + + decorated: (node) => + @transform.statement node + + class: (node) => + build.block_exp { node } + + string: (node) => + delim = node[2] + + convert_part = (part) -> + if type(part) == "string" or part == nil + {"string", delim, part or ""} + else + build.chain { base: "tostring", {"call", {part[2]}} } + + -- reduced to single item + if #node <= 3 + return if type(node[3]) == "string" + node + else + convert_part node[3] + + e = {"exp", convert_part node[3]} + + for i=4, #node + insert e, ".." + insert e, convert_part node[i] + e + + comprehension: (node) => + a = Accumulator! + node = @transform.statement node, (exp) -> + a\mutate_body {exp} + a\wrap node + + tblcomprehension: (node) => + explist, clauses = unpack node, 2 + key_exp, value_exp = unpack explist + + accum = NameProxy "tbl" + + inner = if value_exp + dest = build.chain { base: accum, {"index", key_exp} } + { build.assign_one dest, value_exp } + else + -- If we only have single expression then + -- unpack the result into key and value + key_name, val_name = NameProxy"key", NameProxy"val" + dest = build.chain { base: accum, {"index", key_name} } + { + build.assign names: {key_name, val_name}, values: {key_exp} + build.assign_one dest, val_name + } + + build.block_exp { + build.assign_one accum, build.table! + construct_comprehension inner, clauses + accum + } + + fndef: (node) => + smart_node node + node.body = transform_last_stm node.body, implicitly_return self + + -- from the first destructuring arg onward, argument initialization + -- (default checks, self assigns, unpacking) moves into the body in left + -- to right order, so a later default can reference an earlier + -- destructured name: + -- f = ({:a}, b = a) -> b + -- earlier args stay on the compiler's default handling, keeping output + -- for functions without destructuring unchanged + local first_destructure + for i, arg in ipairs node.args + if ntype(arg[1]) == "table" + first_destructure = i + break + + if first_destructure + -- a destructured name always shadows at the top of the body, so a + -- later parameter of the same name could never be seen. reject the + -- duplicate instead of silently breaking later-parameter-wins + bound_names = {} + -- fat arrow binds self as an implicit first parameter + bound_names.self = true if node.arrow == "fat" + for arg in *node.args + switch ntype arg[1] + when "self", "self_class" + bound_names[arg[1][2]] = true + when "table" + nil + else + bound_names[arg[1]] = true if type(arg[1]) == "string" + + seen_targets = {} + for arg in *node.args + continue unless ntype(arg[1]) == "table" + targets = [t for {t} in *destructure.extract_assign_names arg[1] when ntype(t) == "ref"] + + for target in *targets + name = target[2] + if bound_names[name] or seen_targets[name] + user_error "Can't destructure into '#{name}': name is bound by another parameter", target[-1] + + for target in *targets + seen_targets[target[2]] = true + + default_check = (name, value) -> + {"if", {"exp", name, "==", "nil"}, {{"assign", {name}, {value}}}} + + prelude = {} + for i=first_destructure, #node.args + arg = node.args[i] + name, default_value = arg[1], arg[2] + + switch ntype name + when "table" + proxy = NameProxy "arg" + if default_value + insert prelude, default_check proxy, default_value + insert prelude, destructure.build_assign @, name, proxy, shadow: true + node.args[i] = {proxy} + when "self", "self_class" + raw_name = name[2] + if default_value + insert prelude, default_check {"ref", raw_name}, default_value + insert prelude, build.assign_one name, {"ref", raw_name} + node.args[i] = {raw_name} + else + if default_value + insert prelude, default_check {"ref", name}, default_value + node.args[i] = {name} + + insert prelude, build.group node.body + node.body = prelude + + node + + if: (node) => + build.block_exp { node } + + unless: (node) => + build.block_exp { node } + + with: (node) => + build.block_exp { node } + + switch: (node) => + build.block_exp { node } + + -- pull out colon chain + chain: (node) => + -- escape lua keywords used in dot accessors + for i=2,#node + part = node[i] + if ntype(part) == "dot" and lua_keywords[part[2]] + node[i] = { "index", {"string", '"', part[2]} } + + if ntype(node[2]) == "string" + -- add parens if callee is raw string + node[2] = {"parens", node[2] } + + if chain_is_stub node + base_name = NameProxy "base" + fn_name = NameProxy "fn" + colon = table.remove node + + -- a stub with no base takes the scope from the enclosing with block + unless node[2] + scope_var = @get "scope_var" + unless scope_var + user_error "Short-colon syntax must be called within a with block", node[-1] + node[2] = scope_var + + is_super = ntype(node[2]) == "ref" and node[2][2] == "super" + build.block_exp { + build.assign { + names: {base_name} + values: {node} + } + + build.assign { + names: {fn_name} + values: { + build.chain { base: base_name, {"dot", colon[2]} } + } + } + + build.fndef { + args: {{"..."}} + body: { + build.chain { + base: fn_name, {"call", {is_super and "self" or base_name, "..."}} + } + } + } + } + + block_exp: (node) => + body = unpack node, 2 + + arg_list = {} + fn = smart_node build.fndef body: body + + -- if the body references varargs then the wrapper function must accept + -- and forward them so they remain visible + if has_varargs body + insert arg_list, "..." + insert fn.args, {"..."} + + build.chain { base: {"parens", fn}, {"call", arg_list} } +} + diff --git a/moonscript/types.lua b/moonscript/types.lua index fc5ec01f..e4edebcd 100644 --- a/moonscript/types.lua +++ b/moonscript/types.lua @@ -1,36 +1,99 @@ -module("moonscript.types", package.seeall) local util = require("moonscript.util") -local data = require("moonscript.data") -local insert = table.insert -manual_return = data.Set({ +local Set +Set = require("moonscript.data").Set +local insert +insert = table.insert +local unpack +unpack = util.unpack +local manual_return = Set({ "foreach", "for", "while", "return" }) -cascading = data.Set({ +local cascading = Set({ "if", "unless", "with", - "switch" + "switch", + "class", + "do" }) +local terminating = Set({ + "return", + "break" +}) +local ntype +ntype = function(node) + local _exp_0 = type(node) + if "nil" == _exp_0 then + return "nil" + elseif "table" == _exp_0 then + return node[1] + else + return "value" + end +end +local mtype +do + local moon_type = util.mtype + mtype = function(val) + local mt = getmetatable(val) + if mt and mt.smart_node then + return "table" + end + return moon_type(val) + end +end +local value_can_be_statement +value_can_be_statement = function(node) + if not (ntype(node) == "chain") then + return false + end + return ntype(node[#node]) == "call" +end +local is_value is_value = function(stm) - local compile, transform = moonscript.compile, moonscript.transform + local compile = require("moonscript.compile") + local transform = require("moonscript.transform") return compile.Block:is_value(stm) or transform.Value:can_transform(stm) end -comprehension_has_value = function(comp) - return is_value(comp[2]) +local is_assignable +do + local chain_assignable = { + index = true, + dot = true, + slice = true + } + is_assignable = function(node) + if node == "..." then + return false + end + local _exp_0 = ntype(node) + if "ref" == _exp_0 or "self" == _exp_0 or "value" == _exp_0 or "self_class" == _exp_0 or "table" == _exp_0 then + return true + elseif "chain" == _exp_0 then + return chain_assignable[ntype(node[#node])] + else + return false + end + end end -ntype = function(node) +local value_is_singular +value_is_singular = function(node) if type(node) ~= "table" then - return "value" + return true + end + local _exp_0 = node[1] + if "exp" == _exp_0 then + return #node == 2 and value_is_singular(node[2]) + elseif "length" == _exp_0 or "minus" == _exp_0 or "not" == _exp_0 or "bitnot" == _exp_0 then + return value_is_singular(node[2]) else - return node[1] + return true end end -value_is_singular = function(node) - return type(node) ~= "table" or node[1] ~= "exp" or #node == 2 -end +local is_slice is_slice = function(node) return ntype(node) == "chain" and ntype(node[#node]) == "slice" end @@ -170,9 +233,12 @@ make_builder = function(name) return node end end -build = nil +local build = nil build = setmetatable({ group = function(body) + if body == nil then + body = { } + end return { "group", body @@ -198,9 +264,8 @@ build = setmetatable({ if tbl == nil then tbl = { } end - local _list_0 = tbl - for _index_0 = 1, #_list_0 do - local tuple = _list_0[_index_0] + for _index_0 = 1, #tbl do + local tuple = tbl[_index_0] if type(tuple[1]) == "string" then tuple[1] = { "key_literal", @@ -221,13 +286,18 @@ build = setmetatable({ end, chain = function(parts) local base = parts.base or error("expecting base property for chain") + if type(base) == "string" then + base = { + "ref", + base + } + end local node = { "chain", base } - local _list_0 = parts - for _index_0 = 1, #_list_0 do - local part = _list_0[_index_0] + for _index_0 = 1, #parts do + local part = parts[_index_0] insert(node, part) end return node @@ -238,24 +308,48 @@ build = setmetatable({ return rawget(self, name) end }) -smart_node = function(node) - local index = key_table[ntype(node)] - if not index then - return node - end - return setmetatable(node, { - __index = function(node, key) - if index[key] then - return rawget(node, index[key]) - elseif type(key) == "string" then - return error("unknown key: `" .. key .. "` on node type: `" .. ntype(node) .. "`") - end - end, - __newindex = function(node, key, value) - if index[key] then - key = index[key] +local smart_node_mt = setmetatable({ }, { + __index = function(self, node_type) + local index = key_table[node_type] + local mt = { + smart_node = true, + __index = function(node, key) + if index[key] then + return rawget(node, index[key]) + elseif type(key) == "string" then + return error("unknown key: `" .. key .. "` on node type: `" .. ntype(node) .. "`") + end + end, + __newindex = function(node, key, value) + if index[key] then + key = index[key] + end + return rawset(node, key, value) end - return rawset(node, key, value) - end - }) + } + self[node_type] = mt + return mt + end +}) +local smart_node +smart_node = function(node) + return setmetatable(node, smart_node_mt[ntype(node)]) end +local NOOP = { + "noop" +} +return { + ntype = ntype, + smart_node = smart_node, + build = build, + is_value = is_value, + is_slice = is_slice, + is_assignable = is_assignable, + manual_return = manual_return, + cascading = cascading, + value_is_singular = value_is_singular, + value_can_be_statement = value_can_be_statement, + mtype = mtype, + terminating = terminating, + NOOP = NOOP +} diff --git a/moonscript/types.moon b/moonscript/types.moon index d072adb4..d2e08fce 100644 --- a/moonscript/types.moon +++ b/moonscript/types.moon @@ -1,37 +1,84 @@ -module "moonscript.types", package.seeall -util = require "moonscript.util" -data = require "moonscript.data" -export ntype, smart_node, build, is_value -export is_slice, manual_return, cascading, value_is_singular -export comprehension_has_value +util = require "moonscript.util" +import Set from require "moonscript.data" import insert from table +import unpack from util -- implicit return does not work on these statements -manual_return = data.Set{"foreach", "for", "while", "return"} +manual_return = Set { + "foreach", "for", "while", "return" +} -- Assigns and returns are bubbled into their bodies. -- All cascading statement transform functions accept a second arugment that -- is the transformation to apply to the last statement in their body -cascading = data.Set{ "if", "unless", "with", "switch" } - -is_value = (stm) -> - import compile, transform from moonscript - compile.Block\is_value(stm) or transform.Value\can_transform stm +cascading = Set { + "if", "unless", "with", "switch", "class", "do" +} -comprehension_has_value = (comp) -> - is_value comp[2] +terminating = Set { + "return", "break" +} -- type of node as string ntype = (node) -> - if type(node) != "table" - "value" - else - node[1] + switch type node + when "nil" + "nil" + when "table" + node[1] + else + "value" + +-- gets the class of a type if possible +mtype = do + moon_type = util.mtype + -- lets us check a smart node without throwing an error + (val) -> + mt = getmetatable val + return "table" if mt and mt.smart_node + moon_type val + +-- can this value be compiled in a line by itself +value_can_be_statement = (node) -> + return false unless ntype(node) == "chain" + -- it's a function call + ntype(node[#node]) == "call" + +is_value = (stm) -> + compile = require "moonscript.compile" + transform = require "moonscript.transform" + + compile.Block\is_value(stm) or transform.Value\can_transform stm +-- determines if node is able to be on left side of assignment +is_assignable = do + chain_assignable = { index: true, dot: true, slice: true } + + (node) -> + return false if node == "..." + switch ntype node + when "ref", "self", "value", "self_class", "table" + true + when "chain" + chain_assignable[ntype node[#node]] + else + false + +-- is this expression a single term that can sit next to a binary operator +-- without parentheses changing its meaning value_is_singular = (node) -> - type(node) != "table" or node[1] != "exp" or #node == 2 + return true if type(node) != "table" + switch node[1] + when "exp" + #node == 2 and value_is_singular node[2] + when "length", "minus", "not", "bitnot" + -- prefix operators parse greedily but compile without grouping, so a + -- compound operand renders with a trailing operator sequence + value_is_singular node[2] + else + true is_slice = (node) -> ntype(node) == "chain" and ntype(node[#node]) == "slice" @@ -102,10 +149,12 @@ make_builder = (name) -> build = nil build = setmetatable { - group: (body) -> + group: (body={}) -> {"group", body} + do: (body) -> {"do", body} + assign_one: (name, value) -> build.assign { names: {name} @@ -121,8 +170,13 @@ build = setmetatable { {"table", tbl} block_exp: (body) -> {"block_exp", body} + chain: (parts) -> base = parts.base or error"expecting base property for chain" + + if type(base) == "string" + base = {"ref", base} + node = {"chain", base} for part in *parts insert node, part @@ -133,19 +187,35 @@ build = setmetatable { rawget self, name } +smart_node_mt = setmetatable {}, { + __index: (node_type) => + index = key_table[node_type] + mt = { + smart_node: true + + __index: (node, key) -> + if index[key] + rawget node, index[key] + elseif type(key) == "string" + error "unknown key: `"..key.."` on node type: `"..ntype(node).. "`" + + __newindex: (node, key, value) -> + key = index[key] if index[key] + rawset node, key, value + } + self[node_type] = mt + mt +} + -- makes it so node properties can be accessed by name instead of index smart_node = (node) -> - index = key_table[ntype node] - if not index then return node - setmetatable node, { - __index: (node, key) -> - if index[key] - rawget node, index[key] - elseif type(key) == "string" - error "unknown key: `"..key.."` on node type: `"..ntype(node).. "`" - - __newindex: (node, key, value) -> - key = index[key] if index[key] - rawset node, key, value - } - + setmetatable node, smart_node_mt[ntype node] + +NOOP = {"noop"} + +{ + :ntype, :smart_node, :build, :is_value, :is_slice, :is_assignable, + :manual_return, :cascading, :value_is_singular, + :value_can_be_statement, :mtype, :terminating + :NOOP +} diff --git a/moonscript/util.lua b/moonscript/util.lua index a052ba0f..d1a8e759 100644 --- a/moonscript/util.lua +++ b/moonscript/util.lua @@ -1,68 +1,90 @@ -module("moonscript.util", package.seeall) -local concat = table.concat -moon = { - is_object = function(value) - return type(value) == "table" and value.__class +local concat +concat = table.concat +local unpack = unpack or table.unpack +local type = type +local moon = { + is_class = function(value) + if type(value) == "table" and rawget(value, "__base") ~= nil then + local mt = getmetatable(value) + return mt and rawget(mt, "__call") ~= nil + end + return false end, - type = function(value) - local base_type = type(value) - if base_type == "table" then - local cls = value.__class - if cls then - return cls - end + is_instance = function(value) + if type(value) == "table" then + local mt = getmetatable(value) + return mt and rawget(mt, "__index") == mt and rawget(value, "__index") ~= value end - return base_type + return false end } -pos_to_line = function(str, pos) +local mtype +mtype = function(value) + local base_type = type(value) + if base_type == "table" then + local cls = value.__class + if cls and rawget(value, "__class") == nil then + return cls + end + end + return base_type +end +local pos_to_line_col +pos_to_line_col = function(str, pos) local line = 1 - for _ in str:sub(1, pos):gmatch("\n") do + local line_start = 1 + while true do + local nl = str:find("\n", line_start, true) + if not (nl and nl < pos) then + break + end line = line + 1 + line_start = nl + 1 end + return line, pos - line_start + 1 +end +local pos_to_line +pos_to_line = function(str, pos) + local line = pos_to_line_col(str, pos) return line end -get_closest_line = function(str, line_num) - local line = get_line(str, line_num) - if (not line or trim(line) == "") and line_num > 1 then - return get_closest_line(str, line_num - 1) - else - return line, line_num - end +local trim +trim = function(str) + return str:match("^%s*(.-)%s*$") end +local get_line get_line = function(str, line_num) - for line in str:gmatch("(.-)[\n$]") do + for line in str:gmatch("([^\n]*)\n?") do if line_num == 1 then return line end line_num = line_num - 1 end end -reversed = function(seq) - return coroutine.wrap(function() - for i = #seq, 1, -1 do - coroutine.yield(i, seq[i]) - end - end) -end -trim = function(str) - return str:match("^%s*(.-)%s*$") +local get_closest_line +get_closest_line = function(str, line_num) + local line = get_line(str, line_num) + if (not line or trim(line) == "") and line_num > 1 then + return get_closest_line(str, line_num - 1) + else + return line, line_num + end end +local split split = function(str, delim) if str == "" then return { } end str = str .. delim - return (function() - local _accum_0 = { } - local _len_0 = 0 - for m in str:gmatch("(.-)" .. delim) do - _len_0 = _len_0 + 1 - _accum_0[_len_0] = m - end - return _accum_0 - end)() + local _accum_0 = { } + local _len_0 = 1 + for m in str:gmatch("(.-)" .. delim) do + _accum_0[_len_0] = m + _len_0 = _len_0 + 1 + end + return _accum_0 end +local dump dump = function(what) local seen = { } local _dump @@ -79,23 +101,129 @@ dump = function(what) end seen[what] = true depth = depth + 1 - local lines = (function() + local lines + do local _accum_0 = { } - local _len_0 = 0 + local _len_0 = 1 for k, v in pairs(what) do - local _value_0 = (" "):rep(depth * 4) .. "[" .. tostring(k) .. "] = " .. _dump(v, depth) - if _value_0 ~= nil then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = _value_0 - end + _accum_0[_len_0] = (" "):rep(depth * 4) .. "[" .. tostring(k) .. "] = " .. _dump(v, depth) + _len_0 = _len_0 + 1 end - return _accum_0 - end)() + lines = _accum_0 + end seen[what] = false - return "{\n" .. concat(lines) .. (" "):rep((depth - 1) * 4) .. "}\n" + local class_name + if type(what.__class) == "table" and type(what.__class.__name) == "string" then + class_name = "<" .. tostring(what.__class.__name) .. ">" + end + return (tostring(class_name or "") .. "{\n") .. concat(lines) .. (" "):rep((depth - 1) * 4) .. "}\n" else return tostring(what) .. "\n" end end return _dump(what) end +local debug_posmap +debug_posmap = function(posmap, moon_code, lua_code) + local tuples + do + local _accum_0 = { } + local _len_0 = 1 + for k, v in pairs(posmap) do + _accum_0[_len_0] = { + k, + v + } + _len_0 = _len_0 + 1 + end + tuples = _accum_0 + end + table.sort(tuples, function(a, b) + return a[1] < b[1] + end) + local lines + do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #tuples do + local pair = tuples[_index_0] + local lua_line, pos = unpack(pair) + local moon_line = pos_to_line(moon_code, pos) + local lua_text = get_line(lua_code, lua_line) + local moon_text = get_closest_line(moon_code, moon_line) + local _value_0 = tostring(pos) .. "\t " .. tostring(lua_line) .. ":[ " .. tostring(trim(lua_text)) .. " ] >> " .. tostring(moon_line) .. ":[ " .. tostring(trim(moon_text)) .. " ]" + _accum_0[_len_0] = _value_0 + _len_0 = _len_0 + 1 + end + lines = _accum_0 + end + return concat(lines, "\n") +end +local setfenv = setfenv or function(fn, env) + local name + local i = 1 + while true do + name = debug.getupvalue(fn, i) + if not name or name == "_ENV" then + break + end + i = i + 1 + end + if name then + debug.upvaluejoin(fn, i, (function() + return env + end), 1) + end + return fn +end +local getfenv = getfenv or function(fn) + local i = 1 + while true do + local name, val = debug.getupvalue(fn, i) + if not (name) then + break + end + if name == "_ENV" then + return val + end + i = i + 1 + end + return nil +end +local get_options +get_options = function(...) + local count = select("#", ...) + local opts = select(count, ...) + if type(opts) == "table" then + return opts, unpack({ + ... + }, nil, count - 1) + else + return { }, ... + end +end +local safe_module +safe_module = function(name, tbl) + return setmetatable(tbl, { + __index = function(self, key) + return error("Attempted to import non-existent `" .. tostring(key) .. "` from " .. tostring(name)) + end + }) +end +return { + moon = moon, + mtype = mtype, + pos_to_line_col = pos_to_line_col, + pos_to_line = pos_to_line, + get_closest_line = get_closest_line, + get_line = get_line, + trim = trim, + split = split, + dump = dump, + debug_posmap = debug_posmap, + getfenv = getfenv, + setfenv = setfenv, + get_options = get_options, + unpack = unpack, + safe_module = safe_module +} diff --git a/moonscript/util.moon b/moonscript/util.moon index f9927d55..880fe542 100644 --- a/moonscript/util.moon +++ b/moonscript/util.moon @@ -1,30 +1,58 @@ -module "moonscript.util", package.seeall - -export moon -export pos_to_line, get_closest_line, get_line -export reversed, trim, split -export dump - import concat from table -moon = - is_object: (value) -> -- is a moonscript object - type(value) == "table" and value.__class - type: (value) -> -- the moonscript object class - base_type = type value - if base_type == "table" - cls = value.__class - return cls if cls - base_type - --- convet position in text to line number -pos_to_line = (str, pos) -> +unpack = unpack or table.unpack +type = type + +moon = { + is_class: (value) -> + if type(value) == "table" and rawget(value, "__base") != nil + mt = getmetatable value + return mt and rawget(mt, "__call") != nil + false + + is_instance: (value) -> + if type(value) == "table" + mt = getmetatable value + return mt and rawget(mt, "__index") == mt and rawget(value, "__index") != value + false + +} + +mtype = (value) -> -- the moonscript object class + base_type = type value + if base_type == "table" + cls = value.__class + if cls and rawget(value, "__class") == nil + return cls + base_type + +-- convert position in text to line and column numbers +pos_to_line_col = (str, pos) -> line = 1 - for _ in str\sub(1, pos)\gmatch("\n") + line_start = 1 + while true + nl = str\find "\n", line_start, true + break unless nl and nl < pos line += 1 + line_start = nl + 1 + + line, pos - line_start + 1 + +-- convert position in text to line number +pos_to_line = (str, pos) -> + line = pos_to_line_col str, pos line +trim = (str) -> + str\match "^%s*(.-)%s*$" + +get_line = (str, line_num) -> + -- todo: this returns an extra blank line at the end + for line in str\gmatch "([^\n]*)\n?" + return line if line_num == 1 + line_num -= 1 + get_closest_line = (str, line_num) -> line = get_line str, line_num if (not line or trim(line) == "") and line_num > 1 @@ -32,19 +60,6 @@ get_closest_line = (str, line_num) -> else line, line_num -get_line = (str, line_num) -> - for line in str\gmatch "(.-)[\n$]" - return line if line_num == 1 - line_num -= 1 - -reversed = (seq) -> - coroutine.wrap -> - for i=#seq,1,-1 - coroutine.yield i, seq[i] - -trim = (str) -> - str\match "^%s*(.-)%s*$" - split = (str, delim) -> return {} if str == "" str ..= delim @@ -67,10 +82,70 @@ dump = (what) -> seen[what] = false - "{\n" .. concat(lines) .. (" ")\rep((depth - 1)*4) .. "}\n" + class_name = if type(what.__class) == "table" and type(what.__class.__name) == "string" + "<#{what.__class.__name}>" + + "#{class_name or ""}{\n" .. concat(lines) .. (" ")\rep((depth - 1)*4) .. "}\n" else tostring(what).."\n" _dump what +debug_posmap = (posmap, moon_code, lua_code) -> + tuples = [{k, v} for k, v in pairs posmap] + table.sort tuples, (a, b) -> a[1] < b[1] + + lines = for pair in *tuples + lua_line, pos = unpack pair + moon_line = pos_to_line moon_code, pos + + lua_text = get_line lua_code, lua_line + moon_text = get_closest_line moon_code, moon_line + + "#{pos}\t #{lua_line}:[ #{trim lua_text} ] >> #{moon_line}:[ #{trim moon_text} ]" + + concat(lines, "\n") + +setfenv = setfenv or (fn, env) -> + local name + i = 1 + while true + name = debug.getupvalue fn, i + break if not name or name == "_ENV" + i += 1 + + if name + debug.upvaluejoin fn, i, (-> env), 1 + + fn + +getfenv = getfenv or (fn) -> + i = 1 + while true + name, val = debug.getupvalue fn, i + break unless name + return val if name == "_ENV" + i += 1 + nil + +-- moves the last argument to the front if it's a table, or returns empty table +-- inserted to the front of args +get_options = (...) -> + count = select "#", ... + opts = select count, ... + if type(opts) == "table" + opts, unpack {...}, nil, count - 1 + else + {}, ... + +safe_module = (name, tbl) -> + setmetatable tbl, { + __index: (key) => + error "Attempted to import non-existent `#{key}` from #{name}" + } + +{ + :moon, :mtype, :pos_to_line_col, :pos_to_line, :get_closest_line, :get_line, :trim, :split, :dump, + :debug_posmap, :getfenv, :setfenv, :get_options, :unpack, :safe_module +} diff --git a/moonscript/version.lua b/moonscript/version.lua index f643c52d..006ef970 100644 --- a/moonscript/version.lua +++ b/moonscript/version.lua @@ -1,7 +1,17 @@ - -module("moonscript.version", package.seeall) - -version = "0.2.1-dev" -function print_version() - print("MoonScript version "..version) -end +local version = "0.8.0" +return { + version = version, + print_version = function() + do + local build = MOON_BUILD_INFO + if build then + print("MoonScript version " .. tostring(version) .. " (static build)") + print("Runtime: " .. tostring(jit and jit.version or build.lua)) + print("Commit: " .. tostring(build.commit)) + return print("Built: " .. tostring(build.time)) + else + return print("MoonScript version " .. tostring(version)) + end + end + end +} diff --git a/moonscript/version.moon b/moonscript/version.moon new file mode 100644 index 00000000..7e76451d --- /dev/null +++ b/moonscript/version.moon @@ -0,0 +1,15 @@ + +version = "0.8.0" + +{ + version: version, + print_version: -> + -- MOON_BUILD_INFO is only set by the static binary wrappers (bin/binaries) + if build = MOON_BUILD_INFO + print "MoonScript version #{version} (static build)" + print "Runtime: #{jit and jit.version or build.lua}" + print "Commit: #{build.commit}" + print "Built: #{build.time}" + else + print "MoonScript version #{version}" +} diff --git a/spec/README.md b/spec/README.md new file mode 100644 index 00000000..3ca43503 --- /dev/null +++ b/spec/README.md @@ -0,0 +1,91 @@ + +# MoonScript spec guide + +## Testing the right code + +Because MoonScript is written in MoonScript, and MoonScript specs are written +in MoonScript, you need to be aware of which copy of MoonScript is actually +executing the specs. + +A system installed version of MoonScript is recommended to run the specs (and +for development). This means that you'll typically have two versions of +MoonScript available in the load path: + +* The system version +* The version in the current directory + +> A system install is recommended because you'll always want a functioning +> version of MoonScript to compile with in case you break your development +> version. + +When developing you want to make ensure the tests are executing your changes in +the current directory, and not testing the system install. + +Busted itself is MoonScript aware, so it means it should have a functional +MoonScript compiler in order to load the `.moon` test files. This should be the +system install. After booting your specs though, you would like to use the +current directory version of MoonScript to the test + +Because by default Busted will have the system install take precedence over the +loaded version, running `require "moonscript.base"` within a test you won't get +the working directory version of the code that you should be testing. + +The `with_dev` spec helper will ensure that any require calls within the spec +that ask for MoonScript modules. `with_dev` calls a setup and teardown that +replaces `_G.require` with a custom version. + +You'll use it like this: + +```moonscript +import with_dev from require "spec.helpers" +describe "moonscript.base", -> + with_dev! + + it "should load code", -> + -- the local version is loaded + moonscript = require "moonscript" + moonscript.load "print 12" +``` + +Note that `with_dev`'s `require` function will not use the MoonLoader, it will +only load the `.lua` files in the working directory directory, not the `moon` +ones. This means you must compile the working directory version of MoonScript +before running the tests. + +There is a make task to conveniently do all of this: + +``` +make test +``` + +## Building syntax tests + +The test suite has a series of *syntax* tests (`spec/lang_spec.moon`) that +consist of a bunch of `.moon` files and their expected output. These files +should capture a large range of syntax that can be verified to have the correct +output when you make changes to the language. + +If you are adding new syntax, or changing the expected output, then these tests +will fail until you rebuild the expected outputs. You can do this by running +the syntax test suite with the `BUILD` environment variable set. + +There is a make task to conveniently do this: + +``` +make build_test_outputs +``` + +## Performance timing + +The syntax specs have performance timing collection built in. To get these +times run the test suite with the `TIME` environment variable set. + +``` +TIME=1 busted spec/lang_spec.moon +``` + +Any changes to the compiler should not introduce any substantial performance +decreases. + + + diff --git a/spec/class_spec.moon b/spec/class_spec.moon new file mode 100644 index 00000000..394c59b1 --- /dev/null +++ b/spec/class_spec.moon @@ -0,0 +1,297 @@ + +describe "class", -> + it "should make a class with constructor", -> + class Thing + new: => + @color = "blue" + + instance = Thing! + + assert.same instance, { color: "blue" } + + it "should have instance methods", -> + class Thing + get_color: => @color + + new: => + @color = "blue" + + instance = Thing! + assert.same instance\get_color!, "blue" + + it "should have base properies from class", -> + class Thing + color: "blue" + get_color: => @color + + instance = Thing! + assert.same instance\get_color!, "blue" + assert.same Thing.color, "blue" + + it "should inherit another class", -> + class Base + get_property: => @[@property] + + new: (@property) => + + class Thing extends Base + color: "green" + + instance = Thing "color" + assert.same instance\get_property!, "green" + + + it "should have class properties", -> + class Base + class Thing extends Base + + instance = Thing! + + assert.same Base.__name, "Base" + assert.same Thing.__name, "Thing" + assert.is_true Thing.__parent == Base + + assert.is_true instance.__class == Thing + + it "should have name when assigned", -> + Thing = class + assert.same Thing.__name, "Thing" + + it "should not expose class properties on instance", -> + class Thing + @height: 10 + + Thing.color = "blue" + + instance = Thing! + assert.same instance.color, nil + assert.same instance.height, nil + + it "should expose new things added to __base", -> + class Thing + + instance = Thing! + Thing.__base.color = "green" + + assert.same instance.color, "green" + + it "should call with correct receiver", -> + local instance + + class Thing + is_class: => assert.is_true @ == Thing + is_instance: => assert.is_true @ == instance + + go: => + @@is_class! + @is_instance! + + instance = Thing! + instance\go! + + it "should have class properies take precedence over base properties", -> + class Thing + @prop: "hello" + prop: "world" + + assert.same "hello", Thing.prop + + describe "super", -> + it "should call super constructor", -> + class Base + new: (@property) => + + class Thing extends Base + new: (@name) => + super "name" + + instance = Thing "the_thing" + + assert.same instance.property, "name" + assert.same instance.name, "the_thing" + + it "should call super method", -> + class Base + _count: 111 + counter: => @_count + + class Thing extends Base + counter: => "%08d"\format super! + + instance = Thing! + assert.same instance\counter!, "00000111" + + it "should call other method from super", -> + class Base + _count: 111 + counter: => + @_count + + class Thing extends Base + other_method: => super\counter! + + instance = Thing! + assert.same instance\other_method!, 111 + + it "should get super class", -> + class Base + class Thing extends Base + get_super: => super + + instance = Thing! + assert.is_true instance\get_super! == Base + + it "should get a bound method from super", -> + class Base + count: 1 + get_count: => @count + + class Thing extends Base + get_count: => "this is wrong" + get_method: => super\get_count + + instance = Thing! + assert.same instance\get_method!!, 1 + + it "class properties take precedence in super class over base", -> + class Thing + @prop: "hello" + prop: "world" + + class OtherThing extends Thing + + assert.same "hello", OtherThing.prop + + it "gets value from base in super class", -> + class Thing + prop: "world" + + class OtherThing extends Thing + assert.same "world", OtherThing.prop + + it "should let parent be replaced on class", -> + class A + @prop: "yeah" + cool: => 1234 + plain: => "a" + + class B + @prop: "okay" + cool: => 9999 + plain: => "b" + + class Thing extends A + cool: => + super! + 1 + + get_super: => + super + + instance = Thing! + + assert.same "a", instance\plain! + assert.same 1235, instance\cool! + assert A == instance\get_super!, "expected super to be B" + + Thing.__parent = B + setmetatable Thing.__base, B.__base + + assert.same "b", instance\plain! + assert.same 10000, instance\cool! + assert B == instance\get_super!, "expected super to be B" + + it "should resolve many levels of super", -> + class One + a: => + 1 + + class Two extends One + a: => + super! + 2 + + class Three extends Two + a: => + super! + 3 + + i = Three! + + assert.same 6, i\a! + + + it "should resolve many levels of super with a gap", -> + class One + a: => + 1 + + class Two extends One + + class Three extends Two + a: => + super! + 3 + + class Four extends Three + a: => + super! + 4 + + i = Four! + + assert.same 8, i\a! + + + it "should call correct class/instance super methods", -> + class Base + doit: => + "instance" + + @doit: => + "class" + + class One extends Base + doit: => super! + @doit: => super! + + assert.same "instance", One!\doit! + assert.same "class", One\doit! + + + it "should resolve many levels of super on class methods", -> + class One + @a: => + 1 + + class Two extends One + + class Three extends Two + @a: => + super! + 3 + + class Four extends Three + @a: => + super! + 4 + + assert.same 8, Four\a! + + it "super should still work when method wrapped", -> + add_some = (opts) -> + => opts.amount + opts[1] @ + + class Base + value: => 1 + + class Sub extends Base + value: add_some { + amount: 12 + => + super! + 100 + } + + class OtherSub extends Base + value: if true + => 5 + super! + else + => 2 + super! + + assert.same 1 + 100 + 12, Sub!\value! + assert.same 6, OtherSub!\value! + + diff --git a/spec/cmd_spec.moon b/spec/cmd_spec.moon new file mode 100644 index 00000000..b6e2be6e --- /dev/null +++ b/spec/cmd_spec.moon @@ -0,0 +1,106 @@ + +import with_dev from require "spec.helpers" + +-- TODO: add specs for windows equivalents + +describe "moonc", -> + local moonc + + dev_loaded = with_dev -> + moonc = require "moonscript.cmd.moonc" + + same = (fn, a, b) -> + assert.same b, fn a + + it "should normalize dir", -> + same moonc.normalize_dir, "hello/world/", "hello/world/" + same moonc.normalize_dir, "hello/world//", "hello/world/" + same moonc.normalize_dir, "", "/" -- wrong + same moonc.normalize_dir, "hello", "hello/" + + it "should parse dir", -> + same moonc.parse_dir, "/hello/world/file", "/hello/world/" + same moonc.parse_dir, "/hello/world/", "/hello/world/" + same moonc.parse_dir, "world", "" + same moonc.parse_dir, "", "" + + it "should parse file", -> + same moonc.parse_file, "/hello/world/file", "file" + same moonc.parse_file, "/hello/world/", "" + same moonc.parse_file, "world", "world" + same moonc.parse_file, "", "" + + it "convert path", -> + same moonc.convert_path, "test.moon", "test.lua" + same moonc.convert_path, "/hello/file.moon", "/hello/file.lua" + same moonc.convert_path, "/hello/world/file", "/hello/world/file.lua" + + it "calculate target", -> + p = moonc.path_to_target + + assert.same "test.lua", p "test.moon" + assert.same "hello/world.lua", p "hello/world.moon" + assert.same "compiled/test.lua", p "test.moon", "compiled" + + assert.same "/home/leafo/test.lua", p "/home/leafo/test.moon" + assert.same "compiled/test.lua", p "/home/leafo/test.moon", "compiled" + assert.same "/compiled/test.lua", p "/home/leafo/test.moon", "/compiled/" + + assert.same "moonscript/hello.lua", p "moonscript/hello.moon", nil, "moonscript" + assert.same "out/moonscript/hello.lua", p "moonscript/hello.moon", "out", "moonscript" + + assert.same "out/moonscript/package/hello.lua", + p "moonscript/package/hello.moon", "out", "moonscript/" + + assert.same "/out/moonscript/package/hello.lua", + p "/home/leafo/moonscript/package/hello.moon", "/out", "/home/leafo/moonscript" + + it "should compile file text", -> + assert.same { + [[return print('hello')]] + }, { + moonc.compile_file_text "print'hello'", fname: "test.moon" + } + + describe "watcher", -> + describe "inotify watcher", -> + it "gets dirs", -> + import InotifyWacher from require "moonscript.cmd.watchers" + watcher = InotifyWacher { + {"hello.moon", "hello.lua"} + {"cool/no.moon", "cool/no.lua"} + } + + assert.same { + "./" + "cool/" + }, watcher\get_dirs! + + describe "stubbed lfs", -> + local dirs + + before_each -> + dirs = {} + package.loaded.lfs = nil + dev_loaded["moonscript.cmd.moonc"] = nil + + package.loaded.lfs = { + mkdir: (dir) -> table.insert dirs, dir + attributes: -> "directory" + } + + moonc = require "moonscript.cmd.moonc" + + after_each -> + package.loaded.lfs = nil + dev_loaded["moonscript.cmd.moonc"] = nil + moonc = require "moonscript.cmd.moonc" + + it "should make directory", -> + moonc.mkdir "hello/world/directory" + assert.same { + "hello" + "hello/world" + "hello/world/directory" + }, dirs + diff --git a/spec/compile_error_spec.moon b/spec/compile_error_spec.moon new file mode 100644 index 00000000..870663cf --- /dev/null +++ b/spec/compile_error_spec.moon @@ -0,0 +1,157 @@ +import with_dev, unindent from require "spec.helpers" + +-- tests the user facing compile errors triggered by invalid code, including +-- the source position they point at +describe "compile errors", -> + local to_error, parse_fails + + with_dev -> + parse = require "moonscript.parse" + compile = require "moonscript.compile" + import pos_to_line from require "moonscript.util" + + to_error = (str) -> + tree = assert parse.string str + code, err, pos = compile.tree tree + assert.is_nil code, "expected compile to fail" + err, pos and pos_to_line str, pos + + parse_fails = (str) -> + tree, err = parse.string str + assert.is_nil tree, "expected parse to fail: #{str}" + assert.truthy err + + for {name, code_str, expected_msg, expected_line} in *{ + { + "short-colon stub outside of with" + unindent [[ + print "hello" + x = \foo + ]] + "Short-colon syntax must be called within a with block" + 2 + } + + { + "short-dot outside of with" + unindent [[ + print "hello" + print "world" + x = .field + ]] + "Short-dot syntax must be called within a with block" + 3 + } + + { + "destructuring invalid value" + unindent [[ + print "hello" + {1} = thing + ]] + "Can't destructure value of type: number" + 2 + } + + { + "destructuring into a function call" + unindent [[ + print "hello" + {foo!} = thing + ]] + "Can't destructure into chain ending in call" + 2 + } + + { + "destructuring function argument into call" + unindent [[ + print "hello" + f = ({foo!}) -> foo + ]] + "Can't destructure into chain ending in call" + 2 + } + + { + "destructuring function argument into invalid value" + unindent [[ + print "hello" + print "world" + f = ({1}) -> nil + ]] + "Can't destructure value of type: number" + 3 + } + + { + "destructured parameter followed by parameter of same name" + unindent [[ + print "hello" + f = ({:a}, a) -> a + ]] + "Can't destructure into 'a': name is bound by another parameter" + 2 + } + + { + "destructured parameter preceded by parameter of same name" + unindent [[ + print "hello" + f = (a, {:a}) -> a + ]] + "Can't destructure into 'a': name is bound by another parameter" + 2 + } + + { + "destructured parameter followed by self parameter of same name" + unindent [[ + print "hello" + f = ({:a}, @a) => a + ]] + "Can't destructure into 'a': name is bound by another parameter" + 2 + } + + { + "destructured self parameter in fat arrow" + unindent [[ + print "hello" + f = ({:self}) => @x + ]] + "Can't destructure into 'self': name is bound by another parameter" + 2 + } + + { + "two destructured parameters binding same name" + unindent [[ + print "hello" + f = ({:a}, {a: a}) -> a + ]] + "Can't destructure into 'a': name is bound by another parameter" + 2 + } + + { + "continue outside of loop" + unindent [[ + print "hello" + continue + ]] + "continue must be inside of a loop" + 2 + } + } + it name, -> + err, line = to_error code_str + assert.same expected_msg, err + assert.same expected_line, line + + -- a malformed interpolation must fail the parse instead of falling + -- through to literal string content + it "invalid string interpolation fails to parse", -> + parse_fails [[x = "one #{three ..} two"]] + parse_fails [[x = "abc#{"]] + parse_fails [[x = "#{}"]] diff --git a/spec/compiler_spec.moon b/spec/compiler_spec.moon new file mode 100644 index 00000000..3ab9e0d6 --- /dev/null +++ b/spec/compiler_spec.moon @@ -0,0 +1,221 @@ +import ref, str from require "spec.factory" +import with_dev from require "spec.helpers" + +describe "moonscript.compile", -> + local compile_node + + with_dev -> + import Block from require "moonscript.compile" + + -- no transform step + class SimpleBlock extends Block + new: (...) => + super ... + @transform = { + value: (...) -> ... + statement: (...) -> ... + } + + compile_node = (node) -> + block = SimpleBlock! + block\add block\value node + lines = block._lines\flatten! + lines[#lines] = nil if lines[#lines] == "\n" + table.concat lines + + -- compiling lua ast + describe "value", -> + for {name, node, expected} in *{ + { + "ref" + -> {"ref", "hello_world"} + "hello_world" + } + + { + "number" + -> {"number", "14"} + "14" + } + + { + "minus" + -> {"minus", ref!} + "-val" + } + + { + "explist" + -> { "explist", ref("a"), ref("b"), ref("c")} + "a, b, c" + } + + { + "exp" + -> {"exp", ref("a"), "+", ref("b"), "!=", ref("c")} + "a + b ~= c" + } + + { + "exp (nested under tighter operator)" + -> {"exp", {"exp", ref("a"), "..", ref("b")}, "*", ref("c")} + "(a .. b) * c" + } + + { + "exp (nested under looser operator)" + -> {"exp", ref("a"), "==", {"exp", ref("b"), "..", ref("c")}} + "a == b .. c" + } + + { + "exp (nested right of equal precedence left associative)" + -> {"exp", ref("a"), "-", {"exp", ref("b"), "+", ref("c")}} + "a - (b + c)" + } + + { + "exp (nested concat left of concat keeps grouping)" + -> {"exp", {"exp", ref("a"), "..", ref("b")}, "..", ref("c")} + "(a .. b) .. c" + } + + { + "exp (nested concat right of concat stays flat)" + -> {"exp", ref("a"), "..", {"exp", ref("b"), "..", ref("c")}} + "a .. b .. c" + } + + { + "exp (nested left of right associative operator)" + -> {"exp", {"exp", ref("x"), "^", ref("y")}, "^", ref("b")} + "(x ^ y) ^ b" + } + + { + "parens" + -> { "parens", ref! } + "(val)" + } + + { + "string (single quote)" + -> {"string", "'", "Hello\\'s world"} + "'Hello\\'s world'" + } + + { + "string (double quote)" + -> {"string", '"', "Hello's world"} + [["Hello's world"]] + + } + + { + "string (lua)" + -> {"string", '[==[', "Hello's world"} + "[==[Hello's world]==]" + } + + { + "self" + -> {"self", ref!} + "self.val" + } + + { + "self_class" + -> {"self_class", ref!} + "self.__class.val" + } + + { + "self_class_colon" + -> {"self_class_colon", ref!} + "self.__class:val" + } + + { + "not" + -> {"not", ref!} + "not val" + } + + { + "length" + -> {"length", ref!} + "#val" + } + + { + "length" + -> {"length", ref!} + "#val" + } + + { + "bitnot" + -> {"bitnot", ref!} + "~val" + } + + { + "chain (single)" + -> {"chain", ref!} + "val" + } + + { + "chain (dot)" + -> {"chain", ref!, {"dot", "zone"} } + "val.zone" + } + + { + "chain (index)" + -> {"chain", ref!, {"index", ref("x") } } + "val[x]" + } + + + { + "chain (call)" + -> {"chain", ref!, {"call", { ref("arg") }} } + "val(arg)" + } + + { + "chain" + -> { + "chain" + ref! + {"dot", "one"} + {"index", str!} + {"colon", "two"} + {"call", { ref("arg") }} + } + 'val.one["dogzone"]:two(arg)' + } + + { + "chain (self receiver)" + -> { + "chain" + {"self", ref!} + {"call", {ref "arg"} } + } + "self:val(arg)" + } + + { + "fndef (empty)" + -> {"fndef", {}, {}, "slim", {}} + "function() end" + } + + } + it "compiles #{name}", -> + node = node! + assert.same expected, compile_node(node) + + diff --git a/spec/comprehension_spec.moon b/spec/comprehension_spec.moon new file mode 100644 index 00000000..d8823728 --- /dev/null +++ b/spec/comprehension_spec.moon @@ -0,0 +1,77 @@ + +import unpack from require "moonscript.util" + +describe "comprehension", -> + it "should double every number", -> + input = {1,2,3,4,5,6} + output_1 = [i * 2 for _, i in pairs input ] + output_2 = [i * 2 for i in *input ] + + assert.same output_1, {2,4,6,8,10,12} + + it "should create a slice", -> + input = {1,2,3,4,5,6} + + slice_1 = [i for i in *input[1,3]] + slice_2 = [i for i in *input[,3]] + + slice_3 = [i for i in *input[3,]] + slice_4 = [i for i in *input[,]] + + slice_5 = [i for i in *input[,,2]] + slice_6 = [i for i in *input[2,,2]] + + assert.same slice_1, {1,2,3} + assert.same slice_1, slice_2 + assert.same slice_3, {3,4,5,6} + assert.same slice_4, input + + assert.same slice_5, {1,3,5} + assert.same slice_6, {2,4,6} + + it "should be able to assign to self", -> + input = {1,2,3,4} + output = input + output = [i * 2 for i in *output] + + assert.same input, {1,2,3,4} + assert.same output, {2,4,6,8} + + output = input + output = [i * 2 for _,i in ipairs input] + + assert.same input, {1,2,3,4} + assert.same output, {2,4,6,8} + + +describe "table comprehension", -> + it "should copy table", -> + input = { 1,2,3, hello: "world", thing: true } + output = {k,v for k,v in pairs input } + + assert.is_true input != output + assert.same input, output + + it "should support when", -> + input = { + color: "red" + name: "fast" + width: 123 + } + + output = { k,v for k,v in pairs input when k != "color" } + + assert.same output, { name: "fast", width: 123 } + + it "should do unpack", -> + input = {4,9,16,25} + output = {i, math.sqrt i for i in *input} + + assert.same output, { [4]: 2, [9]: 3, [16]: 4, [25]: 5 } + + it "should use multiple return values", -> + input = { {"hello", "world"}, {"foo", "bar"} } + output = { unpack tuple for tuple in *input } + + assert.same output, { foo: "bar", hello: "world" } + diff --git a/spec/coverage_output_handler.moon b/spec/coverage_output_handler.moon new file mode 100644 index 00000000..0043e462 --- /dev/null +++ b/spec/coverage_output_handler.moon @@ -0,0 +1,51 @@ + +load_line_table = (chunk_name) -> + import to_lua from require "moonscript.base" + + return unless chunk_name\match "^@" + fname = chunk_name\sub 2 + + file = assert io.open fname + code = file\read "*a" + file\close! + + c, ltable = to_lua code + + return nil, ltable unless c + + line_tables = require "moonscript.line_tables" + line_tables[chunk_name] = ltable + true + +(options) -> + busted = require "busted" + handler = require("busted.outputHandlers.utfTerminal") options + + local spec_name + + coverage = require "moonscript.cmd.coverage" + cov = coverage.CodeCoverage! + + busted.subscribe { "test", "start" }, (context) -> + cov\start! + + busted.subscribe { "test", "end" }, -> + cov\stop! + + busted.subscribe { "suite", "end" }, (context) -> + line_counts = {} + + for chunk_name, counts in pairs cov.line_counts + continue unless chunk_name\match("^@$./") or chunk_name\match "@[^/]" + continue if chunk_name\match "^@spec/" + + if chunk_name\match "%.lua$" + chunk_name = chunk_name\gsub "lua$", "moon" + continue unless load_line_table chunk_name + + line_counts[chunk_name] = counts + + cov.line_counts = line_counts + cov\format_results! + + handler diff --git a/spec/destructure_spec.moon b/spec/destructure_spec.moon new file mode 100644 index 00000000..bd3bce98 --- /dev/null +++ b/spec/destructure_spec.moon @@ -0,0 +1,119 @@ +describe "destructure", -> + it "should unpack array", -> + input = {1,2,3} + + {a,b,c} = {1,2,3} + {d,e,f} = input + + assert.same a, 1 + assert.same b, 2 + assert.same c, 3 + + assert.same d, 1 + assert.same e, 2 + assert.same f, 3 + + it "should destructure", -> + futurists = + sculptor: "Umberto Boccioni" + painter: "Vladimir Burliuk" + poet: + name: "F.T. Marinetti" + address: { + "Via Roma 42R" + "Bellagio, Italy 22021" + } + + {poet: {:name, address: {street, city}}} = futurists + + assert.same name, "F.T. Marinetti" + assert.same street, "Via Roma 42R" + assert.same city, "Bellagio, Italy 22021" + + +-- argument destructuring is new syntax, so sources are compiled with the dev +-- compiler at runtime instead of being written directly in this file +describe "function argument destructure", -> + import with_dev, unindent from require "spec.helpers" + + local run + + with_dev -> + parse = require "moonscript.parse" + compile = require "moonscript.compile" + + run = (str) -> + tree = assert parse.string str + code, err = compile.tree tree + assert code, err + chunk = assert (loadstring or load) code + chunk! + + it "unpacks fields", -> + result = run unindent [[ + f = ({:a, :b, :c}) -> a + b + c + return f {a: 1, b: 2, c: 3} + ]] + assert.same 6, result + + it "unpacks multiple table arguments", -> + result = run unindent [[ + dot = ({x: x1, y: y1}, {x: x2, y: y2}) -> x1 * x2 + y1 * y2 + return dot {x: 1, y: 2}, {x: 3, y: 4} + ]] + assert.same 11, result + + it "applies default value", -> + result = run unindent [[ + f = ({:a, :b} = {a: 1, b: 2}) -> a + b + return {f!, f {a: 10, b: 20}} + ]] + assert.same {3, 30}, result + + it "shadows outer variable instead of assigning it", -> + result = run unindent [[ + x = "outer" + f = ({:x}) -> x + inner = f {x: "inner"} + return {inner, x} + ]] + assert.same {"inner", "outer"}, result + + it "assigns self fields with fat arrow", -> + result = run unindent [[ + obj = {total: 10} + obj.add = ({:count}) => @total + count + return obj\add {count: 5} + ]] + assert.same 15, result + + it "preserves surrounding args and varargs", -> + result = run unindent [[ + f = (first, {:mid}, ...) -> {first, mid, select "#", ...} + return f "a", {mid: "b"}, "x", "y" + ]] + assert.same {"a", "b", 2}, result + + it "later defaults see earlier destructured names", -> + result = run unindent [[ + a = "outer" + f = ({:a}, b = a) -> b + return f {a: 42} + ]] + assert.same 42, result + + it "lowers self assigns after destructure in order", -> + result = run unindent [[ + obj = {} + obj.set = ({:base}, @x = base * 2) => @x + first = obj\set {base: 3} + return {first, obj.x, obj\set({base: 3}, 99)} + ]] + assert.same {6, 6, 99}, result + + it "chains destructure defaults left to right", -> + result = run unindent [[ + f = ({:a} = {a: 1}, {:b} = {b: a + 10}) -> {a, b} + return f! + ]] + assert.same {1, 11}, result diff --git a/tests/error_inputs/first.moon b/spec/error_inputs/first.moon similarity index 95% rename from tests/error_inputs/first.moon rename to spec/error_inputs/first.moon index 15afa9e3..c8d45fa4 100644 --- a/tests/error_inputs/first.moon +++ b/spec/error_inputs/first.moon @@ -1,6 +1,8 @@ hello_world = false +print = -> + if hello_world print "sackman" x = 5 + 5 diff --git a/tests/error_inputs/second.moon b/spec/error_inputs/second.moon similarity index 92% rename from tests/error_inputs/second.moon rename to spec/error_inputs/second.moon index 053dcca3..d9b7e689 100644 --- a/tests/error_inputs/second.moon +++ b/spec/error_inputs/second.moon @@ -1,4 +1,5 @@ +print = -> if false true diff --git a/tests/error_inputs/third.moon b/spec/error_inputs/third.moon similarity index 96% rename from tests/error_inputs/third.moon rename to spec/error_inputs/third.moon index 08ef1d7d..b3c1e2e9 100644 --- a/tests/error_inputs/third.moon +++ b/spec/error_inputs/third.moon @@ -1,4 +1,6 @@ +print = -> + if (-> print "hello world" print "who is this") diff --git a/spec/error_rewriting_spec.moon b/spec/error_rewriting_spec.moon new file mode 100644 index 00000000..c3616116 --- /dev/null +++ b/spec/error_rewriting_spec.moon @@ -0,0 +1,71 @@ + +import unindent, with_dev from require "spec.helpers" + +describe "moonscript.errors", -> + local moonscript, errors, util, to_lua + + -- with_dev -> + moonscript = require "moonscript.base" + errors = require "moonscript.errors" + util = require "moonscript.util" + + {:to_lua} = moonscript + + get_rewritten_line_no = (fname) -> + fname = "spec/error_inputs/#{fname}.moon" + chunk = moonscript.loadfile fname + + success, err = pcall chunk + error "`#{fname}` is supposed to have runtime error!" if success + + source = tonumber err\match "^.-:(%d+):" + + line_table = assert require("moonscript.line_tables")["@#{fname}"], "missing line table" + errors.reverse_line_number fname, line_table, source, {} + + describe "error rewriting", -> + tests = { + "first": 24 + "second": 16 + "third": 11 + } + + for name, expected_no in pairs tests + it "should rewrite line number", -> + assert.same get_rewritten_line_no(name), expected_no + + describe "line map", -> + it "should create line table", -> + moon_code = unindent [[ + print "hello world" + if something + print "cats" + ]] + + lua_code, posmap = assert to_lua moon_code + -- print util.debug_posmap(posmap, moon_code, lua_code) + assert.same { 1, 23, 36, 21 }, posmap + + it "should create line table for multiline string", -> + moon_code = unindent [[ + print "one" + x = [==[ + one + two + thre + yes + no + ]==] + print "two" + ]] + + lua_code, posmap = assert to_lua moon_code + -- print util.debug_posmap(posmap, moon_code, lua_code) + assert.same {[1]: 1, [2]: 13, [7]: 13, [8]: 57}, posmap + + describe "error reporting", -> + it "should compile bad code twice", -> + code, err = to_lua "{b=5}" + assert.truthy err + code, err2 = to_lua "{b=5}" + assert.same err, err2 diff --git a/spec/factory.moon b/spec/factory.moon new file mode 100644 index 00000000..a6f3d76c --- /dev/null +++ b/spec/factory.moon @@ -0,0 +1,13 @@ + +-- ast factory + +ref = (name="val") -> + {"ref", name} + +str = (contents="dogzone", delim='"') -> + {"string", delim, contents} + +{ + :ref + :str +} diff --git a/spec/helpers.moon b/spec/helpers.moon new file mode 100644 index 00000000..808381e3 --- /dev/null +++ b/spec/helpers.moon @@ -0,0 +1,60 @@ + +-- remove front indentation from a multiline string, making it suitable to be +-- parsed +unindent = (str) -> + indent = str\match "^%s+" + return str unless indent + (str\gsub("\n#{indent}", "\n")\gsub("%s+$", "")\gsub "^%s+", "") + +in_dev = false + +-- this will ensure any moonscript modules included come from the local +-- directory +with_dev = (fn) -> + error "already in dev mode" if in_dev + + -- a package loader that only looks in currect directory + import make_loader from require "loadkit" + loader = make_loader "lua", nil, "./?.lua" + + import setup, teardown from require "busted" + + old_require = _G.require + dev_cache = {} + + setup -> + _G.require = (mod) -> + mod = switch mod + when "moonscript" + "moonscript.init" + when "moon" + "moon.init" + + return dev_cache[mod] if dev_cache[mod] + + testable = mod\match("moonscript%.") or mod == "moonscript" or + mod\match("moon%.") or mod == "moon" + + if testable + -- the compiled parser can't be loaded from source; resolve it + -- through the regular require (package.preload from the + -- use_slow_parser helper, ./?.so, or the installed rock) + if mod == "moonscript.parse.native" + return old_require mod + + fname = assert loader(mod), "failed to find module: #{mod}" + dev_cache[mod] = assert(loadfile fname)! + return dev_cache[mod] + + old_require mod + + if fn + fn! + + teardown -> + _G.require = old_require + in_dev = false + + dev_cache + +{ :unindent, :with_dev } diff --git a/spec/import_spec.moon b/spec/import_spec.moon new file mode 100644 index 00000000..bb1472cd --- /dev/null +++ b/spec/import_spec.moon @@ -0,0 +1,26 @@ +describe "import", -> + it "should import from table", -> + import sort, insert from table + t = { 4,2,6 } + insert t, 1 + sort t + + assert.same t, {1,2,4,6} + + it "should import from local", -> + thing = { var: 10, hello: "world", func: => @var } + import hello, \func from thing + + assert.same hello, thing.hello + assert.same func!, thing.var + + it "should not call source multiple times", -> + count = 0 + source = -> + count += 1 + { hello: "world", foo: "bar" } + + import hello, foo from source! + + assert.same count, 1 + diff --git a/spec/inputs/ambiguous.moon b/spec/inputs/ambiguous.moon new file mode 100644 index 00000000..66109c11 --- /dev/null +++ b/spec/inputs/ambiguous.moon @@ -0,0 +1,8 @@ +a = 'b' +c = d +(a b) c d +import c from d +(a b) c d +(c d) a b +a, b = c, d +(d a) c diff --git a/spec/inputs/ambiguous_tables.moon b/spec/inputs/ambiguous_tables.moon new file mode 100644 index 00000000..c2c7ea8c --- /dev/null +++ b/spec/inputs/ambiguous_tables.moon @@ -0,0 +1,6 @@ +x = { +hello +(one) +(two) +three() +} diff --git a/tests/inputs/assign.moon b/spec/inputs/assign.moon similarity index 80% rename from tests/inputs/assign.moon rename to spec/inputs/assign.moon index 1e5e7a6f..59a6f7f8 100644 --- a/tests/inputs/assign.moon +++ b/spec/inputs/assign.moon @@ -28,3 +28,9 @@ else + +-- multiple names with single complex value + +a, b = x\fn + +a, b, c = x\fn, 1 diff --git a/tests/inputs/bubbling.moon b/spec/inputs/bubbling.moon similarity index 100% rename from tests/inputs/bubbling.moon rename to spec/inputs/bubbling.moon diff --git a/tests/inputs/class.moon b/spec/inputs/class.moon similarity index 60% rename from tests/inputs/class.moon rename to spec/inputs/class.moon index 92f6e6a5..56b0ae1c 100644 --- a/tests/inputs/class.moon +++ b/spec/inputs/class.moon @@ -77,6 +77,7 @@ class CoolSuper super\yeah"world".okay hi, hi, hi something.super super.super.super.super + super\hello nil @@ -129,4 +130,99 @@ class Whacko print "hello" +yyy = -> + class Cool + nil + + +-- + +class a.b.c.D + nil + + +class a.b["hello"] + nil + +class (-> require "moon")!.Something extends Hello.World + nil + +-- + +a = class +b = class Something +c = class Something extends Hello +d = class extends World + +print (class WhatsUp).__name + +-- + +export ^ +class Something + nil + + +-- + +-- hoisting +class Something + val = 23 + {:insert} = table + new: => print insert, val -- prints nil 23 + +-- + +class X + new: hi + + +-- + +class Cool extends Thing + dang: => + { + hello: -> super! + world: -> super.one + } + +-- + +class Whack extends Thing + dang: do_something => + super! + +--- + +class LocalHoist + local one_thing, another_thing + + one_thing = "hello" + + get_thing: => one_thing + set_thing: (v) => another_thing = v + +class LocalHoistExtends extends LocalHoist + local LocalHoist + + get_class: => LocalHoist + +--- + +class Wowha extends Thing + @butt: -> + super! + super.hello + super\hello! + super\hello + + + @zone: cool { + -> + super! + super.hello + super\hello! + super\hello + } +nil diff --git a/spec/inputs/comprehension.moon b/spec/inputs/comprehension.moon new file mode 100644 index 00000000..1609d79e --- /dev/null +++ b/spec/inputs/comprehension.moon @@ -0,0 +1,52 @@ + +-- see lists.moon for list comprehension tests + +items = {1,2,3,4,5,6} +out = {k,k*2 for k in items} + + +x = hello: "world", okay: 2323 + +copy = {k,v for k,v in pairs x when k != "okay"} + +-- + +{ unpack(x) for x in yes } +{ unpack(x) for x in *yes } + +{ xxxx for x in yes } +{ unpack [a*i for i, a in ipairs x] for x in *{{1,2}, {3,4}} } + + +-- + +n1 = [i for i=1,10] +n2 = [i for i=1,10 when i % 2 == 1] + +aa = [{x,y} for x=1,10 for y=5,14] +bb = [y for thing in y for i=1,10] +cc = [y for i=1,10 for thing in y] +dd = [y for i=1,10 when cool for thing in y when x > 3 when c + 3] + +{"hello", "world" for i=1,10} + +-- + +j = [a for {a,b,c} in things] +k = [a for {a,b,c} in *things] +i = [hello for {:hello, :world} in *things] + +hj = {a,c for {a,b,c} in things} +hk = {a,c for {a,b,c} in *things} +hi = {hello,world for {:hello, :world} in *things} + +ok(a,b,c) for {a,b,c} in things + +-- + +[item for item in *items[1 + 2,3+4]] +[item for item in *items[hello! * 4, 2 - thing[4]]] + + + +nil diff --git a/tests/inputs/cond.moon b/spec/inputs/cond.moon similarity index 89% rename from tests/inputs/cond.moon rename to spec/inputs/cond.moon index d4d26431..18e42b91 100644 --- a/tests/inputs/cond.moon +++ b/spec/inputs/cond.moon @@ -89,6 +89,12 @@ elseif z = true else four +kzy = -> + if something = true + 1 + elseif another = false + 2 + --- unless true @@ -141,6 +147,14 @@ print "hello" unless value dddd = {1,2,3} unless value + +-- + +do + j = 100 + unless j = hi! + error "not j!" + ---------------- a = 12 @@ -148,3 +162,29 @@ a,c,b = "cool" if something +--- + +j = if 1 + if 2 + 3 +else 6 + + +m = if 1 + + + + if 2 + + + 3 + + +else 6 + + + +nil + + + diff --git a/spec/inputs/destructure.moon b/spec/inputs/destructure.moon new file mode 100644 index 00000000..2320709c --- /dev/null +++ b/spec/inputs/destructure.moon @@ -0,0 +1,171 @@ + +do + {a, b} = hello + + {{a}, b, {c}} = hello + + { :hello, :world } = value + +do + { yes: no, thing } = world + + {:a,:b,:c,:d} = yeah + + {a} = one, two + {b}, c = one + {d}, e = one, two + + x, {y} = one, two + + xx, yy = 1, 2 + {yy, xx} = {xx, yy} + + {a, :b, c, :d, e, :f, g} = tbl + +--- + +do + futurists = + sculptor: "Umberto Boccioni" + painter: "Vladimir Burliuk" + poet: + name: "F.T. Marinetti" + address: { + "Via Roma 42R" + "Bellagio, Italy 22021" + } + + {poet: {:name, address: {street, city}}} = futurists + +-- + +do + { @world } = x + { a.b, c.y, func!.z } = x + + { world: @world } = x + +-- + +do + thing = {{1,2}, {3,4}} + + for {x,y} in *thing + print x,y + + +-- + +do + with {a,b} = thing + print a, b + + +-- + +do + thing = nil + if {a} = thing + print a + else + print "nothing" + + thang = {1,2} + if {a,b} = thang + print a,b + + if {a,b} = thing + print a,b + elseif {c,d} = thang + print c,d + else + print "NO" + +-- + +do + z = "yeah" + {a,b,c} = z + +do + {a,b,c} = z + +(z) -> + {a,b,c} = z + +do + z = "oo" + (k) -> + {a,b,c} = z + + +-- destructuring against expression blocks + +{a, b} = for i in *{2, 3} do i * i + +{c, d} = [v * 2 for v in *{1, 2}] + +{:left, :right} = if cond then {left: 1, right: 2} else {left: 3} + +{p, q} = do + {10, 20} + +{:val} = switch x + when 2 then {val: "two"} + +{t} = with something! do .x = 5 + +{u, v} = {7, 8} if cond + +n, {:m} = if cond then 1, {m: 2} + +{:k} = {w, true for w in thing\gmatch "%w"} + +{a: {inner}} = if cond then {a: {"deep"}} + +{lit} = {42} + +{:single} = if cond then {single: 1}, {single: 2} + +-- receivers that can't be chained directly + +grab = (...) -> + {:first_arg} = ... + +{nothing} = nil + +{:negated} = not thing + +{:len} = #thing + +-- assignable non-name targets + +{x: obj.a, y: obj["b"], z: @prop, w: @@cls_prop} = t + +-- destructuring function arguments + +basic = ({:a, :b}) -> a + b + +two = ({x: x1, y: y1}, {x: x2, y: y2}) -> x1 * x2 + y1 * y2 + +with_default = ({:a, :b} = {a: 1, b: 2}) -> a + b + +nested = ({pos: {:x, :y}, [1]: first}) -> x + y + first + +positional = ({first, second}) -> first .. second + +mixed = (name, {:width, :height}, rest=1, ...) -> + print name, width, height, rest, ... + +bound = ({:count}) => @total + count + +class Point + new: ({x: @x, y: @y}) => + +-- outer local is shadowed, not assigned +shadowed = "outer" +shadow_fn = ({:shadowed}) -> shadowed + +-- later defaults see names bound by earlier destructuring args +ordered = ({:a}, b = a, @c = b) => + a + b + @c diff --git a/spec/inputs/destructure_multi_return.moon b/spec/inputs/destructure_multi_return.moon new file mode 100644 index 00000000..509fcff1 --- /dev/null +++ b/spec/inputs/destructure_multi_return.moon @@ -0,0 +1,12 @@ + +num, {:message} = two_values! + +{:alpha}, {:beta}, {:gamma} = multi_destruct! + +head, {:content}, tail = mix! + +start, {:extra}, rest = forward 88, 77 + +prop, {:value} = hello.world! + +{value: built}, {:status} = builder\build! diff --git a/spec/inputs/do.moon b/spec/inputs/do.moon new file mode 100644 index 00000000..334e68f3 --- /dev/null +++ b/spec/inputs/do.moon @@ -0,0 +1,27 @@ + +do + print "hello" + print "world" + +x = do + print "hello" + print "world" + +y = do + things = "shhh" + -> "hello: " .. things + +-> if something then do "yeah" + +t = { + y: do + number = 100 + (x) -> x + number +} + +(y=(do + x = 10 + 2 + x), k=do + "nothing") -> do + "uhhh" + diff --git a/spec/inputs/export.moon b/spec/inputs/export.moon new file mode 100644 index 00000000..0a563798 --- /dev/null +++ b/spec/inputs/export.moon @@ -0,0 +1,77 @@ + +do + export a,b,c = 223, 343 + export cool = "dad" + +do + export class Something + umm: "cool" + +do + export a,b,c + a,b,c,d = "hello" + + +do + What = if this + 232 + else + 4343 + + export ^ + + another = 3434 + Another = 7890 + + if inner then Yeah = "10000" + + What = if this + 232 + else + 4343 + + +do + export * + + What = if this + 232 + else + 4343 + + x,y,z = 1,2,3 + + y = -> + hallo = 3434 + + with tmp + j = 2000 + + +do + export * + x = 3434 + if y then + x = 10 + +do + export * + if y then + x = 10 + x = 3434 + +do + do + export * + + k = 1212 + + do + h = 100 + + y = -> + h = 100 + k = 100 + + h = 100 + diff --git a/spec/inputs/funcs.moon b/spec/inputs/funcs.moon new file mode 100644 index 00000000..08a29b64 --- /dev/null +++ b/spec/inputs/funcs.moon @@ -0,0 +1,158 @@ + + +x = -> print what + +-> + +-> -> -> + +go to the barn + +open -> the -> door + +open -> + the door + hello = -> + my func + +h = -> hi + +eat ->, world + + +(->)() + +x = (...) -> + +hello! +hello.world! + +hello!.something +what!["ofefe"] + +what! the! heck! + +(a,b,c,d,e) -> + +(a,a,a,a,a) -> + print a + +(x=23023) -> + +(x=(y=()->) ->) -> + +(x = if something then yeah else no) -> + +something = (hello=100, world=(x=[[yeah cool]])-> print "eat rice") -> + print hello + +(x, y) => +(@x, @y) => +(x=1) => +(@x=1,y,@z="hello world") => + + +x -> return +y -> return 1 +z -> return 1, "hello", "world" +k -> if yes then return else return + +-> real_name if something + +-- + +d( + -> + print "hello world" + 10 +) + + + +d( + 1,2,3 + 4 + 5 + 6 + + if something + print "okay" + 10 + + 10,20 +) + + +f( + + )( + + )( + what + )(-> + print "srue" + 123) + +-- + +x = (a, + b) -> + print "what" + + +y = (a="hi", + b=23) -> + print "what" + +z = ( + a="hi", + b=23) -> + print "what" + + +j = (f,g,m, + a="hi", + b=23 +) -> + print "what" + + +y = (a="hi", + b=23, + ...) -> + print "what" + + +y = (a="hi", + b=23, + ... +) -> + print "what" + +-- + +args = (a + b) -> + print "what" + + +args = (a="hi" + b=23) -> + print "what" + +args = ( + a="hi" + b=23) -> + print "what" + + +args = (f,g,m + a="hi" + b=23 +) -> + print "what" + + + + +nil diff --git a/spec/inputs/import.moon b/spec/inputs/import.moon new file mode 100644 index 00000000..9d6e6b4b --- /dev/null +++ b/spec/inputs/import.moon @@ -0,0 +1,61 @@ + + +import hello from yeah +import hello, world from table["cool"] + +import a, \b, c from items + + +import master, \ghost from find "mytable" + + +a, yumm = 3434, "hello" + + +_table_0 = 232 + +import something from a table + + +if indent + import okay, \well from tables[100] + +do + import a, b, c from z + +do + import a, + b, c from z + +do + import a + b + c from z + +do + import + a + b + c from z + + +do + import + a + b + c + from z + + + +-- import name is hoisted by local * +do + local * + use = -> insert "hello" + import insert from table + +-- import inside class body is hoisted for methods +class Pipeline + import insert from table + + add: (...) => insert @, ... diff --git a/tests/inputs/lists.moon b/spec/inputs/lists.moon similarity index 92% rename from tests/inputs/lists.moon rename to spec/inputs/lists.moon index 48337cde..c1191857 100644 --- a/tests/inputs/lists.moon +++ b/spec/inputs/lists.moon @@ -3,8 +3,6 @@ hi = [x*2 for _, x in ipairs{1,2,3,4}] items = {1,2,3,4,5,6} -mm = [@x for @x in ipairs items] - [z for z in ipairs items when z > 4] rad = [{a} for a in ipairs { @@ -71,8 +69,4 @@ print thing for thing in *test -> a = b for row in *rows --- testing implicit return --> x for x in *things --> [x for x in *things] - diff --git a/tests/inputs/literals.moon b/spec/inputs/literals.moon similarity index 56% rename from tests/inputs/literals.moon rename to spec/inputs/literals.moon index ef808289..c3a24a6f 100644 --- a/tests/inputs/literals.moon +++ b/spec/inputs/literals.moon @@ -11,6 +11,18 @@ 0xabcdef 0xABCDEF +.2323 +.2323e-1 +.2323e13434 + + +1LL +1ULL +9332LL +9332 +0x2aLL +0x2aULL + [[ hello world ]] [=[ hello world ]=] @@ -19,3 +31,16 @@ "another world" 'what world' + + +" +hello world +" + +'yeah +what is going on +here is something cool' + + +nil + diff --git a/spec/inputs/local.moon b/spec/inputs/local.moon new file mode 100644 index 00000000..fec78b18 --- /dev/null +++ b/spec/inputs/local.moon @@ -0,0 +1,94 @@ + +do + local a + local a,b,c + + b,g = 23232 + + +do + x = 1212 + something = -> + local x + x = 1212 + +do + local * + y = 2323 + z = 2323 + +do + local * + print "Nothing Here!" + +do + local ^ + x = 3434 + y = 3434 + X = 3434 + Y = "yeah" + +do + local ^ + x,y = "a", "b" + +do + local * + x,y = "a", "b" + + +do + local * + if something + x = 2323 + +do + local * + do + x = "one" + + x = 100 + + do + x = "two" + +do + local * + k = if what + 10 + x = 100 + + {:a,:b, :c} = y + + +do + local * + + a = 100 + print "hi" + b = 200 + + local * + c = 100 + print "hi" + d = 200 + d = 2323 + + +do + local ^ + lowercase = 5 + Uppercase = 3 + + class One + Five = 6 + + class Two + class No + +do + local * + -- this generates a nil value in the body + for a in *{} do a + +g = 2323 -- test if anything leaked diff --git a/tests/inputs/loops.moon b/spec/inputs/loops.moon similarity index 65% rename from tests/inputs/loops.moon rename to spec/inputs/loops.moon index c8274707..d099edb6 100644 --- a/tests/inputs/loops.moon +++ b/spec/inputs/loops.moon @@ -74,6 +74,15 @@ i = 0 x = while i < 10 i += 1 +-- values that can'e be coerced + +x = for thing in *3 + y = "hello" + +x = for x=1,2 + y = "hello" + + -- continue while true @@ -105,3 +114,44 @@ for x=1,10 for y = 2,12 continue if y % 3 == 0 + +while true + continue if false + break + +while true + continue if false + return 22 + +-- + +do + xxx = {1,2,3,4} + for thing in *xxx + print thing + + + +-- continue as last statement + +while true + print "hello" + continue + +-- continue at end of switch case in accumulating loop + +values = for x in *items + switch x + when "skip" + continue + else + x + +-- nested continues where outer continue comes after inner loop + +for a in *x + for b in *y + continue if b + print b + continue if a + print a diff --git a/spec/inputs/operators.moon b/spec/inputs/operators.moon new file mode 100644 index 00000000..142ef622 --- /dev/null +++ b/spec/inputs/operators.moon @@ -0,0 +1,72 @@ + +-- binary ops +x = 1 + 3 + +y = 1 + + 3 + +z = 1 + + 3 + + 4 + +-- + +k = b and c and + g + + +h = thing and + -> + print "hello world" + +-- TODO: should fail, indent still set to previous line so it thinks body is +-- indented +i = thing or + -> + print "hello world" + +p = thing and + -> +print "hello world" + +s = thing or + -> and 234 + + +-- +u = { + color: 1 and 2 and + 3 + 4 + 4 +} + +v = { + color: 1 and + -> + "yeah" + "great" + oksy: 3 ^ +2 +} + +-- parens + +nno = ( + yeah + 2 ) + +nn = ( + yeah + 2 +) + +n = hello( + b +) -> + +hello a, + ( + yeah + + 2 + ) - + okay + diff --git a/spec/inputs/return.moon b/spec/inputs/return.moon new file mode 100644 index 00000000..61d3dcad --- /dev/null +++ b/spec/inputs/return.moon @@ -0,0 +1,55 @@ +-- testing `return` propagation + +-> x for x in *things +-> [x for x in *things] + + +-- doesn't make sense on purpose +do + return x for x in *things + +do + return [x for x in *things] + +do + return {x,y for x,y in *things} + +-> + if a + if a + a + else + b + elseif b + if a + a + else + b + else + if a + a + else + b + + +do + return if a + if a + a + else + b + elseif b + if a + a + else + b + else + if a + a + else + b + +-> a\b +do a\b + + diff --git a/tests/inputs/string.moon b/spec/inputs/string.moon similarity index 65% rename from tests/inputs/string.moon rename to spec/inputs/string.moon index 897056a5..648eff44 100644 --- a/tests/inputs/string.moon +++ b/spec/inputs/string.moon @@ -64,3 +64,23 @@ c = 'hello #{hello}' something"hello"\world! something "hello"\world! + +-- interpolation keeps grouping next to tighter binding operators + +x = 10 / "#{b}.5" + +y = 1 + "#{n}0" * 2 + +cmp = a == "v#{b}" + +joined = "a" .. "b#{c}" + +mixed = "#{a}b" * 2 - "c#{d}" + +-- interpolation on the left of concat keeps its grouping for __concat + +prefix = "id: #{id}, " .. rest + +data_url = "data:image/#{kind};base64," .. encode_base64(image_bytes) + +line ..= " #{join} " .. el diff --git a/tests/inputs/stub.moon b/spec/inputs/stub.moon similarity index 100% rename from tests/inputs/stub.moon rename to spec/inputs/stub.moon diff --git a/tests/inputs/switch.moon b/spec/inputs/switch.moon similarity index 76% rename from tests/inputs/switch.moon rename to spec/inputs/switch.moon index abb1b97c..3bc179b3 100644 --- a/tests/inputs/switch.moon +++ b/spec/inputs/switch.moon @@ -42,3 +42,23 @@ call_func switch something when 1 then "yes" else "no" +-- + +switch hi + when hello or world + greene + +-- + +switch hi + when "one", "two" + print "cool" + when "dad" + no + +switch hi + when 3+1, hello!, (-> 4)! + yello + else + print "cool" + diff --git a/tests/inputs/syntax.moon b/spec/inputs/syntax.moon similarity index 83% rename from tests/inputs/syntax.moon rename to spec/inputs/syntax.moon index e9712162..31c79d20 100644 --- a/tests/inputs/syntax.moon +++ b/spec/inputs/syntax.moon @@ -66,6 +66,18 @@ something 'else', "ya" something'else' something"else" +something[[hey]] * 2 +something[======[hey]======] * 2 + + +something'else', 2 +something"else", 2 +something[[else]], 2 + +something 'else', 2 +something "else", 2 +something [[else]], 2 + here(we)"go"[12123] -- this runs @@ -104,6 +116,7 @@ x = -[x for x in x] print "hello" if cool print "hello" unless cool print "hello" unless 1212 and 3434 -- hello +print "hello" for i=1,10 print "nutjob" @@ -142,6 +155,21 @@ y /= 100 m %= 2 hello ..= "world" +@@something += 10 +@something += 10 + +@@then += 10 +@then += 10 + +a["hello"] += 10 +a["hello#{tostring ff}"] += 10 +a[four].x += 10 + +count -= #datum + 1 +count -= #datum +total *= -x + 1 +flag and= not a or b + x = 0 (if ntype(v) == "fndef" then x += 1) for v in *values @@ -154,6 +182,8 @@ hello = good: 230203 +div class: "cool" + 5 + what wack what whack + 5 @@ -225,6 +255,8 @@ another hello, one, a += 3 - 5 a *= 3 + 5 a *= 3 +a >>= 3 +a <<= 3 a /= func "cool" --- @@ -232,4 +264,17 @@ a /= func "cool" x.then = "hello" x.while.true = "hello" +-- + +x or= "hello" +x and= "hello" + +-- + +z = a-b +z = a -b +z = a - b +z = a- b + + -- cooool diff --git a/tests/inputs/tables.moon b/spec/inputs/tables.moon similarity index 62% rename from tests/inputs/tables.moon rename to spec/inputs/tables.moon index e918556f..2bf66d70 100644 --- a/tests/inputs/tables.moon +++ b/spec/inputs/tables.moon @@ -102,3 +102,60 @@ xam = { } +kam = { + hello: 12 + goodcheese: + "mmm" + + yeah: + 12 + 232 + + lets: + keepit going: true, + okay: "yeah" + + more: + { + 1, [x for x=1,10] + } + + [{"one", "two"}]: + one_thing => +} + +-- TODO: both of these have undesirable output +keepit going: true, + okay: "yeah", + workd: "okay" + +thing what: + "great", no: + "more" + okay: 123 + + +-- +thing what: + "great", no: + "more" +okay: 123 -- a anon table + + +-- + +k = { "hello": "world" } +k = { 'hello': 'world' } +k = { "hello": 'world', "hat": "zat" } + +please "hello": "world" +k = "hello": "world", "one": "zone" + +f = "one", "two": three, "four" +f = "two": three, "four" +f = { "one", "two": three, "four" } + + +j = "one", "two": three, "four": five, 6, 7 + + +nil diff --git a/spec/inputs/unless_else.moon b/spec/inputs/unless_else.moon new file mode 100644 index 00000000..fe96c0bd --- /dev/null +++ b/spec/inputs/unless_else.moon @@ -0,0 +1,5 @@ +if a + unless b + print "hi" + elseif c + print "not hi" diff --git a/tests/inputs/using.moon b/spec/inputs/using.moon similarity index 100% rename from tests/inputs/using.moon rename to spec/inputs/using.moon diff --git a/tests/inputs/whitespace.moon b/spec/inputs/whitespace.moon similarity index 84% rename from tests/inputs/whitespace.moon rename to spec/inputs/whitespace.moon index a8242228..4a2ff1fa 100644 --- a/tests/inputs/whitespace.moon +++ b/spec/inputs/whitespace.moon @@ -81,3 +81,22 @@ if hello 1,2,3, print "hello" +-- + +a( + one, two, three +) + +b( + one, + two, + three +) + + +c(one, two, + three, four) + +-- + +nil diff --git a/spec/inputs/with.moon b/spec/inputs/with.moon new file mode 100644 index 00000000..8e4a5867 --- /dev/null +++ b/spec/inputs/with.moon @@ -0,0 +1,124 @@ + +do + a = -> + with something + print .hello + print hi + print "world" + +do + with leaf + .world! + .world 1,2,3 + + g = .what.is.this + + .hi 1,2,3 + + \hi(1,2).world 2323 + + \hi "yeah", "man" + .world = 200 + +do + zyzyzy = with something + .set_state "hello world" + +do + x = 5 + with Something! + \write "hello world" + + +do + x = { + hello: with yeah + \okay! + } + +do + with foo + \prop"something".hello + .prop\send(one) + .prop\send one + + +-- + +do + with a, b -- b is lost + print .world + + mod = with _M = {} + .Thing = "hi" + + -- operate on a only + with a, b = something, pooh + print .world + + x = with a, b = 1, 2 + print a + b + + print with a, b = 1, 2 + print a + b + + -- assignment lhs must be evaluated in the order they appear + p = with hello!.x, world!.y = 1, 2 + print a + b + +-- + +do + x = "hello" + with x + x\upper! + +do + with k = "jo" + print \upper! + +do + with a,b,c = "", "", "" + print \upper! + +do + a = "bunk" + with a,b,c = "", "", "" + print \upper! + +do + with j + print \upper! + +do + with k.j = "jo" + print \upper! + +do + with a + print .b + -- nested `with`s should change the scope correctly + with .c + print .d + +do + with a + -- nested `with`s with assignments should change the scope correctly + with .b = 2 + print .c + +do + -> + with hi + return .a, .b + + +do + with dad + .if "yes" + y = .end.of.function + + +do + with obj + bound = \method + keyword_bound = \function diff --git a/test2.moon b/spec/lang_spec.moon similarity index 51% rename from test2.moon rename to spec/lang_spec.moon index 499837e3..c39a8d8b 100644 --- a/test2.moon +++ b/spec/lang_spec.moon @@ -1,18 +1,14 @@ +lfs = require "lfs" -require "lfs" -require "busted" - -require "moonscript.parse" -require "moonscript.compile" -require "moonscript.util" - -import parse, compile, util from moonscript +import with_dev from require "spec.helpers" pattern = ... +unpack = table.unpack or unpack + options = { - in_dir: "tests/inputs", - out_dir: "tests/outputs", + in_dir: "spec/inputs", + out_dir: "spec/outputs", input_pattern: "(.*)%.moon$", output_ext: ".lua" @@ -22,7 +18,7 @@ options = { tool: "git diff --no-index --color" --color-words" filter: (str) -> -- strip the first four lines - table.concat [line for line in *util.split(str, "\n")[5,]], "\n" + table.concat [l for l in *([line for line in str\gmatch("[^\n]+")])[5,]], "\n" } } @@ -34,18 +30,23 @@ pcall -> require "socket" gettime = socket.gettime +gettime or= os.clock + +-- nil-safe capture of all return values +pack = (...) -> {n: select("#", ...), ...} + benchmark = (fn) -> if gettime start = gettime! - res = {fn!} - gettime! - start, unpack res + res = pack fn! + gettime! - start, unpack res, 1, res.n else nil, fn! read_all = (fname) -> - f = io.open(fname, "r") - with f\read "*a" - f\close! + if f = io.open(fname, "r") + with f\read "*a" + f\close! diff_file = (a_fname, b_fname) -> out = io.popen(options.diff.tool .. " ".. a_fname .. " " .. b_fname, "r")\read "*a" @@ -72,6 +73,7 @@ diff_str = (expected, got) -> string_assert = (expected, got) -> if expected != got diff = diff_str expected, got + error "string equality assert failed" if os.getenv "HIDE_DIFF" error "string equality assert failed:\n" .. diff input_fname = (base) -> @@ -80,24 +82,28 @@ input_fname = (base) -> output_fname = (base) -> options.out_dir .. "/" .. base .. options.output_ext +inputs = for file in lfs.dir options.in_dir + with match = file\match options.input_pattern + continue unless match + +table.sort inputs + describe "input tests", -> - inputs = for file in lfs.dir options.in_dir - file\match options.input_pattern + local parse, compile + + with_dev -> + parse = require "moonscript.parse" + compile = require "moonscript.compile" for name in *inputs input = input_fname name - fn = if pattern and not input\match pattern - pending - else - it - - fn input .. " #input", -> + it input .. " #input", -> file_str = read_all input_fname name parse_time, tree, err = benchmark -> parse.string file_str - return err if err + error err if err compile_time, code, err, pos = benchmark -> compile.tree tree - return compile.format_error err, pos, file_str unless code + error compile.format_error err, pos, file_str unless code table.insert timings, {name, parse_time, compile_time} @@ -107,37 +113,30 @@ describe "input tests", -> \close! else expected_str = read_all output_fname name - return "Test not built: " .. input_fname(name) unless expected_str + error "Test not built: " .. input_fname(name) unless expected_str string_assert expected_str, code nil -busted.options = { - output: require'busted.output.utf_terminal'! - suppress_pending: true -} - -print busted! - -if options.show_timings - format_time = (sec) -> ("%.3fms")\format(sec*1000) - col_width = math.max unpack [#t[1] for t in *timings] - - print "\nTimings:" - total_parse, total_compile = 0, 0 - for tuple in *timings - name, parse_time, compile_time = unpack tuple - name = name .. (" ")\rep col_width - #name - total_parse += parse_time - total_compile += compile_time - - print " * " .. name, - "p: " .. format_time(parse_time), - "c: " .. format_time(compile_time) - - print "\nTotal:" - print " parse:", format_time total_parse - print " compile:", format_time total_compile - + if options.show_timings + teardown -> + format_time = (sec) -> ("%.3fms")\format(sec*1000) + col_width = math.max unpack [#t[1] for t in *timings] + + print "\nTimings:" + total_parse, total_compile = 0, 0 + for tuple in *timings + name, parse_time, compile_time = unpack tuple + name = name .. (" ")\rep col_width - #name + total_parse += parse_time + total_compile += compile_time + + print " * " .. name, + "p: " .. format_time(parse_time), + "c: " .. format_time(compile_time) + + print "\nTotal:" + print " parse:", format_time total_parse + print " compile:", format_time total_compile diff --git a/spec/lint_spec.moon b/spec/lint_spec.moon new file mode 100644 index 00000000..070b7922 --- /dev/null +++ b/spec/lint_spec.moon @@ -0,0 +1,229 @@ +import with_dev, unindent from require "spec.helpers" + +describe "linter", -> + local lint + + with_dev -> + lint = require "moonscript.cmd.lint" + + it "reports unused assignments in nested blocks", -> + code = unindent [[ + if true + unused = 1 + ]] + + assert.same unindent([[ + string input + + line 2: assigned but unused `unused` + ==================================== + > unused = 1 + ]]), lint.lint_code code + + it "reports assigning to constant import", -> + code = unindent [[ + import insert from table + insert = 5 + ]] + + assert.same unindent([[ + string input + + line 2: assigning to constant `insert` + ====================================== + > insert = 5 + ]]), lint.lint_code code + + it "reports update op on constant import", -> + code = unindent [[ + import count from require "thing" + count += 1 + ]] + + assert.same unindent([[ + string input + + line 2: assigning to constant `count` + ===================================== + > count += 1 + ]]), lint.lint_code code + + it "allows shadowing a constant import", -> + code = unindent [[ + import insert from table + insert {}, 1 + f = (insert) -> insert + g = -> + local insert + insert = 5 + insert + f g! + ]] + + assert.is_nil (lint.lint_code code) + + -- loop variables are fresh locals in every loop form, writing them never + -- touches the enclosing binding + it "allows loop variables shadowing a constant import", -> + code = unindent [[ + import x from table + f = -> x + for x = 1, 2 + x = 3 + f! + for x in ipairs {} + x = 4 + f! + for x in *{1, 2} + x = 5 + f! + ]] + + assert.is_nil (lint.lint_code code) + + -- importing a name bound in an enclosing scope writes that binding + -- instead of creating a local + it "flags import overwriting an enclosing binding", -> + code = unindent [[ + insert = "hello" + f = -> + import insert from table + insert + f! + ]] + + assert.same unindent([[ + string input + + line 3: import overwrites existing binding `insert` + =================================================== + > import insert from table + ]]), lint.lint_code code + + it "flags import overwriting a binding in the same scope", -> + code = unindent [[ + insert = "hello" + import insert from table + insert {}, 1 + ]] + + assert.same unindent([[ + string input + + line 2: import overwrites existing binding `insert` + =================================================== + > import insert from table + ]]), lint.lint_code code + + it "flags a final import overwriting an enclosing binding", -> + code = unindent [[ + insert = "hello" + f = -> + print insert + import insert from table + f! + ]] + + assert.same unindent([[ + string input + + line 4: import overwrites existing binding `insert` + =================================================== + > import insert from table + ]]), lint.lint_code code + + it "flags import inside nested function overwriting a declared local", -> + code = unindent [[ + f = -> + local check_app_version + reload = -> + import check_app_version from require "helpers.api" + print "reloaded" + reload! + check_app_version! + f! + ]] + + assert.same unindent([[ + string input + + line 4: import overwrites existing binding `check_app_version` + ============================================================== + > import check_app_version from require "helpers.api" + ]]), lint.lint_code code + + -- a repeated import writes the existing constant binding + it "flags a repeated import", -> + code = unindent [[ + import insert from table + insert {}, 1 + do + import insert from require "custom" + insert 2 + ]] + + assert.same unindent([[ + string input + + line 4: assigning to constant `insert` + ======================================= + > import insert from require "custom" + ]]), lint.lint_code code + + it "limits reporting to the given stages", -> + code = unindent [[ + import insert from table + insert = 5 + do + unused_var = 1 + missing_global 10 + ]] + + result = lint.lint_code code, nil, nil, stages: {"constant_assign"} + assert.same unindent([[ + string input + + line 2: assigning to constant `insert` + ====================================== + > insert = 5 + ]]), result + + result = lint.lint_code code, nil, nil, stages: {"global_access", "unused"} + assert.truthy result\match "accessing global" + assert.truthy result\match "assigned but unused" + assert.is_nil result\match "assigning to constant" + + assert.is_nil (lint.lint_code code, nil, nil, stages: {"import_overwrite"}) + assert.is_nil (lint.lint_code code, nil, nil, stages: {}) + + it "formats output with the compact format", -> + code = unindent [[ + import insert from table + insert = 5 + do + unused_var = 1 + f = -> missing_global 10 + f! + ]] + + assert.same unindent([[ + test.moon:2:1: assigning to constant `insert` [constant_assign] + test.moon:4:3: assigned but unused `unused_var` [unused] + test.moon:5:7: accessing global `missing_global` [global_access] + ]]), lint.lint_code code, "test.moon", nil, format: "compact" + + it "combines compact format with stage filter", -> + code = unindent [[ + import insert from table + insert = 5 + missing_global 10 + ]] + + result = lint.lint_code code, "test.moon", nil, { + format: "compact" + stages: {"global_access"} + } + assert.same "test.moon:3:1: accessing global `missing_global` [global_access]", result + + it "compact format returns nothing for clean code", -> + assert.is_nil (lint.lint_code "x = 5\nprint x", "test.moon", nil, format: "compact") diff --git a/spec/loops_spec.moon b/spec/loops_spec.moon new file mode 100644 index 00000000..68b5f97f --- /dev/null +++ b/spec/loops_spec.moon @@ -0,0 +1,9 @@ + +describe "loops", -> + it "should continue", -> + input = {1,2,3,4,5,6} + output = for x in *input + continue if x % 2 == 1 + x + + assert.same output, { 2,4,6 } diff --git a/spec/moon_spec.moon b/spec/moon_spec.moon new file mode 100644 index 00000000..e43c22b7 --- /dev/null +++ b/spec/moon_spec.moon @@ -0,0 +1,374 @@ +-- test moon library + +import with_dev from require "spec.helpers" + +describe "moon", -> + local moon + + with_dev -> + moon = require "moon" + + describe "type", -> + it "returns 'class' for a class", -> + class Test + assert.equal "class", moon.type Test + + it "returns the class for an instance", -> + class Test + assert.equal Test, moon.type Test! + + it "returns 'table' for __base", -> + class Test + assert.equal "table", moon.type Test.__base + + it "returns 'table' for __base with inheritance", -> + class Parent + class Child extends Parent + assert.equal "table", moon.type Child.__base + assert.equal "table", moon.type Parent.__base + + it "returns primitive type for non-tables", -> + assert.equal "number", moon.type 1 + assert.equal "boolean", moon.type true + assert.equal "nil", moon.type nil + assert.equal "string", moon.type "hello" + assert.equal "function", moon.type -> + + it "returns 'table' for plain tables", -> + assert.equal "table", moon.type {} + assert.equal "table", moon.type {hello: "world"} + + it "returns 'class' for classes with inheritance", -> + class Parent + class Child extends Parent + assert.equal "class", moon.type Parent + assert.equal "class", moon.type Child + + it "works with inheritance", -> + class Parent + class Child extends Parent + assert.equal Child, moon.type Child! + assert.equal Parent, moon.type Parent! + assert.equal "table", moon.type Child.__base + assert.equal "table", moon.type Parent.__base + + it "should get upvalue", -> + fn = do + hello = "world" + -> hello + + assert.same moon.debug.upvalue(fn, "hello"), "world" + + it "should set upvalue", -> + fn = do + hello = "world" + -> hello + + moon.debug.upvalue fn, "hello", "foobar" + assert.same fn!, "foobar" + + it "should run with scope", -> + scope = hello: -> + spy.on scope, "hello" + moon.run_with_scope (-> hello!), scope + + assert.spy(scope.hello).was.called! + + + it "should have access to old environment", -> + scope = {} + res = moon.run_with_scope (-> math), scope + + assert.same res, math + + it "should created bound proxy", -> + class Hello + state: 10 + method: (val) => "the state: #{@state}, the val: #{val}" + + hello = Hello! + bound = moon.bind_methods hello + + assert.same bound.method("xxx"), "the state: 10, the val: xxx" + + it "should create defaulted table", -> + fib = moon.defaultbl {[0]: 0, [1]: 1}, (i) => self[i - 1] + self[i - 2] + fib[7] + + assert.same fib, { [0]: 0, 1, 1, 2, 3, 5, 8, 13 } + + it "should extend", -> + t1 = { hello: "world's", cool: "shortest" } + t2 = { cool: "boots", cowboy: "hat" } + + out = moon.extend t1, t2 + + assert.same { out.hello, out.cool, out.cowboy }, { "world's", "shortest", "hat"} + + it "should make a copy", -> + x = { "hello", yeah: "man" } + y = moon.copy x + + x[1] = "yikes" + x.yeah = "woman" + + assert.same y, { "hello", yeah: "man" } + + + it "should mixin", -> + class TestModule + new: (@var) => + show_var: => "var is: #{@var}" + + class Second + new: => + moon.mixin self, TestModule, "hi" + + obj = Second! + + assert.same obj\show_var!, "var is: hi" + + it "should mixin object", -> + class First + val: 10 + get_val: => "the val: #{@val}" + + class Second + val: 20 + new: => + moon.mixin_object @, First!, { "get_val" } + + obj = Second! + assert.same obj\get_val!, "the val: 10" + + it "should mixin table", -> + a = { hello: "world", cat: "dog" } + b = { cat: "mouse", foo: "bar" } + moon.mixin_table a, b + + assert.same a, { hello: "world", cat: "mouse", foo: "bar"} + + describe "is_class", -> + it "returns true for a class", -> + class Hello + assert.truthy moon.is_class Hello + + it "returns false for an instance", -> + class Hello + assert.falsy moon.is_class Hello! + + it "returns false for __base", -> + class Hello + assert.falsy moon.is_class Hello.__base + + it "returns false for __base with inheritance", -> + class Parent + class Child extends Parent + assert.falsy moon.is_class Child.__base + assert.falsy moon.is_class Parent.__base + + it "returns false for plain tables and non-tables", -> + assert.falsy moon.is_class {} + assert.falsy moon.is_class 123 + assert.falsy moon.is_class "hello" + assert.falsy moon.is_class nil + assert.falsy moon.is_class true + + it "works with inheritance", -> + class Parent + class Child extends Parent + assert.truthy moon.is_class Parent + assert.truthy moon.is_class Child + assert.falsy moon.is_class Child! + + describe "is_instance and is_class with imposter tables", -> + it "rejects table with only __base set", -> + fake = { __base: {} } + assert.falsy moon.is_class fake + assert.falsy moon.is_instance fake + + it "rejects table with __base and non-callable metatable", -> + fake = setmetatable { __base: {} }, { __index: {} } + assert.falsy moon.is_class fake + assert.falsy moon.is_instance fake + + it "rejects table with self-referencing __index but no metatable", -> + fake = {} + fake.__index = fake + assert.falsy moon.is_class fake + assert.falsy moon.is_instance fake + + it "rejects table with __class set directly", -> + fake = { __class: {} } + assert.falsy moon.is_class fake + assert.falsy moon.is_instance fake + + it "rejects table whose metatable has __class but not self-referencing __index", -> + mt = { __class: {} } + fake = setmetatable {}, mt + assert.falsy moon.is_class fake + assert.falsy moon.is_instance fake + + it "rejects table with self-referencing __index used as its own metatable", -> + -- looks like a __base used as a metatable for itself + fake = {} + fake.__index = fake + setmetatable fake, fake + assert.falsy moon.is_class fake + assert.falsy moon.is_instance fake + + describe "is_instance", -> + it "returns true for an instance", -> + class Hello + assert.truthy moon.is_instance Hello! + + it "returns false for a class", -> + class Hello + assert.falsy moon.is_instance Hello + + it "returns false for __base", -> + class Hello + assert.falsy moon.is_instance Hello.__base + + it "returns false for __base with inheritance", -> + class Parent + class Child extends Parent + assert.falsy moon.is_instance Child.__base + assert.falsy moon.is_instance Parent.__base + + it "returns false for plain tables and non-tables", -> + assert.falsy moon.is_instance {} + assert.falsy moon.is_instance 123 + assert.falsy moon.is_instance "hello" + assert.falsy moon.is_instance nil + assert.falsy moon.is_instance true + + it "works with inheritance", -> + class Parent + class Child extends Parent + assert.truthy moon.is_instance Parent! + assert.truthy moon.is_instance Child! + assert.falsy moon.is_instance Parent + assert.falsy moon.is_instance Child + + describe "is_instance_of", -> + it "returns true for direct instance", -> + class Hello + assert.truthy moon.is_instance_of Hello!, Hello + + it "returns true for instance of parent class", -> + class Parent + class Child extends Parent + assert.truthy moon.is_instance_of Child!, Parent + assert.truthy moon.is_instance_of Child!, Child + + it "returns false for instance of unrelated class", -> + class A + class B + assert.falsy moon.is_instance_of A!, B + assert.falsy moon.is_instance_of B!, A + + it "returns false for parent instance checked against child class", -> + class Parent + class Child extends Parent + assert.falsy moon.is_instance_of Parent!, Child + + it "errors when value is not an instance", -> + class Hello + assert.has_error (-> moon.is_instance_of Hello, Hello), "is_instance_of: expected instance, got table" + assert.has_error (-> moon.is_instance_of Hello.__base, Hello), "is_instance_of: expected instance, got table" + assert.has_error (-> moon.is_instance_of {}, Hello), "is_instance_of: expected instance, got table" + assert.has_error (-> moon.is_instance_of nil, Hello), "is_instance_of: expected instance, got nil" + assert.has_error (-> moon.is_instance_of 123, Hello), "is_instance_of: expected instance, got number" + + it "errors when __base is passed as the value", -> + class Parent + class Child extends Parent + assert.has_error (-> moon.is_instance_of Parent.__base, Parent), "is_instance_of: expected instance, got table" + assert.has_error (-> moon.is_instance_of Child.__base, Child), "is_instance_of: expected instance, got table" + assert.has_error (-> moon.is_instance_of Child.__base, Parent), "is_instance_of: expected instance, got table" + + it "returns false when __base is passed as the class", -> + class Parent + class Child extends Parent + assert.falsy moon.is_instance_of Parent!, Parent.__base + assert.falsy moon.is_instance_of Child!, Child.__base + assert.falsy moon.is_instance_of Child!, Parent.__base + + it "errors when __base is on both sides", -> + class Parent + class Child extends Parent + assert.has_error (-> moon.is_instance_of Parent.__base, Parent.__base), "is_instance_of: expected instance, got table" + assert.has_error (-> moon.is_instance_of Child.__base, Child.__base), "is_instance_of: expected instance, got table" + assert.has_error (-> moon.is_instance_of Child.__base, Parent.__base), "is_instance_of: expected instance, got table" + assert.has_error (-> moon.is_instance_of Parent.__base, Child.__base), "is_instance_of: expected instance, got table" + + it "works with deep inheritance chain", -> + class A + class B extends A + class C extends B + assert.truthy moon.is_instance_of C!, A + assert.truthy moon.is_instance_of C!, B + assert.truthy moon.is_instance_of C!, C + assert.falsy moon.is_instance_of A!, B + assert.falsy moon.is_instance_of A!, C + + describe "is_subclass_of", -> + it "returns true for direct child", -> + class Parent + class Child extends Parent + assert.truthy moon.is_subclass_of Child, Parent + + it "returns true for deep inheritance", -> + class A + class B extends A + class C extends B + assert.truthy moon.is_subclass_of C, A + assert.truthy moon.is_subclass_of C, B + assert.truthy moon.is_subclass_of B, A + + it "returns false for same class", -> + class A + assert.falsy moon.is_subclass_of A, A + + it "returns false for parent checked against child", -> + class Parent + class Child extends Parent + assert.falsy moon.is_subclass_of Parent, Child + + it "returns false for unrelated classes", -> + class A + class B + assert.falsy moon.is_subclass_of A, B + assert.falsy moon.is_subclass_of B, A + + it "returns false for class without parent", -> + class A + class B + assert.falsy moon.is_subclass_of A, B + + it "returns false when __base is passed as the parent", -> + class Parent + class Child extends Parent + assert.falsy moon.is_subclass_of Child, Parent.__base + assert.falsy moon.is_subclass_of Child, Child.__base + + it "errors when first argument is not a class", -> + class Hello + assert.has_error (-> moon.is_subclass_of Hello!, Hello), "is_subclass_of: expected class, got table" + assert.has_error (-> moon.is_subclass_of Hello.__base, Hello), "is_subclass_of: expected class, got table" + assert.has_error (-> moon.is_subclass_of {}, Hello), "is_subclass_of: expected class, got table" + assert.has_error (-> moon.is_subclass_of nil, Hello), "is_subclass_of: expected class, got nil" + assert.has_error (-> moon.is_subclass_of 123, Hello), "is_subclass_of: expected class, got number" + + it "errors when __base is passed as the first argument", -> + class Parent + class Child extends Parent + assert.has_error (-> moon.is_subclass_of Parent.__base, Parent), "is_subclass_of: expected class, got table" + assert.has_error (-> moon.is_subclass_of Child.__base, Child), "is_subclass_of: expected class, got table" + + it "should fold", -> + numbers = {4,3,5,6,7,2,3} + sum = moon.fold numbers, (a,b) -> a + b + + assert.same sum, 30 diff --git a/spec/moonscript_spec.moon b/spec/moonscript_spec.moon new file mode 100644 index 00000000..b7e5271b --- /dev/null +++ b/spec/moonscript_spec.moon @@ -0,0 +1,12 @@ +-- moonscript module + +import with_dev from require "spec.helpers" + +describe "moonscript.base", -> + with_dev! + + it "should create moonpath", -> + path = ";./?.lua;/usr/share/lua/5.1/?.lua;/usr/share/lua/5.1/?/init.lua;/usr/lib/lua/5.1/?.luac;/home/leafo/.luarocks/lua/5.1/?.lua" + import create_moonpath from require "moonscript.base" + assert.same "./?.moon;/usr/share/lua/5.1/?.moon;/usr/share/lua/5.1/?/init.moon;/home/leafo/.luarocks/lua/5.1/?.moon", create_moonpath(path) + diff --git a/spec/outputs/ambiguous.lua b/spec/outputs/ambiguous.lua new file mode 100644 index 00000000..2330da40 --- /dev/null +++ b/spec/outputs/ambiguous.lua @@ -0,0 +1,9 @@ +local a = 'b' +local c = d; +(a(b))(c(d)) +c = d.c; +(a(b))(c(d)); +(c(d))(a(b)) +local b +a, b = c, d +return (d(a))(c) \ No newline at end of file diff --git a/spec/outputs/ambiguous_tables.lua b/spec/outputs/ambiguous_tables.lua new file mode 100644 index 00000000..8af0aaab --- /dev/null +++ b/spec/outputs/ambiguous_tables.lua @@ -0,0 +1,6 @@ +local x = { + hello, + (one), + (two), + three() +} \ No newline at end of file diff --git a/tests/outputs/assign.lua b/spec/outputs/assign.lua similarity index 58% rename from tests/outputs/assign.lua rename to spec/outputs/assign.lua index cddfda05..4a9ed73f 100644 --- a/tests/outputs/assign.lua +++ b/spec/outputs/assign.lua @@ -1,5 +1,5 @@ -local _ -_ = function() +local _scrap_0 +_scrap_0 = function() local joop = 2302 return function(hi) local d = 100 @@ -27,4 +27,19 @@ if hello then else print("the other") a, b = "nothing", "yeah" -end \ No newline at end of file +end +a, b = (function() + local _base_0 = x + local _fn_0 = _base_0.fn + return function(...) + return _fn_0(_base_0, ...) + end +end)() +local c +a, b, c = (function() + local _base_0 = x + local _fn_0 = _base_0.fn + return function(...) + return _fn_0(_base_0, ...) + end +end)(), 1 \ No newline at end of file diff --git a/spec/outputs/bubbling.lua b/spec/outputs/bubbling.lua new file mode 100644 index 00000000..036d5fb7 --- /dev/null +++ b/spec/outputs/bubbling.lua @@ -0,0 +1,120 @@ +local f +f = function(...) + return #{ + ... + } +end +local dont_bubble +dont_bubble = function() + local _accum_0 = { } + local _len_0 = 1 + for x in (function(...) + return print(...) + end)("hello") do + _accum_0[_len_0] = x + _len_0 = _len_0 + 1 + end + return _accum_0 +end +local k +do + local _accum_0 = { } + local _len_0 = 1 + for x in (function(...) + return print(...) + end)("hello") do + _accum_0[_len_0] = x + _len_0 = _len_0 + 1 + end + k = _accum_0 +end +local j +do + local _accum_0 = { } + local _len_0 = 1 + for i = 1, 10 do + _accum_0[_len_0] = function(...) + return print(...) + end + _len_0 = _len_0 + 1 + end + j = _accum_0 +end +local m +m = function(...) + local _accum_0 = { } + local _len_0 = 1 + local _list_0 = { + ... + } + for _index_0 = 1, #_list_0 do + local x = _list_0[_index_0] + if f(...) > 4 then + _accum_0[_len_0] = x + _len_0 = _len_0 + 1 + end + end + return _accum_0 +end +local x +do + local _accum_0 = { } + local _len_0 = 1 + local _list_0 = { + ... + } + for _index_0 = 1, #_list_0 do + local i = _list_0[_index_0] + _accum_0[_len_0] = i + _len_0 = _len_0 + 1 + end + x = _accum_0 +end +local y +do + local _accum_0 = { } + local _len_0 = 1 + local _list_0 = { + ... + } + for _index_0 = 1, #_list_0 do + local x = _list_0[_index_0] + _accum_0[_len_0] = x + _len_0 = _len_0 + 1 + end + y = _accum_0 +end +local z +do + local _accum_0 = { } + local _len_0 = 1 + for x in hallo do + if f(...) > 4 then + _accum_0[_len_0] = x + _len_0 = _len_0 + 1 + end + end + z = _accum_0 +end +local a +do + local _accum_0 = { } + local _len_0 = 1 + for i = 1, 10 do + _accum_0[_len_0] = ... + _len_0 = _len_0 + 1 + end + a = _accum_0 +end +local b +do + local _accum_0 = { } + local _len_0 = 1 + for i = 1, 10 do + _accum_0[_len_0] = function() + return print(...) + end + _len_0 = _len_0 + 1 + end + b = _accum_0 +end \ No newline at end of file diff --git a/spec/outputs/class.lua b/spec/outputs/class.lua new file mode 100644 index 00000000..4ea9f31b --- /dev/null +++ b/spec/outputs/class.lua @@ -0,0 +1,984 @@ +local Hello +do + local _class_0 + local _base_0 = { + hello = function(self) + return print(self.test, self.world) + end, + __tostring = function(self) + return "hello world" + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function(self, test, world) + self.test, self.world = test, world + return print("creating object..") + end, + __base = _base_0, + __name = "Hello" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + Hello = _class_0 +end +local x = Hello(1, 2) +x:hello() +print(x) +local Simple +do + local _class_0 + local _base_0 = { + cool = function(self) + return print("cool") + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function() end, + __base = _base_0, + __name = "Simple" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + Simple = _class_0 +end +local Yikes +do + local _class_0 + local _parent_0 = Simple + local _base_0 = { } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self) + return print("created hello") + end, + __base = _base_0, + __name = "Yikes", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + Yikes = _class_0 +end +x = Yikes() +x:cool() +local Hi +do + local _class_0 + local _base_0 = { + cool = function(self, num) + return print("num", num) + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function(self, arg) + return print("init arg", arg) + end, + __base = _base_0, + __name = "Hi" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + Hi = _class_0 +end +do + local _class_0 + local _parent_0 = Hi + local _base_0 = { + cool = function(self) + return _class_0.__parent.__base.cool(self, 120302) + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self) + return _class_0.__parent.__init(self, "man") + end, + __base = _base_0, + __name = "Simple", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + Simple = _class_0 +end +x = Simple() +x:cool() +print(x.__class == Simple) +local Okay +do + local _class_0 + local _base_0 = { + something = 20323 + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function() end, + __base = _base_0, + __name = "Okay" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + Okay = _class_0 +end +local Biggie +do + local _class_0 + local _parent_0 = Okay + local _base_0 = { + something = function(self) + _class_0.__parent.__base.something(self, 1, 2, 3, 4) + _class_0.__parent.something(another_self, 1, 2, 3, 4) + return assert(_class_0.__parent == Okay) + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, ...) + return _class_0.__parent.__init(self, ...) + end, + __base = _base_0, + __name = "Biggie", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + Biggie = _class_0 +end +local Yeah +do + local _class_0 + local _base_0 = { + okay = function(self) + return _class_0.__parent.something(self, 1, 2, 3, 4) + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function() end, + __base = _base_0, + __name = "Yeah" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + Yeah = _class_0 +end +local What +do + local _class_0 + local _base_0 = { + something = function(self) + return print("val:", self.val) + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function() end, + __base = _base_0, + __name = "What" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + What = _class_0 +end +do + local _class_0 + local _parent_0 = What + local _base_0 = { + val = 2323, + something = function(self) + local _base_1 = _class_0.__parent + local _fn_0 = _base_1.something + return function(...) + return _fn_0(self, ...) + end + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, ...) + return _class_0.__parent.__init(self, ...) + end, + __base = _base_0, + __name = "Hello", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + Hello = _class_0 +end +do + local _with_0 = Hello() + x = _with_0:something() + print(x) + x() +end +local CoolSuper +do + local _class_0 + local _base_0 = { + hi = function(self) + _class_0.__parent.__base.hi(self, 1, 2, 3, 4)(1, 2, 3, 4) + _class_0.__parent.something(1, 2, 3, 4) + local _scrap_0 = _class_0.__parent.something(1, 2, 3, 4).world + _class_0.__parent.yeah(self, "world").okay(hi, hi, hi) + _scrap_0 = something.super + _scrap_0 = _class_0.__parent.super.super.super + do + local _base_1 = _class_0.__parent + local _fn_0 = _base_1.hello + _scrap_0 = function(...) + return _fn_0(self, ...) + end + end + return nil + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function() end, + __base = _base_0, + __name = "CoolSuper" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + CoolSuper = _class_0 +end +x = self.hello +x = self.__class.hello +self:hello("world") +self.__class:hello("world") +self.__class:one(self.__class:two(4, 5)(self.three, self.four)) +local xx +xx = function(hello, world, cool) + self.hello, self.__class.world = hello, world +end +local ClassMan +do + local _class_0 + local _base_0 = { + blue = function(self) end, + green = function(self) end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function() end, + __base = _base_0, + __name = "ClassMan" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + local self = _class_0 + self.yeah = 343 + self.hello = 3434 + self.world = 23423 + self.red = function(self) end + ClassMan = _class_0 +end +x = self +local y = self.__class +self(something) +self.__class(something) +local self = self + self / self +self = 343 +self.hello(2, 3, 4) +local _scrap_0 = hello[self].world +local Whacko +do + local _class_0 + local hello + local _base_0 = { } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function() end, + __base = _base_0, + __name = "Whacko" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + local self = _class_0 + local _scrap_1 = self.hello + if something then + print("hello world") + end + hello = "world" + self.another = "day" + if something then + print("yeah") + end + Whacko = _class_0 +end +print("hello") +local yyy +yyy = function() + local Cool + do + local _class_0 + local _base_0 = { } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function() end, + __base = _base_0, + __name = "Cool" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + local self = _class_0 + local _scrap_1 = nil + Cool = _class_0 + return _class_0 + end +end +do + local _class_0 + local _base_0 = { } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function() end, + __base = _base_0, + __name = "D" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + local self = _class_0 + local _scrap_1 = nil + a.b.c.D = _class_0 +end +do + local _class_0 + local _base_0 = { } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function() end, + __base = _base_0, + __name = "hello" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + local self = _class_0 + local _scrap_1 = nil + a.b["hello"] = _class_0 +end +do + local _class_0 + local _parent_0 = Hello.World + local _base_0 = { } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, ...) + return _class_0.__parent.__init(self, ...) + end, + __base = _base_0, + __name = "Something", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + local self = _class_0 + local _scrap_1 = nil + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + (function() + return require("moon") + end)().Something = _class_0 +end +local a +do + local _class_0 + local _base_0 = { } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function() end, + __base = _base_0, + __name = "a" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + a = _class_0 +end +local b +local Something +do + local _class_0 + local _base_0 = { } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function() end, + __base = _base_0, + __name = "Something" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + Something = _class_0 + b = _class_0 +end +local c +do + local _class_0 + local _parent_0 = Hello + local _base_0 = { } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, ...) + return _class_0.__parent.__init(self, ...) + end, + __base = _base_0, + __name = "Something", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + Something = _class_0 + c = _class_0 +end +local d +do + local _class_0 + local _parent_0 = World + local _base_0 = { } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, ...) + return _class_0.__parent.__init(self, ...) + end, + __base = _base_0, + __name = "d", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + d = _class_0 +end +print(((function() + local WhatsUp + do + local _class_0 + local _base_0 = { } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function() end, + __base = _base_0, + __name = "WhatsUp" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + WhatsUp = _class_0 + return _class_0 + end +end)()).__name) +do + local _class_0 + local _base_0 = { } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function() end, + __base = _base_0, + __name = "Something" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + local self = _class_0 + local _scrap_1 = nil + Something = _class_0 +end +do + local _class_0 + local val, insert + local _base_0 = { } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function(self) + return print(insert, val) + end, + __base = _base_0, + __name = "Something" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + local self = _class_0 + val = 23 + insert = table.insert + Something = _class_0 +end +do + local _class_0 + local _base_0 = { } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = hi, + __base = _base_0, + __name = "X" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + X = _class_0 +end +do + local _class_0 + local _parent_0 = Thing + local _base_0 = { + dang = function(self) + return { + hello = function() + return _class_0.__parent.__base.dang(self) + end, + world = function() + return _class_0.__parent.one + end + } + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, ...) + return _class_0.__parent.__init(self, ...) + end, + __base = _base_0, + __name = "Cool", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + Cool = _class_0 +end +do + local _class_0 + local _parent_0 = Thing + local _base_0 = { + dang = do_something(function(self) + return _class_0.__parent.__base.dang(self) + end) + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, ...) + return _class_0.__parent.__init(self, ...) + end, + __base = _base_0, + __name = "Whack", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + Whack = _class_0 +end +do + local _class_0 + local one_thing, another_thing + local _base_0 = { + get_thing = function(self) + return one_thing + end, + set_thing = function(self, v) + another_thing = v + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function() end, + __base = _base_0, + __name = "LocalHoist" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + local self = _class_0 + one_thing = "hello" + LocalHoist = _class_0 +end +do + local _class_0 + local _parent_0 = LocalHoist + local LocalHoist + local _base_0 = { + get_class = function(self) + return LocalHoist + end + } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, ...) + return _class_0.__parent.__init(self, ...) + end, + __base = _base_0, + __name = "LocalHoistExtends", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + LocalHoistExtends = _class_0 +end +do + local _class_0 + local _parent_0 = Thing + local _base_0 = { } + _base_0.__index = _base_0 + setmetatable(_base_0, _parent_0.__base) + _class_0 = setmetatable({ + __init = function(self, ...) + return _class_0.__parent.__init(self, ...) + end, + __base = _base_0, + __name = "Wowha", + __parent = _parent_0 + }, { + __index = function(cls, name) + local val = rawget(_base_0, name) + if val == nil then + local parent = rawget(cls, "__parent") + if parent then + return parent[name] + end + else + return val + end + end, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + local self = _class_0 + self.butt = function() + _class_0.__parent.butt(self) + local _scrap_1 = _class_0.__parent.hello + _class_0.__parent.hello(self) + local _base_1 = _class_0.__parent + local _fn_0 = _base_1.hello + return function(...) + return _fn_0(self, ...) + end + end + self.zone = cool({ + function() + _class_0.__parent.zone(self) + local _scrap_1 = _class_0.__parent.hello + _class_0.__parent.hello(self) + local _base_1 = _class_0.__parent + local _fn_0 = _base_1.hello + return function(...) + return _fn_0(self, ...) + end + end + }) + if _parent_0.__inherited then + _parent_0.__inherited(_parent_0, _class_0) + end + Wowha = _class_0 +end +return nil \ No newline at end of file diff --git a/spec/outputs/comprehension.lua b/spec/outputs/comprehension.lua new file mode 100644 index 00000000..14e1ca7c --- /dev/null +++ b/spec/outputs/comprehension.lua @@ -0,0 +1,260 @@ +local items = { + 1, + 2, + 3, + 4, + 5, + 6 +} +local out +do + local _tbl_0 = { } + for k in items do + _tbl_0[k] = k * 2 + end + out = _tbl_0 +end +local x = { + hello = "world", + okay = 2323 +} +local copy +do + local _tbl_0 = { } + for k, v in pairs(x) do + if k ~= "okay" then + _tbl_0[k] = v + end + end + copy = _tbl_0 +end +local _scrap_0 +do + local _tbl_0 = { } + for x in yes do + local _key_0, _val_0 = unpack(x) + _tbl_0[_key_0] = _val_0 + end + _scrap_0 = _tbl_0 +end +do + local _tbl_0 = { } + local _list_0 = yes + for _index_0 = 1, #_list_0 do + local x = _list_0[_index_0] + local _key_0, _val_0 = unpack(x) + _tbl_0[_key_0] = _val_0 + end + _scrap_0 = _tbl_0 +end +do + local _tbl_0 = { } + for x in yes do + local _key_0, _val_0 = xxxx + _tbl_0[_key_0] = _val_0 + end + _scrap_0 = _tbl_0 +end +do + local _tbl_0 = { } + local _list_0 = { + { + 1, + 2 + }, + { + 3, + 4 + } + } + for _index_0 = 1, #_list_0 do + local x = _list_0[_index_0] + local _key_0, _val_0 = unpack((function() + local _accum_0 = { } + local _len_0 = 1 + for i, a in ipairs(x) do + _accum_0[_len_0] = a * i + _len_0 = _len_0 + 1 + end + return _accum_0 + end)()) + _tbl_0[_key_0] = _val_0 + end + _scrap_0 = _tbl_0 +end +local n1 +do + local _accum_0 = { } + local _len_0 = 1 + for i = 1, 10 do + _accum_0[_len_0] = i + _len_0 = _len_0 + 1 + end + n1 = _accum_0 +end +local n2 +do + local _accum_0 = { } + local _len_0 = 1 + for i = 1, 10 do + if i % 2 == 1 then + _accum_0[_len_0] = i + _len_0 = _len_0 + 1 + end + end + n2 = _accum_0 +end +local aa +do + local _accum_0 = { } + local _len_0 = 1 + for x = 1, 10 do + for y = 5, 14 do + _accum_0[_len_0] = { + x, + y + } + _len_0 = _len_0 + 1 + end + end + aa = _accum_0 +end +local bb +do + local _accum_0 = { } + local _len_0 = 1 + for thing in y do + for i = 1, 10 do + _accum_0[_len_0] = y + _len_0 = _len_0 + 1 + end + end + bb = _accum_0 +end +local cc +do + local _accum_0 = { } + local _len_0 = 1 + for i = 1, 10 do + for thing in y do + _accum_0[_len_0] = y + _len_0 = _len_0 + 1 + end + end + cc = _accum_0 +end +local dd +do + local _accum_0 = { } + local _len_0 = 1 + for i = 1, 10 do + if cool then + for thing in y do + if x > 3 then + if c + 3 then + _accum_0[_len_0] = y + _len_0 = _len_0 + 1 + end + end + end + end + end + dd = _accum_0 +end +do + local _tbl_0 = { } + for i = 1, 10 do + _tbl_0["hello"] = "world" + end + _scrap_0 = _tbl_0 +end +local j +do + local _accum_0 = { } + local _len_0 = 1 + for _des_0 in things do + local a, b, c + a, b, c = _des_0[1], _des_0[2], _des_0[3] + _accum_0[_len_0] = a + _len_0 = _len_0 + 1 + end + j = _accum_0 +end +local k +do + local _accum_0 = { } + local _len_0 = 1 + local _list_0 = things + for _index_0 = 1, #_list_0 do + local _des_0 = _list_0[_index_0] + local a, b, c + a, b, c = _des_0[1], _des_0[2], _des_0[3] + _accum_0[_len_0] = a + _len_0 = _len_0 + 1 + end + k = _accum_0 +end +local i +do + local _accum_0 = { } + local _len_0 = 1 + local _list_0 = things + for _index_0 = 1, #_list_0 do + local _des_0 = _list_0[_index_0] + local hello, world + hello, world = _des_0.hello, _des_0.world + _accum_0[_len_0] = hello + _len_0 = _len_0 + 1 + end + i = _accum_0 +end +local hj +do + local _tbl_0 = { } + for _des_0 in things do + local a, b, c + a, b, c = _des_0[1], _des_0[2], _des_0[3] + _tbl_0[a] = c + end + hj = _tbl_0 +end +local hk +do + local _tbl_0 = { } + local _list_0 = things + for _index_0 = 1, #_list_0 do + local _des_0 = _list_0[_index_0] + local a, b, c + a, b, c = _des_0[1], _des_0[2], _des_0[3] + _tbl_0[a] = c + end + hk = _tbl_0 +end +local hi +do + local _tbl_0 = { } + local _list_0 = things + for _index_0 = 1, #_list_0 do + local _des_0 = _list_0[_index_0] + local hello, world + hello, world = _des_0.hello, _des_0.world + _tbl_0[hello] = world + end + hi = _tbl_0 +end +for _des_0 in things do + local a, b, c + a, b, c = _des_0[1], _des_0[2], _des_0[3] + ok(a, b, c) +end +local _max_0 = 3 + 4 +for _index_0 = 1 + 2, _max_0 < 0 and #items + _max_0 or _max_0 do + local item = items[_index_0] + local _scrap_1 = item +end +local _max_1 = 2 - thing[4] +for _index_0 = hello() * 4, _max_1 < 0 and #items + _max_1 or _max_1 do + local item = items[_index_0] + local _scrap_1 = item +end +return nil \ No newline at end of file diff --git a/tests/outputs/cond.lua b/spec/outputs/cond.lua similarity index 76% rename from tests/outputs/cond.lua rename to spec/outputs/cond.lua index 5da6d042..f3ca1a6b 100644 --- a/tests/outputs/cond.lua +++ b/spec/outputs/cond.lua @@ -1,25 +1,25 @@ local you_cool = false if cool then if you_cool then - local _ = one + local _scrap_0 = one else if eatdic then - local _ = yeah + local _scrap_0 = yeah else - local _ = two - _ = three + local _scrap_0 = two + _scrap_0 = three end end else - local _ = no + local _scrap_0 = no end if cool then - local _ = no + local _scrap_0 = no end if cool then - local _ = no + local _scrap_0 = no else - local _ = yes + local _scrap_0 = yes end if cool then wow(cool) @@ -29,12 +29,12 @@ end if working then if cool then if cool then - local _ = okay + local _scrap_0 = okay else - local _ = what + local _scrap_0 = what end else - local _ = nah + local _scrap_0 = nah end end if yeah then @@ -112,19 +112,19 @@ hello = 5 + (function() end)() local z = false if false then - local _ = one + local _scrap_0 = one else do local x = true if x then - local _ = two + local _scrap_0 = two else do z = true if z then - local _ = three + local _scrap_0 = three else - local _ = four + local _scrap_0 = four end end end @@ -150,6 +150,22 @@ else end end end +local kzy +kzy = function() + do + local something = true + if something then + return 1 + else + do + local another = false + if another then + return 2 + end + end + end + end +end if not (true) then print("cool!") end @@ -230,8 +246,34 @@ if not (value) then 3 } end +do + local j = 100 + do + j = hi() + if not j then + error("not j!") + end + end +end local a = 12 local c, b if something then a, c, b = "cool" -end \ No newline at end of file +end +local j +if 1 then + if 2 then + j = 3 + end +else + j = 6 +end +local m +if 1 then + if 2 then + m = 3 + end +else + m = 6 +end +return nil \ No newline at end of file diff --git a/spec/outputs/destructure.lua b/spec/outputs/destructure.lua new file mode 100644 index 00000000..0342ac62 --- /dev/null +++ b/spec/outputs/destructure.lua @@ -0,0 +1,407 @@ +do + local a, b + do + local _obj_0 = hello + a, b = _obj_0[1], _obj_0[2] + end + local c + do + local _obj_0 = hello + a, b, c = _obj_0[1][1], _obj_0[2], _obj_0[3][1] + end + local hello, world + do + local _obj_0 = value + hello, world = _obj_0.hello, _obj_0.world + end +end +do + local no, thing + do + local _obj_0 = world + no, thing = _obj_0.yes, _obj_0[1] + end + local a, b, c, d + do + local _obj_0 = yeah + a, b, c, d = _obj_0.a, _obj_0.b, _obj_0.c, _obj_0.d + end + a = one[1] + local _scrap_0 = two + b = one[1] + c = nil + d = one[1] + local e = two + local x = one + local y + y = two[1] + local xx, yy = 1, 2 + do + local _obj_0 = { + xx, + yy + } + yy, xx = _obj_0[1], _obj_0[2] + end + local f, g + do + local _obj_0 = tbl + a, b, c, d, e, f, g = _obj_0[1], _obj_0.b, _obj_0[2], _obj_0.d, _obj_0[3], _obj_0.f, _obj_0[4] + end +end +do + local futurists = { + sculptor = "Umberto Boccioni", + painter = "Vladimir Burliuk", + poet = { + name = "F.T. Marinetti", + address = { + "Via Roma 42R", + "Bellagio, Italy 22021" + } + } + } + local name, street, city + name, street, city = futurists.poet.name, futurists.poet.address[1], futurists.poet.address[2] +end +do + self.world = x[1] + do + local _obj_0 = x + a.b, c.y, func().z = _obj_0[1], _obj_0[2], _obj_0[3] + end + self.world = x.world +end +do + local thing = { + { + 1, + 2 + }, + { + 3, + 4 + } + } + for _index_0 = 1, #thing do + local _des_0 = thing[_index_0] + local x, y + x, y = _des_0[1], _des_0[2] + print(x, y) + end +end +do + do + local _with_0 = thing + local a, b + a, b = _with_0[1], _with_0[2] + print(a, b) + end +end +do + local thing = nil + do + local _des_0 = thing + if _des_0 then + local a + a = _des_0[1] + print(a) + else + print("nothing") + end + end + local thang = { + 1, + 2 + } + do + local _des_0 = thang + if _des_0 then + local a, b + a, b = _des_0[1], _des_0[2] + print(a, b) + end + end + do + local _des_0 = thing + if _des_0 then + local a, b + a, b = _des_0[1], _des_0[2] + print(a, b) + else + do + local _des_1 = thang + if _des_1 then + local c, d + c, d = _des_1[1], _des_1[2] + print(c, d) + else + print("NO") + end + end + end + end +end +do + local z = "yeah" + local a, b, c + a, b, c = z[1], z[2], z[3] +end +do + local a, b, c + do + local _obj_0 = z + a, b, c = _obj_0[1], _obj_0[2], _obj_0[3] + end +end +local _scrap_0 +_scrap_0 = function(z) + local a, b, c + a, b, c = z[1], z[2], z[3] +end +do + local z = "oo" + local _scrap_1 + _scrap_1 = function(k) + local a, b, c + a, b, c = z[1], z[2], z[3] + end +end +local a, b +do + local _accum_0 = { } + local _len_0 = 1 + local _list_0 = { + 2, + 3 + } + for _index_0 = 1, #_list_0 do + local i = _list_0[_index_0] + _accum_0[_len_0] = i * i + _len_0 = _len_0 + 1 + end + a, b = _accum_0[1], _accum_0[2] +end +local c, d +do + local _accum_0 = { } + local _len_0 = 1 + local _list_0 = { + 1, + 2 + } + for _index_0 = 1, #_list_0 do + local v = _list_0[_index_0] + _accum_0[_len_0] = v * 2 + _len_0 = _len_0 + 1 + end + c, d = _accum_0[1], _accum_0[2] +end +local left, right +if cond then + do + local _obj_0 = { + left = 1, + right = 2 + } + left, right = _obj_0.left, _obj_0.right + end +else + do + local _obj_0 = { + left = 3 + } + left, right = _obj_0.left, _obj_0.right + end +end +local p, q +do + do + local _obj_0 = { + 10, + 20 + } + p, q = _obj_0[1], _obj_0[2] + end +end +local val +local _exp_0 = x +if 2 == _exp_0 then + val = ({ + val = "two" + }).val +end +local t +do + local _with_0 = something() + _with_0.x = 5 + t = _with_0[1] +end +local u, v +if cond then + do + local _obj_0 = { + 7, + 8 + } + u, v = _obj_0[1], _obj_0[2] + end +end +local n, m +if cond then + local _destruct_0 + n, _destruct_0 = 1, { + m = 2 + } + m = _destruct_0.m +end +local k +do + local _tbl_0 = { } + for w in thing:gmatch("%w") do + _tbl_0[w] = true + end + k = _tbl_0.k +end +local inner +if cond then + inner = ({ + a = { + "deep" + } + }).a[1] +end +local lit +lit = ({ + 42 +})[1] +local single +if cond then + do + local _obj_0 = { + single = 1 + }, { + single = 2 + } + single = _obj_0.single + end +end +local grab +grab = function(...) + local first_arg + do + local _obj_0 = ... + first_arg = _obj_0.first_arg + end +end +local nothing +do + local _obj_0 = nil + nothing = _obj_0[1] +end +local negated +do + local _obj_0 = not thing + negated = _obj_0.negated +end +local len +do + local _obj_0 = #thing + len = _obj_0.len +end +obj.a, obj["b"], self.prop, self.__class.cls_prop = t.x, t.y, t.z, t.w +local basic +basic = function(_arg_0) + local a, b + a, b = _arg_0.a, _arg_0.b + return a + b +end +local two +two = function(_arg_0, _arg_1) + local x1, y1 + x1, y1 = _arg_0.x, _arg_0.y + local x2, y2 + x2, y2 = _arg_1.x, _arg_1.y + return x1 * x2 + y1 * y2 +end +local with_default +with_default = function(_arg_0) + if _arg_0 == nil then + _arg_0 = { + a = 1, + b = 2 + } + end + local a, b + a, b = _arg_0.a, _arg_0.b + return a + b +end +local nested +nested = function(_arg_0) + local x, y, first + x, y, first = _arg_0.pos.x, _arg_0.pos.y, _arg_0[1] + return x + y + first +end +local positional +positional = function(_arg_0) + local first, second + first, second = _arg_0[1], _arg_0[2] + return first .. second +end +local mixed +mixed = function(name, _arg_0, rest, ...) + local width, height + width, height = _arg_0.width, _arg_0.height + if rest == nil then + rest = 1 + end + return print(name, width, height, rest, ...) +end +local bound +bound = function(self, _arg_0) + local count + count = _arg_0.count + return self.total + count +end +local Point +do + local _class_0 + local _base_0 = { } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function(self, _arg_0) + self.x, self.y = _arg_0.x, _arg_0.y + end, + __base = _base_0, + __name = "Point" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + Point = _class_0 +end +local shadowed = "outer" +local shadow_fn +shadow_fn = function(_arg_0) + local shadowed + shadowed = _arg_0.shadowed + return shadowed +end +local ordered +ordered = function(self, _arg_0, b, c) + local a + a = _arg_0.a + if b == nil then + b = a + end + if c == nil then + c = b + end + self.c = c + return a + b + self.c +end \ No newline at end of file diff --git a/spec/outputs/destructure_multi_return.lua b/spec/outputs/destructure_multi_return.lua new file mode 100644 index 00000000..8ae05835 --- /dev/null +++ b/spec/outputs/destructure_multi_return.lua @@ -0,0 +1,24 @@ +local num, _destruct_0 = two_values() +local message +message = _destruct_0.message +local _destruct_1, _destruct_2, _destruct_3 = multi_destruct() +local alpha +alpha = _destruct_1.alpha +local beta +beta = _destruct_2.beta +local gamma +gamma = _destruct_3.gamma +local head, _destruct_4, tail = mix() +local content +content = _destruct_4.content +local start, _destruct_5, rest = forward(88, 77) +local extra +extra = _destruct_5.extra +local prop, _destruct_6 = hello.world() +local value +value = _destruct_6.value +local _destruct_7, _destruct_8 = builder:build() +local built +built = _destruct_7.value +local status +status = _destruct_8.status \ No newline at end of file diff --git a/spec/outputs/do.lua b/spec/outputs/do.lua new file mode 100644 index 00000000..e07b43c9 --- /dev/null +++ b/spec/outputs/do.lua @@ -0,0 +1,48 @@ +do + print("hello") + print("world") +end +local x +do + print("hello") + x = print("world") +end +local y +do + local things = "shhh" + y = function() + return "hello: " .. things + end +end +local _scrap_0 +_scrap_0 = function() + if something then + do + return "yeah" + end + end +end +local t = { + y = (function() + local number = 100 + return function(x) + return x + number + end + end)() +} +return function(y, k) + if y == nil then + y = ((function() + x = 10 + 2 + return x + end)()) + end + if k == nil then + do + k = "nothing" + end + end + do + return "uhhh" + end +end \ No newline at end of file diff --git a/spec/outputs/export.lua b/spec/outputs/export.lua new file mode 100644 index 00000000..08fb4ce8 --- /dev/null +++ b/spec/outputs/export.lua @@ -0,0 +1,88 @@ +do + a, b, c = 223, 343 + cool = "dad" +end +do + do + local _class_0 + local _base_0 = { + umm = "cool" + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function() end, + __base = _base_0, + __name = "Something" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + Something = _class_0 + end +end +do + local d + a, b, c, d = "hello" +end +do + local What + if this then + What = 232 + else + What = 4343 + end + local another = 3434 + Another = 7890 + if inner then + local Yeah = "10000" + end + if this then + What = 232 + else + What = 4343 + end +end +do + if this then + What = 232 + else + What = 4343 + end + x, y, z = 1, 2, 3 + y = function() + local hallo = 3434 + end + do + local j = 2000 + end +end +do + x = 3434 + if y then + x = 10 + end +end +do + if y then + local x = 10 + end + x = 3434 +end +do + do + k = 1212 + do + local h = 100 + end + y = function() + local h = 100 + k = 100 + end + end + local h = 100 +end \ No newline at end of file diff --git a/spec/outputs/funcs.lua b/spec/outputs/funcs.lua new file mode 100644 index 00000000..5ce19ca3 --- /dev/null +++ b/spec/outputs/funcs.lua @@ -0,0 +1,210 @@ +local x +x = function() + return print(what) +end +local _scrap_0 +_scrap_0 = function() end +_scrap_0 = function() + return function() + return function() end + end +end +go(to(the(barn))) +open(function() + return the(function() + return door + end) +end) +open(function() + the(door) + local hello + hello = function() + return my(func) + end +end) +local h +h = function() + return hi +end +eat(function() end, world); +(function() end)() +x = function(...) end +hello() +hello.world() +_scrap_0 = hello().something +_scrap_0 = what()["ofefe"] +what()(the()(heck())) +_scrap_0 = function(a, b, c, d, e) end +_scrap_0 = function(a, a, a, a, a) + return print(a) +end +_scrap_0 = function(x) + if x == nil then + x = 23023 + end +end +_scrap_0 = function(x) + if x == nil then + x = function(y) + if y == nil then + y = function() end + end + end + end +end +_scrap_0 = function(x) + if x == nil then + if something then + x = yeah + else + x = no + end + end +end +local something +something = function(hello, world) + if hello == nil then + hello = 100 + end + if world == nil then + world = function(x) + if x == nil then + x = [[yeah cool]] + end + return print("eat rice") + end + end + return print(hello) +end +_scrap_0 = function(self, x, y) end +_scrap_0 = function(self, x, y) + self.x, self.y = x, y +end +_scrap_0 = function(self, x) + if x == nil then + x = 1 + end +end +_scrap_0 = function(self, x, y, z) + if x == nil then + x = 1 + end + if z == nil then + z = "hello world" + end + self.x, self.z = x, z +end +x(function() end) +y(function() + return 1 +end) +z(function() + return 1, "hello", "world" +end) +k(function() + if yes then + return + else + return + end +end) +_scrap_0 = function() + if something then + return real_name + end +end +d(function() + return print("hello world") +end, 10) +d(1, 2, 3, 4, 5, 6, (function() + if something then + print("okay") + return 10 + end +end)(), 10, 20) +f()()(what)(function() + return print("srue") +end, 123) +x = function(a, b) + return print("what") +end +local y +y = function(a, b) + if a == nil then + a = "hi" + end + if b == nil then + b = 23 + end + return print("what") +end +local z +z = function(a, b) + if a == nil then + a = "hi" + end + if b == nil then + b = 23 + end + return print("what") +end +local j +j = function(f, g, m, a, b) + if a == nil then + a = "hi" + end + if b == nil then + b = 23 + end + return print("what") +end +y = function(a, b, ...) + if a == nil then + a = "hi" + end + if b == nil then + b = 23 + end + return print("what") +end +y = function(a, b, ...) + if a == nil then + a = "hi" + end + if b == nil then + b = 23 + end + return print("what") +end +local args +args = function(a, b) + return print("what") +end +args = function(a, b) + if a == nil then + a = "hi" + end + if b == nil then + b = 23 + end + return print("what") +end +args = function(a, b) + if a == nil then + a = "hi" + end + if b == nil then + b = 23 + end + return print("what") +end +args = function(f, g, m, a, b) + if a == nil then + a = "hi" + end + if b == nil then + b = 23 + end + return print("what") +end +return nil \ No newline at end of file diff --git a/spec/outputs/import.lua b/spec/outputs/import.lua new file mode 100644 index 00000000..694c9b2e --- /dev/null +++ b/spec/outputs/import.lua @@ -0,0 +1,112 @@ +local hello +hello = yeah.hello +local world +do + local _obj_0 = table["cool"] + hello, world = _obj_0.hello, _obj_0.world +end +local a, b, c +do + local _obj_0 = items + a, b, c = _obj_0.a, (function() + local _base_0 = _obj_0 + local _fn_0 = _base_0.b + return function(...) + return _fn_0(_base_0, ...) + end + end)(), _obj_0.c +end +local master, ghost +do + local _obj_0 = find("mytable") + master, ghost = _obj_0.master, (function() + local _base_0 = _obj_0 + local _fn_0 = _base_0.ghost + return function(...) + return _fn_0(_base_0, ...) + end + end)() +end +local yumm +a, yumm = 3434, "hello" +local _table_0 = 232 +local something +something = a(table).something +if indent then + local okay, well + do + local _obj_0 = tables[100] + okay, well = _obj_0.okay, (function() + local _base_0 = _obj_0 + local _fn_0 = _base_0.well + return function(...) + return _fn_0(_base_0, ...) + end + end)() + end +end +do + do + local _obj_0 = z + a, b, c = _obj_0.a, _obj_0.b, _obj_0.c + end +end +do + do + local _obj_0 = z + a, b, c = _obj_0.a, _obj_0.b, _obj_0.c + end +end +do + do + local _obj_0 = z + a, b, c = _obj_0.a, _obj_0.b, _obj_0.c + end +end +do + do + local _obj_0 = z + a, b, c = _obj_0.a, _obj_0.b, _obj_0.c + end +end +do + do + local _obj_0 = z + a, b, c = _obj_0.a, _obj_0.b, _obj_0.c + end +end +do + local use, insert + use = function() + return insert("hello") + end + insert = table.insert +end +local Pipeline +do + local _class_0 + local insert + local _base_0 = { + add = function(self, ...) + return insert(self, ...) + end + } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function() end, + __base = _base_0, + __name = "Pipeline" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + local self = _class_0 + insert = table.insert + Pipeline = _class_0 + return _class_0 +end \ No newline at end of file diff --git a/tests/outputs/lists.lua b/spec/outputs/lists.lua similarity index 50% rename from tests/outputs/lists.lua rename to spec/outputs/lists.lua index acc09ba8..a3e701a0 100644 --- a/tests/outputs/lists.lua +++ b/spec/outputs/lists.lua @@ -1,17 +1,18 @@ -local hi = (function() +local hi +do local _accum_0 = { } - local _len_0 = 0 + local _len_0 = 1 for _, x in ipairs({ 1, 2, 3, 4 }) do - _len_0 = _len_0 + 1 _accum_0[_len_0] = x * 2 + _len_0 = _len_0 + 1 end - return _accum_0 -end)() + hi = _accum_0 +end local items = { 1, 2, @@ -20,23 +21,15 @@ local items = { 5, 6 } -local mm = (function() - local _accum_0 = { } - local _len_0 = 0 - for self.x in ipairs(items) do - _len_0 = _len_0 + 1 - _accum_0[_len_0] = self.x - end - return _accum_0 -end)() for z in ipairs(items) do if z > 4 then - local _ = z + local _scrap_0 = z end end -local rad = (function() +local rad +do local _accum_0 = { } - local _len_0 = 0 + local _len_0 = 1 for a in ipairs({ 1, 2, @@ -46,18 +39,18 @@ local rad = (function() 6 }) do if good_number(a) then - _len_0 = _len_0 + 1 _accum_0[_len_0] = { a } + _len_0 = _len_0 + 1 end end - return _accum_0 -end)() + rad = _accum_0 +end for z in items do for j in list do if z > 4 then - local _ = z + local _scrap_0 = z end end end @@ -78,44 +71,45 @@ range = function(count) end dump((function() local _accum_0 = { } - local _len_0 = 0 + local _len_0 = 1 for x in range(10) do - _len_0 = _len_0 + 1 _accum_0[_len_0] = x + _len_0 = _len_0 + 1 end return _accum_0 end)()) dump((function() local _accum_0 = { } - local _len_0 = 0 + local _len_0 = 1 for x in range(5) do if x > 2 then for y in range(5) do - _len_0 = _len_0 + 1 _accum_0[_len_0] = { x, y } + _len_0 = _len_0 + 1 end end end return _accum_0 end)()) -local things = (function() +local things +do local _accum_0 = { } - local _len_0 = 0 + local _len_0 = 1 for x in range(10) do if x > 5 then for y in range(10) do if y > 7 then - _len_0 = _len_0 + 1 _accum_0[_len_0] = x + y + _len_0 = _len_0 + 1 end end end end - return _accum_0 -end)() + things = _accum_0 +end for x in ipairs({ 1, 2, @@ -135,17 +129,18 @@ for x in items do print("hello", x) end for x in x do - local _ = x + local _scrap_0 = x end -local x = (function() +local x +do local _accum_0 = { } - local _len_0 = 0 + local _len_0 = 1 for x in x do - _len_0 = _len_0 + 1 _accum_0[_len_0] = x + _len_0 = _len_0 + 1 end - return _accum_0 -end)() + x = _accum_0 +end for x in ipairs({ 1, 2, @@ -161,53 +156,50 @@ for x in ipairs({ end end end -local double = (function() +local double +do local _accum_0 = { } - local _len_0 = 0 - local _list_0 = items - for _index_0 = 1, #_list_0 do - x = _list_0[_index_0] - _len_0 = _len_0 + 1 + local _len_0 = 1 + for _index_0 = 1, #items do + local x = items[_index_0] _accum_0[_len_0] = x * 2 + _len_0 = _len_0 + 1 end - return _accum_0 -end)() -local _list_0 = double -for _index_0 = 1, #_list_0 do - x = _list_0[_index_0] + double = _accum_0 +end +for _index_0 = 1, #double do + local x = double[_index_0] print(x) end -local cut = (function() +local cut +do local _accum_0 = { } - local _len_0 = 0 - local _list_1 = items - for _index_0 = 1, #_list_1 do - x = _list_1[_index_0] + local _len_0 = 1 + for _index_0 = 1, #items do + local x = items[_index_0] if x > 3 then - _len_0 = _len_0 + 1 _accum_0[_len_0] = x + _len_0 = _len_0 + 1 end end - return _accum_0 -end)() -local hello = (function() + cut = _accum_0 +end +local hello +do local _accum_0 = { } - local _len_0 = 0 - local _list_1 = items - for _index_0 = 1, #_list_1 do - x = _list_1[_index_0] - local _list_2 = items - for _index_1 = 1, #_list_2 do - local y = _list_2[_index_1] - _len_0 = _len_0 + 1 + local _len_0 = 1 + for _index_0 = 1, #items do + local x = items[_index_0] + for _index_1 = 1, #items do + local y = items[_index_1] _accum_0[_len_0] = x + y + _len_0 = _len_0 + 1 end end - return _accum_0 -end)() -local _list_1 = hello -for _index_0 = 1, #_list_1 do - local z = _list_1[_index_0] + hello = _accum_0 +end +for _index_0 = 1, #hello do + local z = hello[_index_0] print(z) end x = { @@ -219,83 +211,53 @@ x = { 6, 7 } -local _list_2 = x local _max_0 = -5 -for _index_0 = 2, _max_0 < 0 and #_list_2 + _max_0 or _max_0, 2 do - local y = _list_2[_index_0] +for _index_0 = 2, _max_0 < 0 and #x + _max_0 or _max_0, 2 do + local y = x[_index_0] print(y) end -local _list_3 = x local _max_1 = 3 -for _index_0 = 1, _max_1 < 0 and #_list_3 + _max_1 or _max_1 do - local y = _list_3[_index_0] +for _index_0 = 1, _max_1 < 0 and #x + _max_1 or _max_1 do + local y = x[_index_0] print(y) end -local _list_4 = x -for _index_0 = 2, #_list_4 do - local y = _list_4[_index_0] +for _index_0 = 2, #x do + local y = x[_index_0] print(y) end -local _list_5 = x -for _index_0 = 1, #_list_5, 2 do - local y = _list_5[_index_0] +for _index_0 = 1, #x, 2 do + local y = x[_index_0] print(y) end -local _list_6 = x -for _index_0 = 2, #_list_6, 2 do - local y = _list_6[_index_0] +for _index_0 = 2, #x, 2 do + local y = x[_index_0] print(y) end local a, b, c = 1, 5, 2 -local _list_7 = x local _max_2 = b -for _index_0 = a, _max_2 < 0 and #_list_7 + _max_2 or _max_2, c do - local y = _list_7[_index_0] +for _index_0 = a, _max_2 < 0 and #x + _max_2 or _max_2, c do + local y = x[_index_0] print(y) end local normal normal = function(hello) - return (function() - local _accum_0 = { } - local _len_0 = 0 - for x in yeah do - _len_0 = _len_0 + 1 - _accum_0[_len_0] = x - end - return _accum_0 - end)() + local _accum_0 = { } + local _len_0 = 1 + for x in yeah do + _accum_0[_len_0] = x + _len_0 = _len_0 + 1 + end + return _accum_0 end local test = x(1, 2, 3, 4, 5) -local _list_8 = test -for _index_0 = 1, #_list_8 do - local thing = _list_8[_index_0] +for _index_0 = 1, #test do + local thing = test[_index_0] print(thing) end -local _ -_ = function() - local _list_9 = rows - for _index_0 = 1, #_list_9 do - local row = _list_9[_index_0] +return function() + local _list_0 = rows + for _index_0 = 1, #_list_0 do + local row = _list_0[_index_0] a = b end -end -_ = function() - local _list_9 = things - for _index_0 = 1, #_list_9 do - x = _list_9[_index_0] - _ = x - end -end -_ = function() - return (function() - local _accum_0 = { } - local _len_0 = 0 - local _list_9 = things - for _index_0 = 1, #_list_9 do - x = _list_9[_index_0] - _len_0 = _len_0 + 1 - _accum_0[_len_0] = x - end - return _accum_0 - end)() end \ No newline at end of file diff --git a/spec/outputs/literals.lua b/spec/outputs/literals.lua new file mode 100644 index 00000000..b0d8ccbc --- /dev/null +++ b/spec/outputs/literals.lua @@ -0,0 +1,26 @@ +local _scrap_0 = 121 +_scrap_0 = 121.2323 +_scrap_0 = 121.2323e-1 +_scrap_0 = 121.2323e13434 +_scrap_0 = 2323E34 +_scrap_0 = 0x12323 +_scrap_0 = 0xfF2323 +_scrap_0 = 0xabcdef +_scrap_0 = 0xABCDEF +_scrap_0 = .2323 +_scrap_0 = .2323e-1 +_scrap_0 = .2323e13434 +_scrap_0 = 1LL +_scrap_0 = 1ULL +_scrap_0 = 9332LL +_scrap_0 = 9332 +_scrap_0 = 0x2aLL +_scrap_0 = 0x2aULL +_scrap_0 = [[ hello world ]] +_scrap_0 = [=[ hello world ]=] +_scrap_0 = [====[ hello world ]====] +_scrap_0 = "another world" +_scrap_0 = 'what world' +_scrap_0 = "\nhello world\n" +_scrap_0 = 'yeah\nwhat is going on\nhere is something cool' +return nil \ No newline at end of file diff --git a/spec/outputs/local.lua b/spec/outputs/local.lua new file mode 100644 index 00000000..a186aa57 --- /dev/null +++ b/spec/outputs/local.lua @@ -0,0 +1,147 @@ +do + local a + local a, b, c + local g + b, g = 23232 +end +do + local x = 1212 + local something + something = function() + local x + x = 1212 + end +end +do + local y, z + y = 2323 + z = 2323 +end +do + print("Nothing Here!") +end +do + local X, Y + local x = 3434 + local y = 3434 + X = 3434 + Y = "yeah" +end +do + local x, y = "a", "b" +end +do + local x, y + x, y = "a", "b" +end +do + if something then + local x = 2323 + end +end +do + local x + do + x = "one" + end + x = 100 + do + x = "two" + end +end +do + local k, x, a, b, c + if what then + k = 10 + end + x = 100 + do + local _obj_0 = y + a, b, c = _obj_0.a, _obj_0.b, _obj_0.c + end +end +do + local a, b, c, d + a = 100 + print("hi") + b = 200 + c = 100 + print("hi") + d = 200 + d = 2323 +end +do + local Uppercase, One, Two + local lowercase = 5 + Uppercase = 3 + do + local _class_0 + local Five + local _base_0 = { } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function() end, + __base = _base_0, + __name = "One" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + local self = _class_0 + Five = 6 + One = _class_0 + end + do + local _class_0 + local No + local _base_0 = { } + _base_0.__index = _base_0 + _class_0 = setmetatable({ + __init = function() end, + __base = _base_0, + __name = "Two" + }, { + __index = _base_0, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_0) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_0.__class = _class_0 + local self = _class_0 + do + local _class_1 + local _base_1 = { } + _base_1.__index = _base_1 + _class_1 = setmetatable({ + __init = function() end, + __base = _base_1, + __name = "No" + }, { + __index = _base_1, + __call = function(cls, ...) + local _self_0 = setmetatable({}, _base_1) + cls.__init(_self_0, ...) + return _self_0 + end + }) + _base_1.__class = _class_1 + No = _class_1 + end + Two = _class_0 + end +end +do + local _list_0 = { } + for _index_0 = 1, #_list_0 do + local a = _list_0[_index_0] + local _scrap_0 = a + end +end +local g = 2323 \ No newline at end of file diff --git a/tests/outputs/loops.lua b/spec/outputs/loops.lua similarity index 50% rename from tests/outputs/loops.lua rename to spec/outputs/loops.lua index 18b70e6f..ca522638 100644 --- a/tests/outputs/loops.lua +++ b/spec/outputs/loops.lua @@ -38,7 +38,7 @@ end local x x = function() for x in y do - local _ = y + local _scrap_0 = y end end local hello = { @@ -48,72 +48,60 @@ local hello = { 4, 5 } -x = (function() +do local _accum_0 = { } - local _len_0 = 0 - local _list_1 = hello - for _index_0 = 1, #_list_1 do - local y = _list_1[_index_0] - local _value_0 + local _len_0 = 1 + for _index_0 = 1, #hello do + local y = hello[_index_0] if y % 2 == 0 then - _value_0 = y - end - if _value_0 ~= nil then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = _value_0 + _accum_0[_len_0] = y end + _len_0 = _len_0 + 1 end - return _accum_0 -end)() + x = _accum_0 +end x = function() - local _list_1 = hello - for _index_0 = 1, #_list_1 do - x = _list_1[_index_0] - local _ = y + for _index_0 = 1, #hello do + local x = hello[_index_0] + local _scrap_0 = y end end -local t = (function() +local t +do local _accum_0 = { } - local _len_0 = 0 + local _len_0 = 1 for i = 10, 20 do - local _value_0 = i * 2 - if _value_0 ~= nil then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = _value_0 - end + _accum_0[_len_0] = i * 2 + _len_0 = _len_0 + 1 end - return _accum_0 -end)() + t = _accum_0 +end local hmm = 0 -local y = (function() +local y +do local _accum_0 = { } - local _len_0 = 0 + local _len_0 = 1 for j = 3, 30, 8 do hmm = hmm + 1 local _value_0 = j * hmm - if _value_0 ~= nil then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = _value_0 - end + _accum_0[_len_0] = _value_0 + _len_0 = _len_0 + 1 end - return _accum_0 -end)() -local _ -_ = function() + y = _accum_0 +end +local _scrap_0 +_scrap_0 = function() for k = 10, 40 do - _ = "okay" + local _scrap_1 = "okay" end end -_ = function() +_scrap_0 = function() return (function() local _accum_0 = { } - local _len_0 = 0 + local _len_0 = 1 for k = 10, 40 do - local _value_0 = "okay" - if _value_0 ~= nil then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = _value_0 - end + _accum_0[_len_0] = "okay" + _len_0 = _len_0 + 1 end return _accum_0 end)() @@ -127,22 +115,44 @@ while 5 + 5 do end while also do i(work(too)) - _ = "okay" + local _scrap_1 = "okay" end local i = 0 -x = (function() +do local _accum_0 = { } - local _len_0 = 0 + local _len_0 = 1 while i < 10 do + local _value_0 i = i + 1 - local _value_0 = i - if _value_0 ~= nil then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = _value_0 - end + _accum_0[_len_0] = _value_0 + _len_0 = _len_0 + 1 + end + x = _accum_0 +end +do + local _accum_0 = { } + local _len_0 = 1 + local _list_1 = 3 + for _index_0 = 1, #_list_1 do + local thing = _list_1[_index_0] + local _value_0 + y = "hello" + _accum_0[_len_0] = _value_0 + _len_0 = _len_0 + 1 end - return _accum_0 -end)() + x = _accum_0 +end +do + local _accum_0 = { } + local _len_0 = 1 + for x = 1, 2 do + local _value_0 + y = "hello" + _accum_0[_len_0] = _value_0 + _len_0 = _len_0 + 1 + end + x = _accum_0 +end while true do local _continue_0 = false repeat @@ -175,9 +185,10 @@ for x = 1, 10 do break end end -local list = (function() +local list +do local _accum_0 = { } - local _len_0 = 0 + local _len_0 = 1 for x = 1, 10 do local _continue_0 = false repeat @@ -186,18 +197,16 @@ local list = (function() break end local _value_0 = x - if _value_0 ~= nil then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = _value_0 - end + _accum_0[_len_0] = _value_0 + _len_0 = _len_0 + 1 _continue_0 = true until true if not _continue_0 then break end end - return _accum_0 -end)() + list = _accum_0 +end local _list_1 = { 1, 2, @@ -250,4 +259,117 @@ for x = 1, 10 do if not _continue_0 then break end +end +while true do + local _continue_0 = false + repeat + do + if false then + _continue_0 = true + break + end + break + end + _continue_0 = true + until true + if not _continue_0 then + break + end +end +while true do + local _continue_0 = false + repeat + do + if false then + _continue_0 = true + break + end + return 22 + end + _continue_0 = true + until true + if not _continue_0 then + break + end +end +do + local xxx = { + 1, + 2, + 3, + 4 + } + for _index_0 = 1, #xxx do + local thing = xxx[_index_0] + print(thing) + end +end +while true do + local _continue_0 = false + repeat + do + print("hello") + _continue_0 = true + break + end + _continue_0 = true + until true + if not _continue_0 then + break + end +end +local values +do + local _accum_0 = { } + local _len_0 = 1 + local _list_2 = items + for _index_0 = 1, #_list_2 do + local _continue_0 = false + repeat + local x = _list_2[_index_0] + local _exp_0 = x + if "skip" == _exp_0 then + _continue_0 = true + break + else + _accum_0[_len_0] = x + end + _len_0 = _len_0 + 1 + _continue_0 = true + until true + if not _continue_0 then + break + end + end + values = _accum_0 +end +for _index_0 = 1, #x do + local _continue_0 = false + repeat + local a = x[_index_0] + for _index_1 = 1, #y do + local _continue_1 = false + repeat + local b = y[_index_1] + if b then + _continue_1 = true + break + end + print(b) + _continue_1 = true + until true + if not _continue_1 then + break + end + end + if a then + _continue_0 = true + break + end + print(a) + _continue_0 = true + until true + if not _continue_0 then + break + end end \ No newline at end of file diff --git a/spec/outputs/operators.lua b/spec/outputs/operators.lua new file mode 100644 index 00000000..9829ef57 --- /dev/null +++ b/spec/outputs/operators.lua @@ -0,0 +1,29 @@ +local x = 1 + 3 +local y = 1 + 3 +local z = 1 + 3 + 4 +local k = b and c and g +local h = thing and function() + return print("hello world") +end +local i = thing or function() + return print("hello world") +end +local p = thing and function() end +print("hello world") +local s = thing or function() end and 234 +local u = { + color = 1 and 2 and 3, + 4, + 4 +} +local v = { + color = 1 and function() + return "yeah" + end, + "great", + oksy = 3 ^ 2 +} +local nno = (yeah + 2) +local nn = (yeah + 2) +local n = hello(b)(function() end) +return hello(a, (yeah + 2) - okay) \ No newline at end of file diff --git a/spec/outputs/return.lua b/spec/outputs/return.lua new file mode 100644 index 00000000..5ce2e7e6 --- /dev/null +++ b/spec/outputs/return.lua @@ -0,0 +1,102 @@ +local _scrap_0 +_scrap_0 = function() + local _list_0 = things + for _index_0 = 1, #_list_0 do + local x = _list_0[_index_0] + local _scrap_1 = x + end +end +_scrap_0 = function() + local _accum_0 = { } + local _len_0 = 1 + local _list_0 = things + for _index_0 = 1, #_list_0 do + local x = _list_0[_index_0] + _accum_0[_len_0] = x + _len_0 = _len_0 + 1 + end + return _accum_0 +end +do + local _list_0 = things + for _index_0 = 1, #_list_0 do + local x = _list_0[_index_0] + return x + end +end +do + local _accum_0 = { } + local _len_0 = 1 + local _list_0 = things + for _index_0 = 1, #_list_0 do + local x = _list_0[_index_0] + _accum_0[_len_0] = x + _len_0 = _len_0 + 1 + end + return _accum_0 +end +do + local _tbl_0 = { } + local _list_0 = things + for _index_0 = 1, #_list_0 do + local x, y = _list_0[_index_0] + _tbl_0[x] = y + end + return _tbl_0 +end +_scrap_0 = function() + if a then + if a then + return a + else + return b + end + elseif b then + if a then + return a + else + return b + end + else + if a then + return a + else + return b + end + end +end +do + if a then + if a then + return a + else + return b + end + elseif b then + if a then + return a + else + return b + end + else + if a then + return a + else + return b + end + end +end +_scrap_0 = function() + local _base_0 = a + local _fn_0 = _base_0.b + return function(...) + return _fn_0(_base_0, ...) + end +end +do + local _base_0 = a + local _fn_0 = _base_0.b + return function(...) + return _fn_0(_base_0, ...) + end +end \ No newline at end of file diff --git a/tests/outputs/string.lua b/spec/outputs/string.lua similarity index 65% rename from tests/outputs/string.lua rename to spec/outputs/string.lua index 0d1f8d76..0eb1ff31 100644 --- a/tests/outputs/string.lua +++ b/spec/outputs/string.lua @@ -26,7 +26,7 @@ local f = [[hello #{world} world]] a = 'hello #{hello} hello' b = '#{hello} hello' c = 'hello #{hello}' -local _ = "hello" +local _scrap_0 = "hello"; ("hello"):format(1); ("hello"):format(1, 2, 3); ("hello"):format(1, 2, 3)(1, 2, 3); @@ -34,4 +34,12 @@ local _ = "hello" ("hello"):format().hello(1, 2, 3); ("hello"):format(1, 2, 3) something("hello"):world() -something(("hello"):world()) \ No newline at end of file +something(("hello"):world()) +x = 10 / (tostring(b) .. ".5") +local y = 1 + (tostring(n) .. "0") * 2 +local cmp = a == "v" .. tostring(b) +local joined = "a" .. "b" .. tostring(c) +local mixed = (tostring(a) .. "b") * 2 - ("c" .. tostring(d)) +local prefix = ("id: " .. tostring(id) .. ", ") .. rest +local data_url = ("data:image/" .. tostring(kind) .. ";base64,") .. encode_base64(image_bytes) +local line = line .. ((" " .. tostring(join) .. " ") .. el) \ No newline at end of file diff --git a/tests/outputs/stub.lua b/spec/outputs/stub.lua similarity index 73% rename from tests/outputs/stub.lua rename to spec/outputs/stub.lua index 1f02d09c..4ceac088 100644 --- a/tests/outputs/stub.lua +++ b/spec/outputs/stub.lua @@ -4,19 +4,20 @@ local x = { return print(self.val) end } -local fn = (function() +local fn +do local _base_0 = x local _fn_0 = _base_0.val - return function(...) + fn = function(...) return _fn_0(_base_0, ...) end -end)() +end print(fn()) print(x:val()) -x = (function(...) +do local _base_0 = hello(...) local _fn_0 = _base_0.world - return function(...) + x = function(...) return _fn_0(_base_0, ...) end -end)(...) \ No newline at end of file +end \ No newline at end of file diff --git a/tests/outputs/switch.lua b/spec/outputs/switch.lua similarity index 61% rename from tests/outputs/switch.lua rename to spec/outputs/switch.lua index 9b39d7bc..aa9389db 100644 --- a/tests/outputs/switch.lua +++ b/spec/outputs/switch.lua @@ -12,8 +12,8 @@ local _exp_2 = value if "cool" == _exp_2 then print("hello world") elseif "yeah" == _exp_2 then - local _ = [[FFFF]] + [[MMMM]] -elseif 2323 + 32434 == _exp_2 then + local _scrap_0 = [[FFFF]] + [[MMMM]] +elseif (2323 + 32434) == _exp_2 then print("okay") else print("okay rad") @@ -37,9 +37,9 @@ do local _with_0 = something local _exp_5 = _with_0:value() if _with_0.okay == _exp_5 then - local _ = "world" + local _scrap_0 = "world" else - local _ = "yesh" + local _scrap_0 = "yesh" end end fix(this) @@ -50,4 +50,22 @@ call_func((function() else return "no" end -end)()) \ No newline at end of file +end)()) +local _exp_5 = hi +if (hello or world) == _exp_5 then + local _scrap_0 = greene +end +local _exp_6 = hi +if "one" == _exp_6 or "two" == _exp_6 then + print("cool") +elseif "dad" == _exp_6 then + local _scrap_0 = no +end +local _exp_7 = hi +if (3 + 1) == _exp_7 or hello() == _exp_7 or (function() + return 4 +end)() == _exp_7 then + return yello +else + return print("cool") +end \ No newline at end of file diff --git a/tests/outputs/syntax.lua b/spec/outputs/syntax.lua similarity index 70% rename from tests/outputs/syntax.lua rename to spec/outputs/syntax.lua index 1fd64485..1c43742c 100644 --- a/tests/outputs/syntax.lua +++ b/spec/outputs/syntax.lua @@ -18,26 +18,26 @@ fun(a)(b, bad(hello)) hello(world(what(are(you(doing(here)))))) what(the)[3243](world, yeck(heck)) hairy[hands][are](gross)(okay(okay[world])) -local _ = (get[something] + 5)[years] +local _scrap_0 = (get[something] + 5)[years] local i, x = 200, 300 local yeah = (1 + 5) * 3 yeah = ((1 + 5) * 3) / 2 yeah = ((1 + 5) * 3) / 2 + i % 100 local whoa = (1 + 2) * (3 + 4) * (4 + 5) -_ = function() +_scrap_0 = function() if something then return 1, 2, 4 end return print("hello") end -_ = function() +_scrap_0 = function() if hello then return "heloo", "world" else return no, way end end -_ = function() +_scrap_0 = function() return 1, 2, 34 end return 5 + function() @@ -47,13 +47,21 @@ return 5 + (function() return 4 end) + 2 print(5 + function() - _ = 34 + local _scrap_1 = 34 return good(nads) end) something('else', "ya") something('else') something("else") -_ = here(we)("go")[12123] +_scrap_0 = something([[hey]]) * 2 +_scrap_0 = something([======[hey]======]) * 2 +_scrap_0 = something('else'), 2 +_scrap_0 = something("else"), 2 +_scrap_0 = something([[else]]), 2 +something('else', 2) +something("else", 2) +something([[else]], 2) +_scrap_0 = here(we)("go")[12123] local something = { test = 12323, what = function() @@ -91,10 +99,10 @@ x = -hello(world(one(two))) local hi = -"herfef" x = -(function() local _accum_0 = { } - local _len_0 = 0 + local _len_0 = 1 for x in x do - _len_0 = _len_0 + 1 _accum_0[_len_0] = x + _len_0 = _len_0 + 1 end return _accum_0 end)() @@ -107,14 +115,17 @@ end if not (1212 and 3434) then print("hello") end +for i = 1, 10 do + print("hello") +end print("nutjob") if hello then - _ = 343 + local _scrap_1 = 343 end if cool then print("what") else - _ = whack + local _scrap_1 = whack end local arg = { ... @@ -137,7 +148,7 @@ x = #{ 2 } } -_ = hello, world +_scrap_0 = hello, world something:hello(what)(a, b) something:hello(what) something.hello:world(a, b) @@ -149,14 +160,27 @@ y = y * 2 y = y / 100 local m = m % 2 local hello = hello .. "world" +self.__class.something = self.__class.something + 10 +self.something = self.something + 10 +self.__class["then"] = self.__class["then"] + 10 +self["then"] = self["then"] + 10 +local _update_0 = "hello" +a[_update_0] = a[_update_0] + 10 +local _update_1 = "hello" .. tostring(tostring(ff)) +a[_update_1] = a[_update_1] + 10 +local _update_2 = four +a[_update_2].x = a[_update_2].x + 10 +local count = count - (#datum + 1) +count = count - #datum +local total = total * (-x + 1) +local flag = flag and (not a or b) x = 0 local _list_0 = values for _index_0 = 1, #_list_0 do local v = _list_0[_index_0] - _ = ((function() + local _scrap_1 = ((function() if ntype(v) == "fndef" then x = x + 1 - return x end end)()) end @@ -167,11 +191,14 @@ hello = { ["function"] = "okay", good = 230203 } -_ = 5 + what(wack) +div({ + class = "cool" +}) +_scrap_0 = 5 + what(wack) what(whack + 5) -_ = 5 - what(wack) +_scrap_0 = 5 - what(wack) what(whack - 5) -x = hello - world - something +x = hello - world - something; (function(something) if something == nil then do @@ -183,16 +210,16 @@ x = hello - world - something return print(something) end)() if something then - _ = 03589 + local _scrap_1 = 03589 else - _ = 3434 + local _scrap_1 = 3434 end if something then - _ = yeah + local _scrap_1 = yeah elseif "ymmm" then print("cool") else - _ = okay + local _scrap_1 = okay end x = notsomething y = ifsomething @@ -226,6 +253,14 @@ another(hello, one, two, three, four, { a = a + (3 - 5) a = a * (3 + 5) a = a * 3 +a = a >> 3 +a = a << 3 a = a / func("cool") x["then"] = "hello" -x["while"]["true"] = "hello" \ No newline at end of file +x["while"]["true"] = "hello" +x = x or "hello" +x = x and "hello" +z = a - b +z = a(-b) +z = a - b +z = a - b \ No newline at end of file diff --git a/tests/outputs/tables.lua b/spec/outputs/tables.lua similarity index 59% rename from tests/outputs/tables.lua rename to spec/outputs/tables.lua index 7eabec1c..3d568b47 100644 --- a/tests/outputs/tables.lua +++ b/spec/outputs/tables.lua @@ -125,4 +125,82 @@ local xam = { hello = 1234, ["hello"] = 12354, ["hello"] = 12354 -} \ No newline at end of file +} +local kam = { + hello = 12, + goodcheese = "mmm", + yeah = 12 + 232, + lets = keepit({ + going = true + }, { + okay = "yeah" + }), + more = { + 1, + (function() + local _accum_0 = { } + local _len_0 = 1 + for x = 1, 10 do + _accum_0[_len_0] = x + _len_0 = _len_0 + 1 + end + return _accum_0 + end)() + }, + [{ + "one", + "two" + }] = one_thing(function(self) end) +} +keepit({ + going = true +}, { + okay = "yeah", + workd = "okay" +}) +thing({ + what = "great", + no = "more" +}, { + okay = 123 +}) +thing({ + what = "great", + no = "more" +}) +local _scrap_0 = { + okay = 123 +} +local k = { + ["hello"] = "world" +} +k = { + ['hello'] = 'world' +} +k = { + ["hello"] = 'world', + ["hat"] = "zat" +} +please({ + ["hello"] = "world" +}) +k = { + ["hello"] = "world", + ["one"] = "zone" +} +local f = "one", { + ["two"] = three +}, "four" +f = { + ["two"] = three +}, "four" +f = { + "one", + ["two"] = three, + "four" +} +local j = "one", { + ["two"] = three, + ["four"] = five +}, 6, 7 +return nil \ No newline at end of file diff --git a/spec/outputs/unless_else.lua b/spec/outputs/unless_else.lua new file mode 100644 index 00000000..f05e7517 --- /dev/null +++ b/spec/outputs/unless_else.lua @@ -0,0 +1,7 @@ +if a then + if not (b) then + return print("hi") + elseif c then + return print("not hi") + end +end \ No newline at end of file diff --git a/tests/outputs/using.lua b/spec/outputs/using.lua similarity index 66% rename from tests/outputs/using.lua rename to spec/outputs/using.lua index 2ab41016..c99d4bb9 100644 --- a/tests/outputs/using.lua +++ b/spec/outputs/using.lua @@ -1,18 +1,18 @@ local hello = "hello" local world = "world" -local _ -_ = function() +local _scrap_0 +_scrap_0 = function() local hello = 3223 end -_ = function(a) +_scrap_0 = function(a) local hello = 3223 a = 323 end -_ = function(a, b, c) +_scrap_0 = function(a, b, c) a, b, c = 1, 2, 3 local world = 12321 end -_ = function(a, e, f) +return function(a, e, f) local b, c a, b, c = 1, 2, 3 hello = 12321 diff --git a/tests/outputs/whitespace.lua b/spec/outputs/whitespace.lua similarity index 72% rename from tests/outputs/whitespace.lua rename to spec/outputs/whitespace.lua index bcc868b0..27e33347 100644 --- a/tests/outputs/whitespace.lua +++ b/spec/outputs/whitespace.lua @@ -1,30 +1,30 @@ -local _ = { +local _scrap_0 = { 1, 2 } -_ = { +_scrap_0 = { 1, 2 } -_ = { +_scrap_0 = { 1, 2 } -_ = { +_scrap_0 = { 1, 2 } -_ = { +_scrap_0 = { 1, 2 } -_ = { +_scrap_0 = { something(1, 2, 4, 5, 6), 3, 4, 5 } -_ = { +_scrap_0 = { a(1, 2, 3), 4, 5, @@ -33,7 +33,7 @@ _ = { 2, 3 } -_ = { +_scrap_0 = { b(1, 2, 3, 4, 5, 6), 1, 2, @@ -42,19 +42,19 @@ _ = { 2, 3 } -_ = { +_scrap_0 = { 1, 2, 3 } -_ = { +_scrap_0 = { c(1, 2, 3) } hello(1, 2, 3, 4, 1, 2, 3, 4, 4, 5) x(1, 2, 3, 4, 5, 6) hello(1, 2, 3, world(4, 5, 6, 5, 6, 7, 8)) hello(1, 2, 3, world(4, 5, 6, 5, 6, 7, 8), 9, 9) -_ = { +_scrap_0 = { hello(1, 2), 3, 4, @@ -73,4 +73,8 @@ if hello(1, 2, 3, world, world) then end if hello(1, 2, 3, world, world) then print("hello") -end \ No newline at end of file +end +a(one, two, three) +b(one, two, three) +c(one, two, three, four) +return nil \ No newline at end of file diff --git a/spec/outputs/with.lua b/spec/outputs/with.lua new file mode 100644 index 00000000..758e2d54 --- /dev/null +++ b/spec/outputs/with.lua @@ -0,0 +1,193 @@ +do + local a + a = function() + do + local _with_0 = something + print(_with_0.hello) + print(hi) + print("world") + return _with_0 + end + end +end +do + do + local _with_0 = leaf + _with_0.world() + _with_0.world(1, 2, 3) + local g = _with_0.what.is.this + _with_0.hi(1, 2, 3) + _with_0:hi(1, 2).world(2323) + _with_0:hi("yeah", "man") + _with_0.world = 200 + end +end +do + local zyzyzy + do + local _with_0 = something + _with_0.set_state("hello world") + zyzyzy = _with_0 + end +end +do + local x = 5 + (function() + do + local _with_0 = Something() + _with_0:write("hello world") + return _with_0 + end + end)() +end +do + local x = { + hello = (function() + do + local _with_0 = yeah + _with_0:okay() + return _with_0 + end + end)() + } +end +do + do + local _with_0 = foo + local _scrap_0 = _with_0:prop("something").hello + _with_0.prop:send(one) + _with_0.prop:send(one) + end +end +do + do + local _with_0 = a, b + print(_with_0.world) + end + local mod + do + local _M = { } + _M.Thing = "hi" + mod = _M + end + do + local a, b = something, pooh + print(a.world) + end + local x + do + local a, b = 1, 2 + print(a + b) + x = a + end + print((function() + do + local a, b = 1, 2 + print(a + b) + return a + end + end)()) + local p + do + local _with_0 = 1 + hello().x, world().y = _with_0, 2 + print(a + b) + p = _with_0 + end +end +do + local x = "hello" + do + x:upper() + end +end +do + do + local k = "jo" + print(k:upper()) + end +end +do + do + local a, b, c = "", "", "" + print(a:upper()) + end +end +do + local a = "bunk" + do + local b, c + a, b, c = "", "", "" + print(a:upper()) + end +end +do + do + local _with_0 = j + print(_with_0:upper()) + end +end +do + do + local _with_0 = "jo" + k.j = _with_0 + print(_with_0:upper()) + end +end +do + do + local _with_0 = a + print(_with_0.b) + do + local _with_1 = _with_0.c + print(_with_1.d) + end + end +end +do + do + local _with_0 = a + do + local _with_1 = 2 + _with_0.b = _with_1 + print(_with_1.c) + end + end +end +do + local _scrap_0 + _scrap_0 = function() + do + local _with_0 = hi + return _with_0.a, _with_0.b + end + end +end +do + do + local _with_0 = dad + _with_0["if"]("yes") + local y = _with_0["end"].of["function"] + end +end +do + do + local _with_0 = obj + local bound + do + local _base_0 = _with_0 + local _fn_0 = _base_0.method + bound = function(...) + return _fn_0(_base_0, ...) + end + end + local keyword_bound + do + local _base_0 = _with_0 + local _fn_0 = _base_0["function"] + keyword_bound = function(...) + return _fn_0(_base_0, ...) + end + end + return _with_0 + end +end \ No newline at end of file diff --git a/spec/parser_spec.moon b/spec/parser_spec.moon new file mode 100644 index 00000000..a1dc0dac --- /dev/null +++ b/spec/parser_spec.moon @@ -0,0 +1,151 @@ +-- Unit tests for the parser. Expected trees are written out inline, +-- including [-1] position annotations. The lang_spec corpus covers whole +-- files from parse to compiled output; these are quick hand-checked cases. + +import with_dev from require "spec.helpers" + +describe "moonscript.parse", -> + local parse + + with_dev -> + parse = require "moonscript.parse" + + it "parses assignment", -> + assert.same { + {"assign" + {{"ref", "x", [-1]: 1}} + {{"number", "5", [-1]: 4}} + [-1]: 1} + }, parse.string "x = 5" + + it "parses an open call", -> + assert.same { + {"chain" + {"ref", "print", [-1]: 1} + {"call", {{"ref", "x", [-1]: 6}}} + [-1]: 1} + }, parse.string "print x" + + it "parses table with self-assign and string key", -> + assert.same { + {"assign" + {{"ref", "t", [-1]: 1}} + {{"table", { + {{"key_literal", "name"}, {"ref", "name", [-1]: 11}} + {{"string", '"', "k"}, {"ref", "v", [-1]: 17}} + }, [-1]: 4}} + [-1]: 1} + }, parse.string [[t = {:name, "k": v}]] + + it "parses a function literal with binary op body", -> + assert.same { + {"assign" + {{"ref", "f", [-1]: 1}} + {{"fndef", {{"a"}}, {}, "slim", { + {"exp", {"ref", "a", [-1]: 11}, "+", {"number", "1", [-1]: 15}, [-1]: 11} + }, [-1]: 4}} + [-1]: 1} + }, parse.string "f = (a) -> a + 1" + + it "parses an indented block", -> + assert.same { + {"if", {"ref", "x", [-1]: 3}, { + {"chain", {"ref", "y", [-1]: 8}, {"call", {}}, [-1]: 8} + }, [-1]: 1} + }, parse.string "if x\n y!" + + it "parses lua strings, reconstructing the open delimiter", -> + assert.same { + {"assign" + {{"ref", "x", [-1]: 1}} + {{"string", "[==[", "str", [-1]: 4}} + [-1]: 1} + }, parse.string "x = [==[str]==]" + + it "parses an anonymous class with a nil name slot", -> + tree = assert parse.string "x = class" + class_node = tree[1][3][1] + assert.same "class", class_node[1] + assert.is_nil class_node[2] + assert.same "", class_node[3] + assert.same {}, class_node[4] + + it "parses an empty file", -> + assert.same {}, parse.string "" + + it "parses a comment-only file", -> + assert.same {}, parse.string "-- nothing here" + + it "fails on unbalanced parens", -> + tree, err = parse.string "x = (a + b" + assert.is_nil tree + assert.is_string err + + it "fails on a bad outdent", -> + tree = parse.string "if x\n y\n z" + assert.is_nil tree + + it "rejects a non-assignable left hand side", -> + tree, err = parse.string "f! = 5" + assert.is_nil tree + assert.matches "not assignable", err + assert.matches "line 1", err + + it "reports a line position for parse failures", -> + tree, err = parse.string "x = 1\ny = 2\nz = (a +" + assert.is_nil tree + assert.matches "line 3", err + + tree, err = parse.string 'x = "hello\ny = 1' + assert.is_nil tree + assert.matches "line 2", err + + tree, err = parse.string "import a from" + assert.is_nil tree + assert.matches "line 1", err + + -- regression: T() labels were tried in this grammar and removed. Value + -- tries Comprehension before String, so at "[[" the parser attempts to + -- parse string content as code; a label reached during such an attempt + -- rejected valid programs like these. + it "parses code-like content inside long strings", -> + assert.is_table parse.string "y = [[if x then import a]] .. z" + assert.is_table parse.string "x = [[=[hi]] .. 1" + assert.is_table parse.string [=[x = y .. [[Flow:extend("]] .. z .. [[")]] .. w]=] + + it "keeps the parser reusable after a failed parse", -> + tree = parse.string "x = (" + assert.is_nil tree + + assert.same { + {"assign" + {{"ref", "x", [-1]: 1}} + {{"number", "1", [-1]: 4}} + [-1]: 1} + }, parse.string "x = 1" + + it "returns nil, err instead of raising on the recursion depth limit", -> + str = ("(")\rep 6000 + tree, err = parse.string str + assert.is_nil tree + assert.matches "max recursion depth", err + + it "disables do expressions inside loop headers", -> + -- `do` after the while condition is the block keyword, not a do-expression + tree = assert parse.string "while x do print 1" + assert.same "while", tree[1][1] + + -- but a do-expression is fine in normal expression position + tree = assert parse.string "x = do\n 5" + assert.same "do", tree[1][3][1][1] + + it "handles interpolation containing a lua string", -> + tree = assert parse.string [=[x = "a#{ [[long]] }b"]=] + str = tree[1][3][1] + assert.same "string", str[1] + assert.same "a", str[3] + assert.same "interpolate", str[4][1] + assert.same "string", str[4][2][1] + assert.same "[[", str[4][2][2] + assert.same "long", str[4][2][3] + assert.same "b", str[5] diff --git a/spec/transform_spec.moon b/spec/transform_spec.moon new file mode 100644 index 00000000..c62cd7a2 --- /dev/null +++ b/spec/transform_spec.moon @@ -0,0 +1,300 @@ + +import with_dev from require "spec.helpers" + + +describe "moonscript.transform.destructure", -> + local extract_assign_names, split_assign, Block + + with_dev -> + { :extract_assign_names, :split_assign } = require "moonscript.transform.destructure" + {:Block} = require "moonscript.compile" + + describe "split_assign #fff", -> + -- {:hello} = world + it "simple assignment", -> + node = { + "assign" + { + { "table", { + {{"key_literal", "hello"}, {"ref", "hello"}} + } + } + } + { + {"ref", "world"} + } + } + + out = split_assign Block!, node + + assert.same { "group", { + { "group", { + { "declare", { {"ref", "hello"} } } + { "assign", { {"ref", "hello"} }, { {"chain", {"ref", "world"}, {"dot", "hello"}} } } + }} + }}, out + + -- {:a, :b} = world! + -- a complex value should never be repeated to avoid double execution + it "complex value", -> + node = { + "assign" + { + { "table", { + {{"key_literal", "a"}, {"ref", "a"}} + {{"key_literal", "b"}, {"ref", "b"}} + } + } + } + { + {"chain", {"ref", "world"}, {"call", {}}} + } + } + + out = split_assign Block!, node + + -- the temp name the result is stored into + tmp = {"temp_name", prefix: "obj"} + + assert.same { "group", { + { "group", { + { "declare", { {"ref", "a"}, {"ref", "b"} } } + + { "do", { + {"assign", { tmp }, { {"chain", {"ref", "world"}, {"call", {}}} } } + {"assign", { {"ref", "a"}, {"ref", "b"} }, { {"chain", tmp, {"dot", "a"}}, {"chain", tmp, {"dot", "b"}} } } + }} + }} + }}, out + + -- a, {:hello} = one, two + it "multiple assigns", -> + node = { + "assign" + { + {"ref", "a"} + { "table", { + {{"key_literal", "hello"}, {"ref", "hello"}} + } + } + } + { + {"ref", "one"} + {"ref", "two"} + } + } + + out = split_assign Block!, node + + assert.same { "group", { + {"assign", { {"ref", "a"} }, { {"ref", "one"} }} + + { "group", { + { "declare", { {"ref", "hello"} } } + { "assign", { {"ref", "hello"} }, { {"chain", {"ref", "two"}, {"dot", "hello"}} } } + }} + }}, out + + -- {:hello}, a = one, two + it "multiple assigns swapped", -> + node = { + "assign" + { + { "table", { + {{"key_literal", "hello"}, {"ref", "hello"}} + } + } + {"ref", "a"} + } + { + {"ref", "one"} + {"ref", "two"} + } + } + + out = split_assign Block!, node + + assert.same { "group", { + { "group", { + { "declare", { {"ref", "hello"} } } + { "assign", { {"ref", "hello"} }, { {"chain", {"ref", "one"}, {"dot", "hello"}} } } + }} + + {"assign", { {"ref", "a"} }, { {"ref", "two"} }} + }}, out + + -- a, {:hello} = func! -- where func returns multiple values, second destructured + it "multiple return value", -> + node = { + "assign" + { + {"ref", "a"} + { "table", { + {{"key_literal", "hello"}, {"ref", "hello"}} + } + } + } + { + {"chain", {"ref", "func"}, {"call", {}}} + } + } + + out = split_assign Block!, node + + assert.same "group", out[1] + assert.same 2, #out[2] + + assign = out[2][1] + assert.same "assign", assign[1] + assert.same {"ref", "a"}, assign[2][1] + tmp = assign[2][2] + assert.same "temp_name", tmp[1] + assert.same { {"chain", {"ref", "func"}, {"call", {}}} }, assign[3] + + destruct_group = out[2][2] + assert.same "group", destruct_group[1] + declare = destruct_group[2][1] + assert.same { "declare", { {"ref", "hello"} } }, declare + + destruct_assign = destruct_group[2][2] + assert.same "assign", destruct_assign[1] + assert.same { {"ref", "hello"} }, destruct_assign[2] + + destruct_value = destruct_assign[3][1] + assert.same "chain", destruct_value[1] + assert.is_true tmp == destruct_value[2] + assert.same "dot", destruct_value[3][1] + assert.same "hello", destruct_value[3][2] + + + it "extracts names from table destructure", -> + des = { + "table" + { + {{"key_literal", "hi"}, {"ref", "hi"}} + {{"key_literal", "world"}, {"ref", "world"}} + } + } + + assert.same { + { + {"ref", "hi"} -- target + { + {"dot", "hi"} + } -- chain suffix + } + + { + {"ref", "world"} + { + {"dot", "world"} + } + } + + }, extract_assign_names des + + it "extracts names from array destructure", -> + des = { + "table" + { + {{"ref", "hi"}} + } + } + + assert.same { + { + {"ref", "hi"} + { + {"index", {"number", 1}} + } + } + }, extract_assign_names des + +describe "moonscript.transform.statements", -> + local last_stm, transform_last_stm, Run + + with_dev -> + { :last_stm, :transform_last_stm, :Run } = require "moonscript.transform.statements" + + describe "last_stm", -> + it "gets last statement from empty list", -> + assert.same nil, (last_stm {}) + + it "gets last statement", -> + stms = { + {"ref", "butt_world"} + {"ref", "hello_world"} + } + + stm, idx, t = last_stm stms + assert stms[2] == stm + assert.same 2, idx + assert stms == t + + it "gets last statement ignoring run", -> + stms = { + {"ref", "butt_world"} + {"ref", "hello_world"} + Run => print "hi" + } + + stm, idx, t = last_stm stms + assert stms[2] == stm + assert.same 2, idx + assert stms == t + + it "gets last from within group", -> + stms = { + {"ref", "butt_world"} + {"group", { + {"ref", "hello_world"} + {"ref", "cool_world"} + }} + } + + last = stms[2][2][2] + + stm, idx, t = last_stm stms + assert stm == last, "should get last" + assert.same 2, idx + assert t == stms[2][2], "should get correct table" + + describe "transform_last_stm", -> + + it "transforms empty stms", -> + before = {} + after = transform_last_stm before, (n) -> {"wrapped", n} + + assert.same before, after + assert before != after + + it "transforms stms", -> + before = { + {"ref", "butt_world"} + {"ref", "hello_world"} + } + + transformer = (n) -> n + after = transform_last_stm before, transformer + + assert.same { + {"ref", "butt_world"} + {"transform", {"ref", "hello_world"}, transformer} + }, after + + it "transforms empty stms ignoring runs", -> + before = { + {"ref", "butt_world"} + {"ref", "hello_world"} + Run => print "hi" + } + + transformer = (n) -> n + after = transform_last_stm before, transformer + + assert.same { + {"ref", "butt_world"} + {"transform", {"ref", "hello_world"}, transformer} + before[3] + }, after + diff --git a/spec/use_slow_parser.moon b/spec/use_slow_parser.moon new file mode 100644 index 00000000..a6e54670 --- /dev/null +++ b/spec/use_slow_parser.moon @@ -0,0 +1,7 @@ +-- busted helper (busted --helper=spec/use_slow_parser.moon) that runs the +-- suite against the pure Lua parser in place of the native C module. busted +-- loads moonscript, and with it the native parser, before helpers run, so +-- the package.loaded entry must be replaced as well +slow = dofile "moonscript/parse/slow.lua" +package.loaded["moonscript.parse.native"] = slow +package.preload["moonscript.parse.native"] = -> slow diff --git a/test.lua b/test.lua deleted file mode 100755 index 073a4503..00000000 --- a/test.lua +++ /dev/null @@ -1,193 +0,0 @@ -#!/usr/bin/env lua - -require "lfs" -require "alt_getopt" - -local gettime = nil - -pcall(function() - require "socket" - gettime = socket.gettime -end) - -require "moonscript.parse" -require "moonscript.compile" -local parse, compile = moonscript.parse, moonscript.compile - -local opts, ind = alt_getopt.get_opts(arg, "qd:", { }) - -local argv = {} -for i = ind, #arg do table.insert(argv, arg[i]) end - -local action = table.remove(argv, 1) or "run" - -local diff_tool = opts.d or "diff" -local quiet = opts.q - -local opts = { - in_dir = "tests/inputs", - out_dir = "tests/outputs", - input_pattern = "(.*)%.moon$", - output_ext = ".lua" -} - -local total_time = { - parse = 0, - compile = 0 -} - -local function format_time(sec) - return ("%.3fms"):format(sec*1000) -end - -local function diff(a_fname, b_fname) - return io.popen(diff_tool.." ".. a_fname.." "..b_fname, "r"):read("*a") -end - -local function input_name(name) return opts.in_dir.."/".. name end -local function output_name(name) - return opts.out_dir.."/"..name:match(opts.input_pattern)..opts.output_ext -end - -local function run_file(name, benchmark) - name = input_name(name) - file_str = io.open(name):read("*a") - - local start_parse - if benchmark then start_parse = gettime() end - - local tree, err = parse.string(file_str) - - local parse_time = 0 - if benchmark then parse_time = gettime() - start_parse end - - if not tree then - error("Parse error in "..name.."\n"..err) - end - - local start_compile - if benchmark then start_compile = gettime() end - - local code, err, pos = compile.tree(tree) - if not code then - print() - print(("Failed to compile: %s"):format(name)) - print(compile.format_error(err, pos, file_str)) - os.exit() - end - - -- local success, code = pcall(compile.tree, tree) - -- if not success then - -- error("Compile error in"..name..":\n"..code) - -- end - - if benchmark then - local compile_time = gettime() - start_compile - return code, parse_time, compile_time - end - - return code -end - -local function inputs(pattern) - return coroutine.wrap(function() - for file in lfs.dir(opts.in_dir) do - local body = file:match(opts.input_pattern) - if body then - if not pattern or body:match(pattern) then - coroutine.yield(file) - end - end - end - end) -end - -local actions = { - build = function(pattern) - for file in inputs(pattern) do - local out_fname = output_name(file) - print("Building: ", file, out_fname) - local result = run_file(file) - if result then - io.open(out_fname, "w"):write(result) - end - end - end, - run = function(pattern) - local failed = false - local tests_run, result = 0 - for file in inputs(pattern) do - tests_run = tests_run + 1 - local correct_fname = output_name(file) - result, parse_time, compile_time = run_file(file, gettime) - local handle = io.open(correct_fname) - - if not handle then - print("Test not built yet:", correct_fname) - else - local correct = handle:read("*a") - if result ~= correct then - print("Test", file, "failed") - local tmp_name = os.tmpname() - local tmp = io.open(tmp_name, "w") - tmp:write(result) - tmp:close() - - if not quiet then - print(diff(correct_fname, tmp_name)) - end - os.remove(tmp_name) - -- break - else - if parse_time then - total_time.parse = total_time.parse + parse_time - total_time.compile = total_time.compile + compile_time - - parse_time = format_time(parse_time) - compile_time = format_time(compile_time) - print("Test", file, "passed", "", - ("p: %s, c: %s"):format(parse_time, compile_time)) - else - print("Test", file, "passed") - end - end - end - end - - if gettime then - print"" - print"total:" - print(" parse time", format_time(total_time.parse)) - print(" compile time", format_time(total_time.compile)) - end - - - if tests_run == 0 then - if not pattern then - print("No tests found") - else - print("No tests matching pattern:", pattern) - end - elseif tests_run == 1 then - -- print(result) - end - end, - list = function(pattern) - local count = 0 - for file in inputs(pattern) do - count = count + 1 - print(file) - end - if count > 0 then print("") end - print("Count:", count) - end -} - -local fn = actions[action] -if fn then - fn(unpack(argv)) -else - print("Unknown action:", action) -end - - diff --git a/tests/inputs/comprehension.moon b/tests/inputs/comprehension.moon deleted file mode 100644 index 6822c844..00000000 --- a/tests/inputs/comprehension.moon +++ /dev/null @@ -1,19 +0,0 @@ - --- see lists.moon for list comprehension tests - -items = {1,2,3,4,5,6} -out = {k,k*2 for k in items} - - -x = hello: "world", okay: 2323 - -copy = {k,v for k,v in pairs x when k != "okay"} - --- - -{ unpack(x) for x in yes } -{ unpack(x) for x in *yes } - -{ xxxx for x in yes } -{ unpack [a*i for i, a in ipairs x] for x in *{{1,2}, {3,4}} } - diff --git a/tests/inputs/export.moon b/tests/inputs/export.moon deleted file mode 100644 index 8889ceab..00000000 --- a/tests/inputs/export.moon +++ /dev/null @@ -1,44 +0,0 @@ - -export a,b,c = 223, 343 -export cool = "dad" - -export class Something - umm: "cool" - -What = if this - 232 -else - 4343 - -export a,b,c - -a,b,c,d = "hello" - -export ^ - -another = 3434 -Another = 7890 - -if inner then Yeah = "10000" - -What = if this - 232 -else - 4343 - -export * - -What = if this - 232 -else - 4343 - -x,y,z = 1,2,3 - -y = -> - hallo = 3434 - -with tmp - j = 2000 - - diff --git a/tests/inputs/funcs.moon b/tests/inputs/funcs.moon deleted file mode 100644 index 8c17439b..00000000 --- a/tests/inputs/funcs.moon +++ /dev/null @@ -1,61 +0,0 @@ - - -x = -> print what - --> - --> -> -> - -go to the barn - -open -> the -> door - -open -> - the door - hello = -> - my func - -h = -> hi - -eat ->, world - - -(->)() - -x = (...) -> - -hello! -hello.world! - -hello!.something -what!["ofefe"] - -what! the! heck! - -(a,b,c,d,e) -> - -(a,a,a,a,a) -> - print a - -(x=23023) -> - -(x=(y=()->) ->) -> - -(x = if something then yeah else no) -> - -something = (hello=100, world=(x=[[yeah cool]])-> print "eat rice") -> - print hello - -(x, y) => -(@x, @y) => -(x=1) => -(@x=1,y,@z="hello world") => - - -x -> return -y -> return 1 -z -> return 1, "hello", "world" -k -> if yes then return else return - --> real_name if something - diff --git a/tests/inputs/import.moon b/tests/inputs/import.moon deleted file mode 100644 index dac41017..00000000 --- a/tests/inputs/import.moon +++ /dev/null @@ -1,22 +0,0 @@ - - -import hello from yeah -import hello, world from table["cool"] - -import a, \b, c from items - - -import master, \ghost from find "mytable" - - -a, yumm = 3434, "hello" - - -_table_0 = 232 - -import something from a table - - -if indent - import okay, \well from tables[100] - diff --git a/tests/inputs/local.moon b/tests/inputs/local.moon deleted file mode 100644 index 8f30e865..00000000 --- a/tests/inputs/local.moon +++ /dev/null @@ -1,11 +0,0 @@ - -local a -local a,b,c - - -x = 1212 -something = -> - local x - x = 1212 - - diff --git a/tests/inputs/with.moon b/tests/inputs/with.moon deleted file mode 100644 index e9b35acd..00000000 --- a/tests/inputs/with.moon +++ /dev/null @@ -1,38 +0,0 @@ - -a = -> - with something - print .hello - print hi - print "world" - -with leaf - .world! - .world 1,2,3 - - g = .what.is.this - - .hi 1,2,3 - - \hi(1,2).world 2323 - - \hi "yeah", "man" - .world = 200 - -zyzyzy = with something - .set_state "hello world" - - -x = 5 + with Something! - \write "hello world" - - -x = { - hello: with yeah - \okay! -} - -with foo - \prop"something".hello - .prop\send(one) - .prop\send one - diff --git a/tests/outputs/bubbling.lua b/tests/outputs/bubbling.lua deleted file mode 100644 index d657beaa..00000000 --- a/tests/outputs/bubbling.lua +++ /dev/null @@ -1,131 +0,0 @@ -local f -f = function(...) - return #{ - ... - } -end -local dont_bubble -dont_bubble = function() - return (function() - local _accum_0 = { } - local _len_0 = 0 - for x in (function(...) - return print(...) - end)("hello") do - _len_0 = _len_0 + 1 - _accum_0[_len_0] = x - end - return _accum_0 - end)() -end -local k = (function() - local _accum_0 = { } - local _len_0 = 0 - for x in (function(...) - return print(...) - end)("hello") do - _len_0 = _len_0 + 1 - _accum_0[_len_0] = x - end - return _accum_0 -end)() -local j = (function() - local _accum_0 = { } - local _len_0 = 0 - for i = 1, 10 do - local _value_0 - _value_0 = function(...) - return print(...) - end - if _value_0 ~= nil then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = _value_0 - end - end - return _accum_0 -end)() -local m -m = function(...) - return (function(...) - local _accum_0 = { } - local _len_0 = 0 - local _list_0 = { - ... - } - for _index_0 = 1, #_list_0 do - local x = _list_0[_index_0] - if f(...) > 4 then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = x - end - end - return _accum_0 - end)(...) -end -local x = (function(...) - local _accum_0 = { } - local _len_0 = 0 - local _list_0 = { - ... - } - for _index_0 = 1, #_list_0 do - local i = _list_0[_index_0] - local _value_0 = i - if _value_0 ~= nil then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = _value_0 - end - end - return _accum_0 -end)(...) -local y = (function(...) - local _accum_0 = { } - local _len_0 = 0 - local _list_0 = { - ... - } - for _index_0 = 1, #_list_0 do - x = _list_0[_index_0] - _len_0 = _len_0 + 1 - _accum_0[_len_0] = x - end - return _accum_0 -end)(...) -local z = (function(...) - local _accum_0 = { } - local _len_0 = 0 - for x in hallo do - if f(...) > 4 then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = x - end - end - return _accum_0 -end)(...) -local a = (function(...) - local _accum_0 = { } - local _len_0 = 0 - for i = 1, 10 do - local _value_0 = ... - if _value_0 ~= nil then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = _value_0 - end - end - return _accum_0 -end)(...) -local b = (function() - local _accum_0 = { } - local _len_0 = 0 - for i = 1, 10 do - local _value_0 - _value_0 = function() - return print(...) - end - if _value_0 ~= nil then - _len_0 = _len_0 + 1 - _accum_0[_len_0] = _value_0 - end - end - return _accum_0 -end)() \ No newline at end of file diff --git a/tests/outputs/class.lua b/tests/outputs/class.lua deleted file mode 100644 index 1f1aa4fd..00000000 --- a/tests/outputs/class.lua +++ /dev/null @@ -1,592 +0,0 @@ -local Hello -Hello = (function() - local _parent_0 = nil - local _base_0 = { - hello = function(self) - return print(self.test, self.world) - end, - __tostring = function(self) - return "hello world" - end - } - _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self, test, world) - self.test, self.world = test, world - return print("creating object..") - end, - __base = _base_0, - __name = "Hello", - __parent = _parent_0 - }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, - __call = function(cls, ...) - local _self_0 = setmetatable({}, _base_0) - cls.__init(_self_0, ...) - return _self_0 - end - }) - _base_0.__class = _class_0 - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -local x = Hello(1, 2) -x:hello() -print(x) -local Simple -Simple = (function() - local _parent_0 = nil - local _base_0 = { - cool = function(self) - return print("cool") - end - } - _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self, ...) - if _parent_0 then - return _parent_0.__init(self, ...) - end - end, - __base = _base_0, - __name = "Simple", - __parent = _parent_0 - }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, - __call = function(cls, ...) - local _self_0 = setmetatable({}, _base_0) - cls.__init(_self_0, ...) - return _self_0 - end - }) - _base_0.__class = _class_0 - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -local Yikes -Yikes = (function() - local _parent_0 = Simple - local _base_0 = { } - _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self) - return print("created hello") - end, - __base = _base_0, - __name = "Yikes", - __parent = _parent_0 - }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, - __call = function(cls, ...) - local _self_0 = setmetatable({}, _base_0) - cls.__init(_self_0, ...) - return _self_0 - end - }) - _base_0.__class = _class_0 - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -x = Yikes() -x:cool() -local Hi -Hi = (function() - local _parent_0 = nil - local _base_0 = { - cool = function(self, num) - return print("num", num) - end - } - _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self, arg) - return print("init arg", arg) - end, - __base = _base_0, - __name = "Hi", - __parent = _parent_0 - }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, - __call = function(cls, ...) - local _self_0 = setmetatable({}, _base_0) - cls.__init(_self_0, ...) - return _self_0 - end - }) - _base_0.__class = _class_0 - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -Simple = (function() - local _parent_0 = Hi - local _base_0 = { - cool = function(self) - return _parent_0.cool(self, 120302) - end - } - _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self) - return _parent_0.__init(self, "man") - end, - __base = _base_0, - __name = "Simple", - __parent = _parent_0 - }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, - __call = function(cls, ...) - local _self_0 = setmetatable({}, _base_0) - cls.__init(_self_0, ...) - return _self_0 - end - }) - _base_0.__class = _class_0 - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -x = Simple() -x:cool() -print(x.__class == Simple) -local Okay -Okay = (function() - local _parent_0 = nil - local _base_0 = { - something = 20323 - } - _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self, ...) - if _parent_0 then - return _parent_0.__init(self, ...) - end - end, - __base = _base_0, - __name = "Okay", - __parent = _parent_0 - }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, - __call = function(cls, ...) - local _self_0 = setmetatable({}, _base_0) - cls.__init(_self_0, ...) - return _self_0 - end - }) - _base_0.__class = _class_0 - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -local Biggie -Biggie = (function() - local _parent_0 = Okay - local _base_0 = { - something = function(self) - _parent_0.something(self, 1, 2, 3, 4) - _parent_0.something(another_self, 1, 2, 3, 4) - return assert(_parent_0 == Okay) - end - } - _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self, ...) - if _parent_0 then - return _parent_0.__init(self, ...) - end - end, - __base = _base_0, - __name = "Biggie", - __parent = _parent_0 - }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, - __call = function(cls, ...) - local _self_0 = setmetatable({}, _base_0) - cls.__init(_self_0, ...) - return _self_0 - end - }) - _base_0.__class = _class_0 - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -local Yeah -Yeah = (function() - local _parent_0 = nil - local _base_0 = { - okay = function(self) - return _parent_0.something(self, 1, 2, 3, 4) - end - } - _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self, ...) - if _parent_0 then - return _parent_0.__init(self, ...) - end - end, - __base = _base_0, - __name = "Yeah", - __parent = _parent_0 - }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, - __call = function(cls, ...) - local _self_0 = setmetatable({}, _base_0) - cls.__init(_self_0, ...) - return _self_0 - end - }) - _base_0.__class = _class_0 - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -local What -What = (function() - local _parent_0 = nil - local _base_0 = { - something = function(self) - return print("val:", self.val) - end - } - _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self, ...) - if _parent_0 then - return _parent_0.__init(self, ...) - end - end, - __base = _base_0, - __name = "What", - __parent = _parent_0 - }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, - __call = function(cls, ...) - local _self_0 = setmetatable({}, _base_0) - cls.__init(_self_0, ...) - return _self_0 - end - }) - _base_0.__class = _class_0 - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -Hello = (function() - local _parent_0 = What - local _base_0 = { - val = 2323, - something = function(self) - return (function() - local _base_1 = _parent_0 - local _fn_0 = _base_1.something - return function(...) - return _fn_0(self, ...) - end - end)() - end - } - _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self, ...) - if _parent_0 then - return _parent_0.__init(self, ...) - end - end, - __base = _base_0, - __name = "Hello", - __parent = _parent_0 - }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, - __call = function(cls, ...) - local _self_0 = setmetatable({}, _base_0) - cls.__init(_self_0, ...) - return _self_0 - end - }) - _base_0.__class = _class_0 - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -do - local _with_0 = Hello() - x = _with_0:something() - print(x) - x() -end -local CoolSuper -CoolSuper = (function() - local _parent_0 = nil - local _base_0 = { - hi = function(self) - _parent_0.hi(self, 1, 2, 3, 4)(1, 2, 3, 4) - _parent_0.something(1, 2, 3, 4) - local _ = _parent_0.something(1, 2, 3, 4).world - _parent_0.yeah(self, "world").okay(hi, hi, hi) - _ = something.super - _ = _parent_0.super.super.super - return nil - end - } - _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self, ...) - if _parent_0 then - return _parent_0.__init(self, ...) - end - end, - __base = _base_0, - __name = "CoolSuper", - __parent = _parent_0 - }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, - __call = function(cls, ...) - local _self_0 = setmetatable({}, _base_0) - cls.__init(_self_0, ...) - return _self_0 - end - }) - _base_0.__class = _class_0 - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -x = self.hello -x = self.__class.hello -self:hello("world") -self.__class:hello("world") -self.__class:one(self.__class:two(4, 5)(self.three, self.four)) -local xx -xx = function(hello, world, cool) - self.hello, self.__class.world = hello, world -end -local ClassMan -ClassMan = (function() - local _parent_0 = nil - local _base_0 = { - blue = function(self) end, - green = function(self) end - } - _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self, ...) - if _parent_0 then - return _parent_0.__init(self, ...) - end - end, - __base = _base_0, - __name = "ClassMan", - __parent = _parent_0 - }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, - __call = function(cls, ...) - local _self_0 = setmetatable({}, _base_0) - cls.__init(_self_0, ...) - return _self_0 - end - }) - _base_0.__class = _class_0 - local self = _class_0 - self.yeah = 343 - self.hello = 3434 - self.world = 23423 - self.red = function(self) end - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -x = self -local y = self.__class -self(something) -self.__class(something) -local self = self + self / self -self = 343 -self.hello(2, 3, 4) -local _ = hello[self].world -local Whacko -Whacko = (function() - local hello - local _parent_0 = nil - local _base_0 = { } - _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self, ...) - if _parent_0 then - return _parent_0.__init(self, ...) - end - end, - __base = _base_0, - __name = "Whacko", - __parent = _parent_0 - }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, - __call = function(cls, ...) - local _self_0 = setmetatable({}, _base_0) - cls.__init(_self_0, ...) - return _self_0 - end - }) - _base_0.__class = _class_0 - local self = _class_0 - _ = self.hello - if something then - print("hello world") - end - hello = "world" - self.another = "day" - if something then - print("yeah") - end - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -print("hello") \ No newline at end of file diff --git a/tests/outputs/comprehension.lua b/tests/outputs/comprehension.lua deleted file mode 100644 index b1bf218d..00000000 --- a/tests/outputs/comprehension.lua +++ /dev/null @@ -1,81 +0,0 @@ -local items = { - 1, - 2, - 3, - 4, - 5, - 6 -} -local out = (function() - local _tbl_0 = { } - for k in items do - _tbl_0[k] = k * 2 - end - return _tbl_0 -end)() -local x = { - hello = "world", - okay = 2323 -} -local copy = (function() - local _tbl_0 = { } - for k, v in pairs(x) do - if k ~= "okay" then - _tbl_0[k] = v - end - end - return _tbl_0 -end)() -local _ = (function() - local _tbl_0 = { } - for x in yes do - local _key_0, _val_0 = unpack(x) - _tbl_0[_key_0] = _val_0 - end - return _tbl_0 -end)() -_ = (function() - local _tbl_0 = { } - local _list_0 = yes - for _index_0 = 1, #_list_0 do - x = _list_0[_index_0] - local _key_0, _val_0 = unpack(x) - _tbl_0[_key_0] = _val_0 - end - return _tbl_0 -end)() -_ = (function() - local _tbl_0 = { } - for x in yes do - local _key_0, _val_0 = xxxx - _tbl_0[_key_0] = _val_0 - end - return _tbl_0 -end)() -_ = (function() - local _tbl_0 = { } - local _list_0 = { - { - 1, - 2 - }, - { - 3, - 4 - } - } - for _index_0 = 1, #_list_0 do - x = _list_0[_index_0] - local _key_0, _val_0 = unpack((function() - local _accum_0 = { } - local _len_0 = 0 - for i, a in ipairs(x) do - _len_0 = _len_0 + 1 - _accum_0[_len_0] = a * i - end - return _accum_0 - end)()) - _tbl_0[_key_0] = _val_0 - end - return _tbl_0 -end)() \ No newline at end of file diff --git a/tests/outputs/export.lua b/tests/outputs/export.lua deleted file mode 100644 index 50e1328c..00000000 --- a/tests/outputs/export.lua +++ /dev/null @@ -1,72 +0,0 @@ -a, b, c = 223, 343 -cool = "dad" -Something = (function() - local _parent_0 = nil - local _base_0 = { - umm = "cool" - } - _base_0.__index = _base_0 - if _parent_0 then - setmetatable(_base_0, _parent_0.__base) - end - local _class_0 = setmetatable({ - __init = function(self, ...) - if _parent_0 then - return _parent_0.__init(self, ...) - end - end, - __base = _base_0, - __name = "Something", - __parent = _parent_0 - }, { - __index = function(cls, name) - local val = rawget(_base_0, name) - if val == nil and _parent_0 then - return _parent_0[name] - else - return val - end - end, - __call = function(cls, ...) - local _self_0 = setmetatable({}, _base_0) - cls.__init(_self_0, ...) - return _self_0 - end - }) - _base_0.__class = _class_0 - if _parent_0 and _parent_0.__inherited then - _parent_0.__inherited(_parent_0, _class_0) - end - return _class_0 -end)() -local What -if this then - What = 232 -else - What = 4343 -end -local d -a, b, c, d = "hello" -local another = 3434 -Another = 7890 -if inner then - local Yeah = "10000" -end -if this then - What = 232 -else - What = 4343 -end -if this then - What = 232 -else - What = 4343 -end -x, y, z = 1, 2, 3 -y = function() - local hallo = 3434 -end -do - local _with_0 = tmp - local j = 2000 -end \ No newline at end of file diff --git a/tests/outputs/funcs.lua b/tests/outputs/funcs.lua deleted file mode 100644 index 98a3f5f6..00000000 --- a/tests/outputs/funcs.lua +++ /dev/null @@ -1,115 +0,0 @@ -local x -x = function() - return print(what) -end -local _ -_ = function() end -_ = function() - return function() - return function() end - end -end -go(to(the(barn))) -open(function() - return the(function() - return door - end) -end) -open(function() - the(door) - local hello - hello = function() - return my(func) - end -end) -local h -h = function() - return hi -end -eat(function() end, world); -(function() end)() -x = function(...) end -hello() -hello.world() -_ = hello().something -_ = what()["ofefe"] -what()(the()(heck())) -_ = function(a, b, c, d, e) end -_ = function(a, a, a, a, a) - return print(a) -end -_ = function(x) - if x == nil then - x = 23023 - end -end -_ = function(x) - if x == nil then - x = function(y) - if y == nil then - y = function() end - end - end - end -end -_ = function(x) - if x == nil then - if something then - x = yeah - else - x = no - end - end -end -local something -something = function(hello, world) - if hello == nil then - hello = 100 - end - if world == nil then - world = function(x) - if x == nil then - x = [[yeah cool]] - end - return print("eat rice") - end - end - return print(hello) -end -_ = function(self, x, y) end -_ = function(self, x, y) - self.x, self.y = x, y -end -_ = function(self, x) - if x == nil then - x = 1 - end -end -_ = function(self, x, y, z) - if x == nil then - x = 1 - end - if z == nil then - z = "hello world" - end - self.x, self.z = x, z -end -x(function() end) -y(function() - return 1 -end) -z(function() - return 1, "hello", "world" -end) -k(function() - if yes then - return - else - return - end -end) -_ = function() - if something then - return real_name - end -end \ No newline at end of file diff --git a/tests/outputs/import.lua b/tests/outputs/import.lua deleted file mode 100644 index 9c3cf0e4..00000000 --- a/tests/outputs/import.lua +++ /dev/null @@ -1,45 +0,0 @@ -local hello = yeah.hello -local world -do - local _table_0 = table["cool"] - hello, world = _table_0.hello, _table_0.world -end -local a, b, c = items.a, (function() - local _base_0 = items - local _fn_0 = _base_0.b - return function(...) - return _fn_0(_base_0, ...) - end -end)(), items.c -local master, ghost -do - local _table_0 = find("mytable") - master, ghost = _table_0.master, (function() - local _base_0 = _table_0 - local _fn_0 = _base_0.ghost - return function(...) - return _fn_0(_base_0, ...) - end - end)() -end -local yumm -a, yumm = 3434, "hello" -local _table_0 = 232 -local something -do - local _table_1 = a(table) - something = _table_1.something -end -if indent then - local okay, well - do - local _table_1 = tables[100] - okay, well = _table_1.okay, (function() - local _base_0 = _table_1 - local _fn_0 = _base_0.well - return function(...) - return _fn_0(_base_0, ...) - end - end)() - end -end \ No newline at end of file diff --git a/tests/outputs/literals.lua b/tests/outputs/literals.lua deleted file mode 100644 index 0f78e4cb..00000000 --- a/tests/outputs/literals.lua +++ /dev/null @@ -1,14 +0,0 @@ -local _ = 121 -_ = 121.2323 -_ = 121.2323e-1 -_ = 121.2323e13434 -_ = 2323E34 -_ = 0x12323 -_ = 0xfF2323 -_ = 0xabcdef -_ = 0xABCDEF -_ = [[ hello world ]] -_ = [=[ hello world ]=] -_ = [====[ hello world ]====] -_ = "another world" -_ = 'what world' \ No newline at end of file diff --git a/tests/outputs/local.lua b/tests/outputs/local.lua deleted file mode 100644 index b76663ce..00000000 --- a/tests/outputs/local.lua +++ /dev/null @@ -1,8 +0,0 @@ -local a -local a, b, c -local x = 1212 -local something -something = function() - local x - x = 1212 -end \ No newline at end of file diff --git a/tests/outputs/with.lua b/tests/outputs/with.lua deleted file mode 100644 index a0af190e..00000000 --- a/tests/outputs/with.lua +++ /dev/null @@ -1,48 +0,0 @@ -local a -a = function() - do - local _with_0 = something - print(_with_0.hello) - print(hi) - print("world") - return _with_0 - end -end -do - local _with_0 = leaf - _with_0.world() - _with_0.world(1, 2, 3) - local g = _with_0.what.is.this - _with_0.hi(1, 2, 3) - _with_0:hi(1, 2).world(2323) - _with_0:hi("yeah", "man") - _with_0.world = 200 -end -local zyzyzy -do - local _with_0 = something - _with_0.set_state("hello world") - zyzyzy = _with_0 -end -local x = 5 + (function() - do - local _with_0 = Something() - _with_0:write("hello world") - return _with_0 - end -end)() -x = { - hello = (function() - do - local _with_0 = yeah - _with_0:okay() - return _with_0 - end - end)() -} -do - local _with_0 = foo - local _ = _with_0:prop("something").hello - _with_0.prop:send(one) - _with_0.prop:send(one) -end \ No newline at end of file diff --git a/thoughts b/thoughts new file mode 100644 index 00000000..dfd6b69d --- /dev/null +++ b/thoughts @@ -0,0 +1,31 @@ +# TODO +# +# +# + +- don't reuse _, put a local on it, so we don't keep around trash + +-- swithc X with Func + +- error with stray comma at end of line + +* multiline comments +* table slices (almost) + +* combine for and if line decorators + +* allow return anywhere in block + +-- all function literals have a string that is their function definition + +-- super should work here: + +thing = Thing! +thing.method = -> + super 1,2,3 + +-- goes to + +thing.method = function(self) do + self.__class:method(1,2,3) +end diff --git a/todo b/todo deleted file mode 100644 index 3b80bc66..00000000 --- a/todo +++ /dev/null @@ -1,59 +0,0 @@ -# TODO - --- error when assining a loop whose body doesn't end in expression - -x = for x in y - y = 100 - --- seems like running in moon messes up require order - -- don't reuse _, put a local on it, so we don't keep around trash - --- swithc X with Func - -- or= and= - -- a do block for making a quick anon func and calling it - -- error with stray comma at end of line -- ugly error if attempting to assign to rvalue like #hello - -- dump node in mark, see why values are being parsed multiple times - -- varargs that get put in a nested generated function aren't valid anymore: - -- class expressions, x = class extends Hello do new: => print "hello" - -* multiline comments -* table slices (almost) -* add continue keyword (ouch) - -* combine for and if line decorators - -* elseif with value that inserts lines - - need to decompose elif into if inside else - -* export later? - - x = 232 - export x - - -* allow return anywhere in block - -* any/every keywords for comprehensions? (what about iterators) - -* let array items in table be defined without {} when indented - - -* allow dot access with names that are keywords: hello.then - -* convert tree transformer to be depth first instead of breadth first (lazy) - - --- for searching? -x = for thing in *things - if is_important thing - break thing - -