From 7066392d1c974065181d95d93274136dcd625d43 Mon Sep 17 00:00:00 2001 From: Li Jin Date: Wed, 17 Feb 2021 11:22:07 +0800 Subject: stop reusing variables, rename project. --- .gitignore | 8 +- CMakeLists.txt | 16 +- MoonPlus.sln | 31 - MoonPlus.vcxproj | 244 -- MoonPlus.vcxproj.filters | 238 -- MoonPlus.vcxproj.user | 4 - README.md | 63 +- Yuescript.sln | 31 + Yuescript.vcxproj | 245 ++ Yuescript.vcxproj.filters | 238 ++ appveyor.yml | 2 +- docker/Dockerfile | 8 +- makefile | 22 +- moonplus-dev-1.rockspec | 33 - spec/inputs/ambiguous.mp | 26 - spec/inputs/ambiguous.yue | 26 + spec/inputs/assign.mp | 33 - spec/inputs/assign.yue | 33 + spec/inputs/attrib.mp | 40 - spec/inputs/attrib.yue | 40 + spec/inputs/backcall.mp | 139 - spec/inputs/backcall.yue | 153 ++ spec/inputs/bubbling.mp | 27 - spec/inputs/bubbling.yue | 27 + spec/inputs/class.mp | 232 -- spec/inputs/class.yue | 232 ++ spec/inputs/comprehension.mp | 52 - spec/inputs/comprehension.yue | 52 + spec/inputs/cond.mp | 190 -- spec/inputs/cond.yue | 190 ++ spec/inputs/destructure.mp | 117 - spec/inputs/destructure.yue | 117 + spec/inputs/do.mp | 27 - spec/inputs/do.yue | 27 + spec/inputs/existential.mp | 51 - spec/inputs/existential.yue | 51 + spec/inputs/export.mp | 79 - spec/inputs/export.yue | 79 + spec/inputs/export_default.mp | 15 - spec/inputs/export_default.yue | 15 + spec/inputs/funcs.mp | 161 -- spec/inputs/funcs.yue | 161 ++ spec/inputs/global.mp | 77 - spec/inputs/global.yue | 77 + spec/inputs/goto.mp | 41 - spec/inputs/goto.yue | 41 + spec/inputs/import.mp | 68 - spec/inputs/import.yue | 68 + spec/inputs/lists.mp | 72 - spec/inputs/lists.yue | 72 + spec/inputs/literals.mp | 46 - spec/inputs/literals.yue | 46 + spec/inputs/local.mp | 94 - spec/inputs/local.yue | 94 + spec/inputs/loops.mp | 148 -- spec/inputs/loops.yue | 148 ++ spec/inputs/macro-export.mp | 29 - spec/inputs/macro-export.yue | 29 + spec/inputs/macro-teal.mp | 87 - spec/inputs/macro-teal.yue | 87 + spec/inputs/macro.mp | 258 -- spec/inputs/macro.yue | 258 ++ spec/inputs/multiline_chain.mp | 67 - spec/inputs/multiline_chain.yue | 98 + spec/inputs/operators.mp | 72 - spec/inputs/operators.yue | 72 + spec/inputs/plus.mp | 40 - spec/inputs/plus.yue | 40 + spec/inputs/return.mp | 55 - spec/inputs/return.yue | 55 + spec/inputs/string.mp | 66 - spec/inputs/string.yue | 66 + spec/inputs/stub.mp | 18 - spec/inputs/stub.yue | 18 + spec/inputs/switch.mp | 64 - spec/inputs/switch.yue | 64 + spec/inputs/syntax.mp | 418 --- spec/inputs/syntax.yue | 418 +++ spec/inputs/tables.mp | 223 -- spec/inputs/tables.yue | 223 ++ spec/inputs/teal-lang.mp | 39 - spec/inputs/teal-lang.yue | 39 + spec/inputs/unless_else.mp | 5 - spec/inputs/unless_else.yue | 5 + spec/inputs/using.mp | 22 - spec/inputs/using.yue | 22 + spec/inputs/whitespace.mp | 140 - spec/inputs/whitespace.yue | 140 + spec/inputs/with.mp | 123 - spec/inputs/with.yue | 123 + src/MoonP/ast.cpp | 147 - src/MoonP/ast.hpp | 571 ---- src/MoonP/moon_ast.h | 750 ------ src/MoonP/moon_compiler.cpp | 5622 --------------------------------------- src/MoonP/moon_compiler.h | 63 - src/MoonP/moon_parser.cpp | 719 ----- src/MoonP/moon_parser.h | 317 --- src/MoonP/moonplus.cpp | 139 - src/MoonP/moonplus.h | 251 -- src/MoonP/parser.cpp | 1412 ---------- src/MoonP/parser.hpp | 422 --- src/MoonP/stacktraceplus.h | 528 ---- src/moonp.cpp | 570 ---- src/yue.cpp | 570 ++++ src/yuescript/ast.cpp | 147 + src/yuescript/ast.hpp | 571 ++++ src/yuescript/parser.cpp | 1412 ++++++++++ src/yuescript/parser.hpp | 422 +++ src/yuescript/stacktraceplus.h | 528 ++++ src/yuescript/yue_ast.h | 750 ++++++ src/yuescript/yue_compiler.cpp | 5612 ++++++++++++++++++++++++++++++++++++++ src/yuescript/yue_compiler.h | 63 + src/yuescript/yue_parser.cpp | 719 +++++ src/yuescript/yue_parser.h | 317 +++ src/yuescript/yuescript.cpp | 139 + src/yuescript/yuescript.h | 251 ++ yuescript-dev-1.rockspec | 33 + 117 files changed, 15606 insertions(+), 15589 deletions(-) delete mode 100644 MoonPlus.sln delete mode 100644 MoonPlus.vcxproj delete mode 100644 MoonPlus.vcxproj.filters delete mode 100644 MoonPlus.vcxproj.user create mode 100644 Yuescript.sln create mode 100644 Yuescript.vcxproj create mode 100644 Yuescript.vcxproj.filters delete mode 100644 moonplus-dev-1.rockspec delete mode 100644 spec/inputs/ambiguous.mp create mode 100644 spec/inputs/ambiguous.yue delete mode 100644 spec/inputs/assign.mp create mode 100644 spec/inputs/assign.yue delete mode 100644 spec/inputs/attrib.mp create mode 100644 spec/inputs/attrib.yue delete mode 100644 spec/inputs/backcall.mp create mode 100644 spec/inputs/backcall.yue delete mode 100644 spec/inputs/bubbling.mp create mode 100644 spec/inputs/bubbling.yue delete mode 100644 spec/inputs/class.mp create mode 100644 spec/inputs/class.yue delete mode 100644 spec/inputs/comprehension.mp create mode 100644 spec/inputs/comprehension.yue delete mode 100644 spec/inputs/cond.mp create mode 100644 spec/inputs/cond.yue delete mode 100644 spec/inputs/destructure.mp create mode 100644 spec/inputs/destructure.yue delete mode 100644 spec/inputs/do.mp create mode 100644 spec/inputs/do.yue delete mode 100644 spec/inputs/existential.mp create mode 100644 spec/inputs/existential.yue delete mode 100644 spec/inputs/export.mp create mode 100644 spec/inputs/export.yue delete mode 100644 spec/inputs/export_default.mp create mode 100644 spec/inputs/export_default.yue delete mode 100644 spec/inputs/funcs.mp create mode 100644 spec/inputs/funcs.yue delete mode 100644 spec/inputs/global.mp create mode 100644 spec/inputs/global.yue delete mode 100644 spec/inputs/goto.mp create mode 100644 spec/inputs/goto.yue delete mode 100644 spec/inputs/import.mp create mode 100644 spec/inputs/import.yue delete mode 100644 spec/inputs/lists.mp create mode 100644 spec/inputs/lists.yue delete mode 100644 spec/inputs/literals.mp create mode 100644 spec/inputs/literals.yue delete mode 100644 spec/inputs/local.mp create mode 100644 spec/inputs/local.yue delete mode 100644 spec/inputs/loops.mp create mode 100644 spec/inputs/loops.yue delete mode 100644 spec/inputs/macro-export.mp create mode 100644 spec/inputs/macro-export.yue delete mode 100644 spec/inputs/macro-teal.mp create mode 100644 spec/inputs/macro-teal.yue delete mode 100644 spec/inputs/macro.mp create mode 100644 spec/inputs/macro.yue delete mode 100644 spec/inputs/multiline_chain.mp create mode 100644 spec/inputs/multiline_chain.yue delete mode 100644 spec/inputs/operators.mp create mode 100644 spec/inputs/operators.yue delete mode 100644 spec/inputs/plus.mp create mode 100644 spec/inputs/plus.yue delete mode 100644 spec/inputs/return.mp create mode 100644 spec/inputs/return.yue delete mode 100644 spec/inputs/string.mp create mode 100644 spec/inputs/string.yue delete mode 100644 spec/inputs/stub.mp create mode 100644 spec/inputs/stub.yue delete mode 100644 spec/inputs/switch.mp create mode 100644 spec/inputs/switch.yue delete mode 100644 spec/inputs/syntax.mp create mode 100644 spec/inputs/syntax.yue delete mode 100644 spec/inputs/tables.mp create mode 100644 spec/inputs/tables.yue delete mode 100644 spec/inputs/teal-lang.mp create mode 100644 spec/inputs/teal-lang.yue delete mode 100644 spec/inputs/unless_else.mp create mode 100644 spec/inputs/unless_else.yue delete mode 100644 spec/inputs/using.mp create mode 100644 spec/inputs/using.yue delete mode 100644 spec/inputs/whitespace.mp create mode 100644 spec/inputs/whitespace.yue delete mode 100644 spec/inputs/with.mp create mode 100644 spec/inputs/with.yue delete mode 100644 src/MoonP/ast.cpp delete mode 100644 src/MoonP/ast.hpp delete mode 100644 src/MoonP/moon_ast.h delete mode 100644 src/MoonP/moon_compiler.cpp delete mode 100644 src/MoonP/moon_compiler.h delete mode 100644 src/MoonP/moon_parser.cpp delete mode 100644 src/MoonP/moon_parser.h delete mode 100644 src/MoonP/moonplus.cpp delete mode 100644 src/MoonP/moonplus.h delete mode 100644 src/MoonP/parser.cpp delete mode 100644 src/MoonP/parser.hpp delete mode 100644 src/MoonP/stacktraceplus.h delete mode 100644 src/moonp.cpp create mode 100644 src/yue.cpp create mode 100644 src/yuescript/ast.cpp create mode 100644 src/yuescript/ast.hpp create mode 100644 src/yuescript/parser.cpp create mode 100644 src/yuescript/parser.hpp create mode 100644 src/yuescript/stacktraceplus.h create mode 100644 src/yuescript/yue_ast.h create mode 100644 src/yuescript/yue_compiler.cpp create mode 100644 src/yuescript/yue_compiler.h create mode 100644 src/yuescript/yue_parser.cpp create mode 100644 src/yuescript/yue_parser.h create mode 100644 src/yuescript/yuescript.cpp create mode 100644 src/yuescript/yuescript.h create mode 100644 yuescript-dev-1.rockspec diff --git a/.gitignore b/.gitignore index 17620dc..9936698 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,10 @@ -moonp -!src/MoonP +yue +!src/yuescript/ build build.luarocks spec/outputs -moonplus-*.rock* -!moonplus-dev-1.rockspec +yuescript-*.rock* +!yuescript-dev-1.rockspec .vs bin src/lua/*.o diff --git a/CMakeLists.txt b/CMakeLists.txt index 1fcb248..d323b6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -project ( moonp CXX ) +project ( yue CXX ) cmake_minimum_required ( VERSION 3.1 ) MESSAGE(STATUS "Lua: using information from luarocks") @@ -28,15 +28,15 @@ enable_language( CXX ) include_directories( src ${LUA_INCLUDE_DIR} ) add_definitions( -std=c++17 -O3 -fPIC ) -add_library( libmoonp MODULE src/MoonP/ast.cpp src/MoonP/parser.cpp src/MoonP/moon_parser.cpp src/MoonP/moon_compiler.cpp src/MoonP/moonplus.cpp) -set_target_properties( libmoonp PROPERTIES PREFIX "" ) -set_target_properties( libmoonp PROPERTIES OUTPUT_NAME "moonp" ) -target_link_libraries( libmoonp ${LUA_LIBRARIES} ) +add_library( libyue MODULE src/yuescript/ast.cpp src/yuescript/parser.cpp src/yuescript/yue_parser.cpp src/yuescript/yue_compiler.cpp src/yuescript/yuescript.cpp) +set_target_properties( libyue PROPERTIES PREFIX "" ) +set_target_properties( libyue PROPERTIES OUTPUT_NAME "yue" ) +target_link_libraries( libyue ${LUA_LIBRARIES} ) -add_executable( moonp src/MoonP/ast.cpp src/MoonP/moon_compiler.cpp src/MoonP/moon_parser.cpp src/MoonP/moonplus.cpp src/MoonP/parser.cpp src/moonp.cpp ) -target_link_libraries( moonp ${LUA_LIBRARIES} ) +add_executable( yue src/yuescript/ast.cpp src/yuescript/yue_compiler.cpp src/yuescript/yue_parser.cpp src/yuescript/yuescript.cpp src/yuescript/parser.cpp src/yue.cpp ) +target_link_libraries( yue ${LUA_LIBRARIES} ) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_link_options( moonp PRIVATE -ldl -lpthread -lstdc++fs ) + target_link_options( yue PRIVATE -ldl -lpthread -lstdc++fs ) endif() install(CODE "") diff --git a/MoonPlus.sln b/MoonPlus.sln deleted file mode 100644 index 252e9b4..0000000 --- a/MoonPlus.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29806.167 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MoonPlus", "MoonPlus.vcxproj", "{CF4F54DB-61FE-48E0-BA2E-4168BA27ECBD}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {CF4F54DB-61FE-48E0-BA2E-4168BA27ECBD}.Debug|x64.ActiveCfg = Debug|x64 - {CF4F54DB-61FE-48E0-BA2E-4168BA27ECBD}.Debug|x64.Build.0 = Debug|x64 - {CF4F54DB-61FE-48E0-BA2E-4168BA27ECBD}.Debug|x86.ActiveCfg = Debug|Win32 - {CF4F54DB-61FE-48E0-BA2E-4168BA27ECBD}.Debug|x86.Build.0 = Debug|Win32 - {CF4F54DB-61FE-48E0-BA2E-4168BA27ECBD}.Release|x64.ActiveCfg = Release|x64 - {CF4F54DB-61FE-48E0-BA2E-4168BA27ECBD}.Release|x64.Build.0 = Release|x64 - {CF4F54DB-61FE-48E0-BA2E-4168BA27ECBD}.Release|x86.ActiveCfg = Release|Win32 - {CF4F54DB-61FE-48E0-BA2E-4168BA27ECBD}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {49826A7F-7A92-4CFC-95DD-28B7045BB2F7} - EndGlobalSection -EndGlobal diff --git a/MoonPlus.vcxproj b/MoonPlus.vcxproj deleted file mode 100644 index d01007c..0000000 --- a/MoonPlus.vcxproj +++ /dev/null @@ -1,244 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 16.0 - {CF4F54DB-61FE-48E0-BA2E-4168BA27ECBD} - MoonPlus - 10.0 - - - - Application - true - v142 - Unicode - - - Application - false - v142 - true - Unicode - - - Application - true - v142 - Unicode - - - Application - false - v142 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - $(SolutionDir)\build\windows\$(Configuration)\ - $(SolutionDir)\bin\windows\$(Configuration)\ - moonp - - - true - $(SolutionDir)\bin\windows\$(Platform)\$(Configuration)\ - $(SolutionDir)\build\windows\$(Platform)\$(Configuration)\ - moonp - - - false - $(SolutionDir)\build\windows\$(Configuration)\ - $(SolutionDir)\bin\windows\$(Configuration)\ - moonp - - - false - $(SolutionDir)\bin\windows\$(Platform)\$(Configuration)\ - $(SolutionDir)\build\windows\$(Platform)\$(Configuration)\ - moonp - - - - Level3 - true - _DEBUG;_CONSOLE;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;%(PreprocessorDefinitions) - true - stdcpplatest - .\src;.\src\lua;%(AdditionalIncludeDirectories) - /Zc:__cplusplus %(AdditionalOptions) - - - Console - true - - - - - Level3 - true - _DEBUG;_CONSOLE;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;%(PreprocessorDefinitions) - true - stdcpplatest - .\src;.\src\lua;%(AdditionalIncludeDirectories) - /Zc:__cplusplus %(AdditionalOptions) - - - Console - true - - - - - Level3 - true - true - true - NDEBUG;_CONSOLE;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;%(PreprocessorDefinitions) - true - stdcpplatest - .\src;.\src\lua;%(AdditionalIncludeDirectories) - /Zc:__cplusplus %(AdditionalOptions) - - - Console - true - true - true - - - - - Level3 - true - true - true - NDEBUG;_CONSOLE;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;%(PreprocessorDefinitions) - true - stdcpplatest - .\src;.\src\lua;%(AdditionalIncludeDirectories) - /Zc:__cplusplus %(AdditionalOptions) - - - Console - true - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MoonPlus.vcxproj.filters b/MoonPlus.vcxproj.filters deleted file mode 100644 index 6e61e9c..0000000 --- a/MoonPlus.vcxproj.filters +++ /dev/null @@ -1,238 +0,0 @@ - - - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;ipp;xsd - - - {c88639d5-457d-42eb-b852-65682c89c618} - - - {338bdf98-6a02-4589-a373-c7bca25c746b} - - - - - src\MoonP - - - src\MoonP - - - src\MoonP - - - src\MoonP - - - src - - - src\MoonP - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - - - src\MoonP - - - src\MoonP - - - src\MoonP - - - src\MoonP - - - src\MoonP - - - src\MoonP - - - src\MoonP - - - src - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - src\lua - - - \ No newline at end of file diff --git a/MoonPlus.vcxproj.user b/MoonPlus.vcxproj.user deleted file mode 100644 index 88a5509..0000000 --- a/MoonPlus.vcxproj.user +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/README.md b/README.md index 5d699cd..6a4304d 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ -# MoonPlus +# YueScript |macOS|Linux|Windows| |:---:|:---:|:-----:| |![CI](https://github.com/pigpigyyy/MoonPlus/workflows/build-test/badge.svg)|![CI](https://github.com/pigpigyyy/MoonPlus/workflows/build-test/badge.svg)|[![Build status](https://ci.appveyor.com/api/projects/status/4nbkye9mx9b3bf83/branch/master?svg=true)](https://ci.appveyor.com/project/pigpigyyy/moonplus/branch/master)| -MoonPlus is a compiler with features from [Moonscript language](https://github.com/leafo/moonscript) 0.5.0 and adopting new features to make Moonscript more up to date. +Yuescript is a Moonscript dialect. It is derived from [Moonscript language](https://github.com/leafo/moonscript) 0.5.0 and continuously adopting new features to be more up to date. -Since original Moonscript has been used to write web framework [lapis](https://github.com/leafo/lapis) and run a few business web sites like [itch.io](https://itch.io) and [streak.club](https://streak.club) with some large code bases. The original language is getting too hard to adopt new features for those may break the stablility for existing applications. +Moonscript is a language that compiles to Lua. Since original Moonscript has been used to write web framework [lapis](https://github.com/leafo/lapis) and run a few business web sites like [itch.io](https://itch.io) and [streak.club](https://streak.club) with some large code bases. The original language is getting too hard to adopt new features for those may break the stablility for existing applications. -So MoonPlus is a new code base for pushing the language to go forward and being a playground to try introducing new language syntax or programing paradigms to make Moonscript language more expressive and productive. +So Yuescript is a new code base for pushing the language to go forward and being a playground to try introducing new language syntax or programing paradigms to make Moonscript language more expressive and productive. @@ -16,8 +16,9 @@ So MoonPlus is a new code base for pushing the language to go forward and being * No other dependencies needed except modified **parserlib** library from Achilleas Margaritis with some performance enhancement. **lpeg** library is no longer needed. * Written in C++17. -* Support full Moonscript language features, generate the same Lua codes with original compiler. -* Reserve line numbers from Moonscript sources in compiled Lua codes to help with debugging. +* Support most of the features from Moonscript language. Generate Lua codes in the same way like the original compiler. +* Reserve line numbers from source file in the compiled Lua codes to help debugging. +* More features like macro, existential operator, backcall operator, Javascript-like export syntax and etc. * See other details in the [changelog](./CHANGELOG.md). @@ -26,27 +27,27 @@ So MoonPlus is a new code base for pushing the language to go forward and being * **Lua Module** -  Build `moonp.so` file with +  Build `yue.so` file with ```sh > make shared LUAI=/usr/local/include/lua LUAL=/usr/local/lib/lua ``` -  Then get the binary file from path `bin/shared/moonp.so`. +  Then get the binary file from path `bin/shared/yue.so`.   Or you can install [luarocks](https://luarocks.org), a package manager for Lua modules. Then install it as a Lua module with ```sh -> luarocks install moonplus +> luarocks install yuescript ``` -  Then require the MoonPlus module in Lua: +  Then require the Yuescript module in Lua: ```Lua -require("moonp")("main") -- require `main.mp` +require("yue")("main") -- require `main.yue` -local moonp = require("moonp") -local codes, err, globals = moonp.to_lua([[ +local yue = require("yue") +local codes, err, globals = yue.to_lua([[ f = -> print "hello world" f! @@ -66,21 +67,21 @@ f! > make install ``` -  Build MoonPlus tool without macro feature: +  Build Yuescript tool without macro feature: ```sh > make install NO_MACRO=true ``` -  Build MoonPlus tool without built-in Lua binary: +  Build Yuescript tool without built-in Lua binary: ```sh > make install NO_LUA=true ``` -  Use MoonPlus tool with: +  Use Yuescript tool with: ```sh -> moonp -h -Usage: moonp [options|files|directories] ... +> yue -h +Usage: yue [options|files|directories] ... -h Print this message -e str Execute a file or raw codes @@ -99,30 +100,18 @@ Usage: moonp [options|files|directories] ... in a single line to start/stop multi-line mode ```   Use cases: -  Recursively compile every moon+ file with extension `.mp` under current path: `moonp .` -  Compile and save results to a target path: `moonp -t /target/path/ .` -  Compile and reserve debug info: `moonp -l .` -  Compile and generate minified codes: `moonp -m .` -  Execute raw codes: `moonp -e 'print 123'` -  Execute a moon+ file: `moonp -e main.mp` +  Recursively compile every Yuescript file with extension `.yue` under current path: `yue .` +  Compile and save results to a target path: `yue -t /target/path/ .` +  Compile and reserve debug info: `yue -l .` +  Compile and generate minified codes: `yue -m .` +  Execute raw codes: `yue -e 'print 123'` +  Execute a Yuescript file: `yue -e main.yue` -* **Docker Hub** - Try moonp in another easy way: https://hub.docker.com/r/moonplus/moonplus. - ```bash - docker run -it moonplus/moonplus:0.4.19 - ``` - - The REPL console should be ready, - ```bash - Moonscript+ 0.4.19 - > - ``` - ## Editor Support -* [Vim](https://github.com/pigpigyyy/MoonPlus-vim) +* [Vim](https://github.com/pigpigyyy/Yuescript-vim) diff --git a/Yuescript.sln b/Yuescript.sln new file mode 100644 index 0000000..7328cef --- /dev/null +++ b/Yuescript.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29806.167 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Yuescript", "Yuescript.vcxproj", "{CF4F54DB-61FE-48E0-BA2E-4168BA27ECBD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CF4F54DB-61FE-48E0-BA2E-4168BA27ECBD}.Debug|x64.ActiveCfg = Debug|x64 + {CF4F54DB-61FE-48E0-BA2E-4168BA27ECBD}.Debug|x64.Build.0 = Debug|x64 + {CF4F54DB-61FE-48E0-BA2E-4168BA27ECBD}.Debug|x86.ActiveCfg = Debug|Win32 + {CF4F54DB-61FE-48E0-BA2E-4168BA27ECBD}.Debug|x86.Build.0 = Debug|Win32 + {CF4F54DB-61FE-48E0-BA2E-4168BA27ECBD}.Release|x64.ActiveCfg = Release|x64 + {CF4F54DB-61FE-48E0-BA2E-4168BA27ECBD}.Release|x64.Build.0 = Release|x64 + {CF4F54DB-61FE-48E0-BA2E-4168BA27ECBD}.Release|x86.ActiveCfg = Release|Win32 + {CF4F54DB-61FE-48E0-BA2E-4168BA27ECBD}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {49826A7F-7A92-4CFC-95DD-28B7045BB2F7} + EndGlobalSection +EndGlobal diff --git a/Yuescript.vcxproj b/Yuescript.vcxproj new file mode 100644 index 0000000..e284a4b --- /dev/null +++ b/Yuescript.vcxproj @@ -0,0 +1,245 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {CF4F54DB-61FE-48E0-BA2E-4168BA27ECBD} + Yuescript + 10.0 + Yuescript + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\windows\$(Configuration)\ + $(SolutionDir)\bin\windows\$(Configuration)\ + yue + + + true + $(SolutionDir)\bin\windows\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\windows\$(Platform)\$(Configuration)\ + yue + + + false + $(SolutionDir)\build\windows\$(Configuration)\ + $(SolutionDir)\bin\windows\$(Configuration)\ + yue + + + false + $(SolutionDir)\bin\windows\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\windows\$(Platform)\$(Configuration)\ + yue + + + + Level3 + true + _DEBUG;_CONSOLE;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;%(PreprocessorDefinitions) + true + stdcpplatest + .\src;.\src\lua;%(AdditionalIncludeDirectories) + /Zc:__cplusplus %(AdditionalOptions) + + + Console + true + + + + + Level3 + true + _DEBUG;_CONSOLE;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;%(PreprocessorDefinitions) + true + stdcpplatest + .\src;.\src\lua;%(AdditionalIncludeDirectories) + /Zc:__cplusplus %(AdditionalOptions) + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;%(PreprocessorDefinitions) + true + stdcpplatest + .\src;.\src\lua;%(AdditionalIncludeDirectories) + /Zc:__cplusplus %(AdditionalOptions) + + + Console + true + true + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;%(PreprocessorDefinitions) + true + stdcpplatest + .\src;.\src\lua;%(AdditionalIncludeDirectories) + /Zc:__cplusplus %(AdditionalOptions) + + + Console + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Yuescript.vcxproj.filters b/Yuescript.vcxproj.filters new file mode 100644 index 0000000..97385f3 --- /dev/null +++ b/Yuescript.vcxproj.filters @@ -0,0 +1,238 @@ + + + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {338bdf98-6a02-4589-a373-c7bca25c746b} + + + {af398a57-cb37-4862-a5e8-5ec539259611} + + + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\yuescript + + + src\yuescript + + + src\yuescript + + + src\yuescript + + + src\yuescript + + + src + + + + + src + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\lua + + + src\yuescript + + + src\yuescript + + + src\yuescript + + + src\yuescript + + + src\yuescript + + + src\yuescript + + + src\yuescript + + + \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index 94125fd..c7f83e9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,7 +8,7 @@ configuration: - Release platform: x86 build: - project: MoonPlus.sln + project: Yuescript.sln parallel: true verbosity: normal test: off diff --git a/docker/Dockerfile b/docker/Dockerfile index c4ccab9..d1e75b1 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -11,10 +11,6 @@ RUN cd /tmp && wget https://luarocks.org/releases/luarocks-3.3.1.tar.gz \ && make && make install \ && cd /tmp ; rm -rf luarocks-3.3.1 luarocks-3.3.1.tar.gz -RUN luarocks install moonplus +RUN luarocks install yuescript -RUN cd /tmp && git clone https://github.com/pigpigyyy/MoonPlus \ - && cd MoonPlus && make install \ - && cd /tmp ; rm -rf MoonPlus/ - -CMD ["moonp"] +CMD ["yue"] diff --git a/makefile b/makefile index 1a6f2f1..e1d43cc 100644 --- a/makefile +++ b/makefile @@ -1,6 +1,6 @@ #### PROJECT SETTINGS #### # The name of the executable to be created -BIN_NAME := moonp +BIN_NAME := yue # Compiler used CXX ?= g++ # Extension of source files used in the project @@ -42,10 +42,10 @@ TEST_OUTPUT = ./spec/outputs UNAME_S:=$(shell uname -s) ifeq ($(NO_LUA),true) - COMPILE_FLAGS += -DMOONP_NO_MACRO -DMOONP_COMPILER_ONLY + COMPILE_FLAGS += -DYUE_NO_MACRO -DYUE_COMPILER_ONLY else ifeq ($(NO_MACRO),true) - COMPILE_FLAGS += -DMOONP_NO_MACRO + COMPILE_FLAGS += -DYUE_NO_MACRO endif INCLUDES += -I ./src/lua LINK_FLAGS += -L ./src/lua -llua -ldl @@ -117,7 +117,7 @@ ifeq ($(SOURCES),) endif ifeq ($(NO_LUA),true) - SOURCES := $(filter-out ./src/MoonP/moonplus.cpp, $(SOURCES)) + SOURCES := $(filter-out ./src/yuescript/yue.cpp, $(SOURCES)) endif # Set the object file names, with the source directory stripped @@ -199,7 +199,7 @@ endif @echo -n "Total build time: " @$(END_TIME) -$(BUILD_PATH)/moonp.so: src/MoonP/ast.cpp src/MoonP/moon_compiler.cpp src/MoonP/moon_parser.cpp src/MoonP/moonplus.cpp src/MoonP/parser.cpp +$(BUILD_PATH)/yue.so: src/yuescript/ast.cpp src/yuescript/yue_compiler.cpp src/yuescript/yue_parser.cpp src/yuescript/yue.cpp src/yuescript/parser.cpp $(CMD_PREFIX)$(CXX) $(CXXFLAGS) -I $(SRC_PATH) -I $(LUAI) -L $(LUAL) -llua -o $@ -fPIC -shared $? # Standard, non-optimized release build @@ -211,10 +211,10 @@ else @echo "Beginning release build" endif @$(START_TIME) - @$(MAKE) $(BUILD_PATH)/moonp.so --no-print-directory + @$(MAKE) $(BUILD_PATH)/yue.so --no-print-directory @echo -n "Total build time: " @$(END_TIME) - @mv $(BUILD_PATH)/moonp.so $(BIN_PATH)/moonp.so + @mv $(BUILD_PATH)/yue.so $(BIN_PATH)/yue.so # Create the directories used in the build .PHONY: dirs @@ -248,13 +248,13 @@ clean: @echo "Deleting generated Lua codes" @$(RM) -r $(TEST_OUTPUT) -# Test Moonscript compiler +# Test Yuescript compiler .PHONY: test -test: debug - @echo "Compiling Moonscript codes..." +test: release + @echo "Compiling Yuescript codes..." @$(START_TIME) @./$(BIN_NAME) $(TEST_INPUT) -t $(TEST_OUTPUT) -tl_enabled=true - @./$(BIN_NAME) $(TEST_INPUT)/teal-lang.mp -o $(TEST_OUTPUT)/teal-lang.lua + @./$(BIN_NAME) $(TEST_INPUT)/teal-lang.yue -o $(TEST_OUTPUT)/teal-lang.lua @echo -en "Compile time: " @$(END_TIME) diff --git a/moonplus-dev-1.rockspec b/moonplus-dev-1.rockspec deleted file mode 100644 index 40db89b..0000000 --- a/moonplus-dev-1.rockspec +++ /dev/null @@ -1,33 +0,0 @@ -rockspec_format = "3.0" -package = "MoonPlus" -version = "dev-1" -source = { - url = "git+https://github.com/pigpigyyy/MoonPlus" -} -description = { - summary = "MoonPlus is a compiler for Moonscript written in C++.", - detailed = [[ - MoonPlus is a compiler with features from Moonscript language 0.5.0 and implementing new features to make Moonscript more up to date. ]], - homepage = "https://github.com/pigpigyyy/MoonPlus", - maintainer = "Li Jin ", - labels = {"moonscript","cpp","transpiler"}, - license = "MIT" -} -dependencies = { - "lua >= 5.1", -} -build = { - type = "cmake", - variables = { - LUA_INCDIR="$(LUA_INCDIR)", - LUA="$(LUA)", - }, - install = { - lib = { - "build.luarocks/moonp.so" - }, - bin = { - "build.luarocks/moonp" - } - } -} diff --git a/spec/inputs/ambiguous.mp b/spec/inputs/ambiguous.mp deleted file mode 100644 index d147e8e..0000000 --- a/spec/inputs/ambiguous.mp +++ /dev/null @@ -1,26 +0,0 @@ -import bind from grasp -(bind stmt) color: "Red" - -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 - -macro f = (func,arg)-> "(#{func}) #{arg}" -for i = 1, 10 - a = -> - $f print, 1 - a = f - $f print, 2 - if cond - $f print, 3 - ::abc:: - (print) 4 - goto abc - (print) 5 -nil - diff --git a/spec/inputs/ambiguous.yue b/spec/inputs/ambiguous.yue new file mode 100644 index 0000000..d147e8e --- /dev/null +++ b/spec/inputs/ambiguous.yue @@ -0,0 +1,26 @@ +import bind from grasp +(bind stmt) color: "Red" + +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 + +macro f = (func,arg)-> "(#{func}) #{arg}" +for i = 1, 10 + a = -> + $f print, 1 + a = f + $f print, 2 + if cond + $f print, 3 + ::abc:: + (print) 4 + goto abc + (print) 5 +nil + diff --git a/spec/inputs/assign.mp b/spec/inputs/assign.mp deleted file mode 100644 index dac2ba3..0000000 --- a/spec/inputs/assign.mp +++ /dev/null @@ -1,33 +0,0 @@ - -_ = -> - joop = 2302 - - (hi) -> - d = 100 - hi = 1021 - - a,b,c,d = 1,2,3,4 - - hello[232], (5+5)[121], hello, x[99] = 100, 200, 300 - - joop = 12 - -joop = 2345 - -a, b = if hello - "hello" -else - "nothing", "yeah" - - -a, b = if hello - if yeah then "one", "two" else "mmhh" -else - print "the other" - "nothing", "yeah" - -c, d = 1, 2 if true - -x = (do - f! - 123) if f = getHandler! diff --git a/spec/inputs/assign.yue b/spec/inputs/assign.yue new file mode 100644 index 0000000..dac2ba3 --- /dev/null +++ b/spec/inputs/assign.yue @@ -0,0 +1,33 @@ + +_ = -> + joop = 2302 + + (hi) -> + d = 100 + hi = 1021 + + a,b,c,d = 1,2,3,4 + + hello[232], (5+5)[121], hello, x[99] = 100, 200, 300 + + joop = 12 + +joop = 2345 + +a, b = if hello + "hello" +else + "nothing", "yeah" + + +a, b = if hello + if yeah then "one", "two" else "mmhh" +else + print "the other" + "nothing", "yeah" + +c, d = 1, 2 if true + +x = (do + f! + 123) if f = getHandler! diff --git a/spec/inputs/attrib.mp b/spec/inputs/attrib.mp deleted file mode 100644 index 7e9a42c..0000000 --- a/spec/inputs/attrib.mp +++ /dev/null @@ -1,40 +0,0 @@ -do - close a, b = setmetatable {},__close:=> print "closed" - const c, d = 123, 'abc' - - close a, b - const c, d - -do - close v = if flag - func! - else - setmetatable {},__close:=> - - close f = with io.open "file.txt" - \write "Hello" - -macro defer = (item)-> "close _ = #{item}" -macro defer_f = (func)-> "close _ = setmetatable {},__close:#{func}" - -do - $defer with io.open "file.txt" - \write "Hello" - - $defer setmetatable {},__close:=> print "second" - - $defer_f -> print "first" - -_defers = setmetatable {},__close:=> - @[#@]! - @[#@] = nil - -macro defer_i = (item)-> " -_defers[#_defers + 1] = #{item} -close _ = _defers" - -do - $defer_i -> print 3 - $defer_i -> print 2 - $defer_i -> print 1 - diff --git a/spec/inputs/attrib.yue b/spec/inputs/attrib.yue new file mode 100644 index 0000000..7e9a42c --- /dev/null +++ b/spec/inputs/attrib.yue @@ -0,0 +1,40 @@ +do + close a, b = setmetatable {},__close:=> print "closed" + const c, d = 123, 'abc' + + close a, b + const c, d + +do + close v = if flag + func! + else + setmetatable {},__close:=> + + close f = with io.open "file.txt" + \write "Hello" + +macro defer = (item)-> "close _ = #{item}" +macro defer_f = (func)-> "close _ = setmetatable {},__close:#{func}" + +do + $defer with io.open "file.txt" + \write "Hello" + + $defer setmetatable {},__close:=> print "second" + + $defer_f -> print "first" + +_defers = setmetatable {},__close:=> + @[#@]! + @[#@] = nil + +macro defer_i = (item)-> " +_defers[#_defers + 1] = #{item} +close _ = _defers" + +do + $defer_i -> print 3 + $defer_i -> print 2 + $defer_i -> print 1 + diff --git a/spec/inputs/backcall.mp b/spec/inputs/backcall.mp deleted file mode 100644 index 0d52f3f..0000000 --- a/spec/inputs/backcall.mp +++ /dev/null @@ -1,139 +0,0 @@ - -{"abc", 123, 998} |> foreach print - -{1,2,3} - |> map (x)-> x * 2 - |> filter (x)-> x > 4 - |> reduce 0, (a,b)-> a + b - |> print - -[i |> tostring for i = 0,10] |> table.concat(",") |> print - -b = 1 + 2 + (4 |> tostring |> print(1) or 123) - -if x = 233 |> math.max 998 - print x - -with b |> create? "new" - .value = 123 - print \work! - -123 |> f? - -"abc" |> f1? |> f2? - -c = "abc" |> f1? |> f2? - -f = -> - arg |> x.y?\if - -998 |> func2 "abc", 233 |> func0 |> func1 -998 |> func0("abc", 233) |> func1 |> func2 - -1 |> f 2, 3, 4, 5 -val(2) |> f 1, _, 3, 4, 5 -arr[3] |> f 1, 2, _, 4, 5 - -a = {"1","2","3"} |> table.concat("") |> tonumber |> f1(1, 2, 3, _) |> f2(1, _, 3) - -readFile "example.txt" - |> extract _, language, {} - |> parse _, language - |> emit - |> render - |> print - -123 |> not func! |> f - -do - _1 = list{"abc","xyz","123"}\map"#"\value! - |> -func! - |> abc 123, _, "x" - - global _2,_3,_4 = 1,2,3 - |> f - - local _5 = v |> f1 1 - |> f2 2 - |> f3 3 - |> f4 4 - -do - (x)<- map {1,2,3} - x * 2 - -do - (x)<- map _,{1,2,3} - x * 2 - -do - (x)<- filter _, do - (x)<- map _,{1,2,3,4} - x * 2 - x > 2 - -do - (data)<- http?.get "ajaxtest" - body[".result"]\html data - (processed)<- http.post "ajaxprocess", data - body[".result"]\append processed - <- setTimeout 1000 - print "done" - -do - <- syncStatus - (err,data="nil")<- loadAsync "file.moon" - if err - print err - return - (codes)<- compileAsync data - func = loadstring codes - func! - -do - <- f1 - <- f2 - do - <- f3 - <- f4 - <- f5 - <- f6 - f7! - -do - :result,:msg = do - (data)<- receiveAsync "filename.txt" - print data - (info)<- processAsync data - check info - print result,msg - - totalSize = (for file in *files - (data)<- loadAsync file - addToCache file,data) |> reduce 0,(a,b)-> a+b - -propA = do - (value)<= property => @_value - print "old value: #{@_value}" - print "new value: #{value}" - @_value = value - -propB = do - <= property _, (value)=> - print "old value: #{@_value}" - print "new value: #{value}" - @_value = value - @_value - -alert "hi" - -x = 123 |> a |> b or 456 |> c |> d or a.if\then("abc") or a?.b\c?(123) or x\y - -x1 = 3 * -4 |> f - -x2 = 3 * -2 ^ 2 |> f - -y = 1 + not # 2 |> (a ^ c) |> b(3,_) * 4 ^ -123 |> f |> f1 or 123 - -nil - diff --git a/spec/inputs/backcall.yue b/spec/inputs/backcall.yue new file mode 100644 index 0000000..bad3a56 --- /dev/null +++ b/spec/inputs/backcall.yue @@ -0,0 +1,153 @@ + +{"abc", 123, 998} |> foreach print + +{1,2,3} + |> map (x)-> x * 2 + |> filter (x)-> x > 4 + |> reduce 0, (a,b)-> a + b + |> print + +[i |> tostring for i = 0,10] |> table.concat(",") |> print + +b = 1 + 2 + (4 |> tostring |> print(1) or 123) + +if x = 233 |> math.max 998 + print x + +with b |> create? "new" + .value = 123 + print \work! + +123 |> f? + +"abc" |> f1? |> f2? + +c = "abc" |> f1? |> f2? + +f = -> + arg |> x.y?\if + +998 |> func2 "abc", 233 |> func0 |> func1 +998 |> func0("abc", 233) |> func1 |> func2 + +1 |> f 2, 3, 4, 5 +val(2) |> f 1, _, 3, 4, 5 +arr[3] |> f 1, 2, _, 4, 5 + +a = {"1","2","3"} |> table.concat("") |> tonumber |> f1(1, 2, 3, _) |> f2(1, _, 3) + +readFile("example.txt") |> + extract(_, language, {}) |> + parse(_, language) |> + emit |> + render |> + print + +readFile("example.txt") \ + |> extract(_, language, {}) \ + |> parse(_, language) \ + |> emit \ + |> render \ + |> print + +readFile "example.txt" + |> extract _, language, {} + |> parse _, language + |> emit + |> render + |> print + +123 |> not func! |> f + +do + _1 = list{"abc","xyz","123"}\map"#"\value! + |> -func! + |> abc 123, _, "x" + + global _2,_3,_4 = 1,2,3 + |> f + + local _5 = v |> f1 1 + |> f2 2 + |> f3 3 + |> f4 4 + +do + (x)<- map {1,2,3} + x * 2 + +do + (x)<- map _,{1,2,3} + x * 2 + +do + (x)<- filter _, do + (x)<- map _,{1,2,3,4} + x * 2 + x > 2 + +do + (data)<- http?.get "ajaxtest" + body[".result"]\html data + (processed)<- http.post "ajaxprocess", data + body[".result"]\append processed + <- setTimeout 1000 + print "done" + +do + <- syncStatus + (err,data="nil")<- loadAsync "file.moon" + if err + print err + return + (codes)<- compileAsync data + func = loadstring codes + func! + +do + <- f1 + <- f2 + do + <- f3 + <- f4 + <- f5 + <- f6 + f7! + +do + :result,:msg = do + (data)<- receiveAsync "filename.txt" + print data + (info)<- processAsync data + check info + print result,msg + + totalSize = (for file in *files + (data)<- loadAsync file + addToCache file,data) |> reduce 0,(a,b)-> a+b + +propA = do + (value)<= property => @_value + print "old value: #{@_value}" + print "new value: #{value}" + @_value = value + +propB = do + <= property _, (value)=> + print "old value: #{@_value}" + print "new value: #{value}" + @_value = value + @_value + +alert "hi" + +x = 123 |> a |> b or 456 |> c |> d or a.if\then("abc") or a?.b\c?(123) or x\y + +x1 = 3 * -4 |> f + +x2 = 3 * -2 ^ 2 |> f + +y = 1 + not # 2 |> (a ^ c) |> b(3,_) * 4 ^ -123 |> f |> f1 or 123 + +nil + diff --git a/spec/inputs/bubbling.mp b/spec/inputs/bubbling.mp deleted file mode 100644 index 23a85d4..0000000 --- a/spec/inputs/bubbling.mp +++ /dev/null @@ -1,27 +0,0 @@ - --- vararg bubbling -f = (...) -> #{...} - -dont_bubble = -> - [x for x in ((...)-> print ...)("hello")] - -k = [x for x in ((...)-> print ...)("hello")] - -j = for i=1,10 - (...) -> print ... - --- bubble me - -m = (...) -> - [x for x in *{...} when f(...) > 4] - -_ = (...)-> - x = for i in *{...} do i - y = [x for x in *{...}] - z = [x for x in hallo when f(...) > 4] - - a = for i=1,10 do ... - - b = for i=1,10 - (...)-> print ... - diff --git a/spec/inputs/bubbling.yue b/spec/inputs/bubbling.yue new file mode 100644 index 0000000..23a85d4 --- /dev/null +++ b/spec/inputs/bubbling.yue @@ -0,0 +1,27 @@ + +-- vararg bubbling +f = (...) -> #{...} + +dont_bubble = -> + [x for x in ((...)-> print ...)("hello")] + +k = [x for x in ((...)-> print ...)("hello")] + +j = for i=1,10 + (...) -> print ... + +-- bubble me + +m = (...) -> + [x for x in *{...} when f(...) > 4] + +_ = (...)-> + x = for i in *{...} do i + y = [x for x in *{...}] + z = [x for x in hallo when f(...) > 4] + + a = for i=1,10 do ... + + b = for i=1,10 + (...)-> print ... + diff --git a/spec/inputs/class.mp b/spec/inputs/class.mp deleted file mode 100644 index ca8b58c..0000000 --- a/spec/inputs/class.mp +++ /dev/null @@ -1,232 +0,0 @@ - -class Hello - new: (@test, @world) => - print "creating object.." - hello: => - print @test, @world - __tostring: => "hello world" - -x = Hello 1,2 -x\hello() - -print x - -class Simple - cool: => print "cool" - -class Yikes extends Simple - new: => print "created hello" - -x = Yikes() -x\cool() - - -class Hi - new: (arg) => - print "init arg", arg - - cool: (num) => - print "num", num - - -class Simple extends Hi - new: => super "man" - cool: => super 120302 - -x = Simple() -x\cool() - -print x.__class == Simple - - -class Okay - -- what is going on - something: 20323 - -- yeaha - - -class Biggie extends Okay - something: => - super 1,2,3,4 - super.something another_self, 1,2,3,4 - assert super == Okay - - -class Yeah - okay: => - super\something 1,2,3,4 - - -class What - something: => print "val:", @val - -class Hello extends What - val: 2323 - something: => super\something - -with Hello! - x = \something! - print x - x! - -class CoolSuper - hi: => - super(1,2,3,4) 1,2,3,4 - super.something 1,2,3,4 - _ = super.something(1,2,3,4).world - super\yeah"world".okay hi, hi, hi - _ = something.super - _ = super.super.super.super - _ = super\hello - nil - - --- selfing -x = @hello -x = @@hello - -@hello "world" -@@hello "world" - -@@one @@two(4,5) @three, @four - -xx = (@hello, @@world, cool) -> - - --- class properties -class ClassMan - @yeah: 343 - blue: => - @hello: 3434, @world: 23423 - green: => - @red: => - - -x = @ -y = @@ - -@ something - -@@ something - -@ = @ + @ / @ - -@ = 343 -@.hello 2,3,4 - -_ = hello[@].world - - -class Whacko - _ = @hello - if something - print "hello world" - - hello = "world" - @another = "day" - - print "yeah" if something -- this is briken - - -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 - --- - -global ^ -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 Wowha extends Thing - @butt: -> - super! - _ = super.hello - super\hello! - super\hello - - - @zone: cool { - -> - super! - _ = super.hello - super\hello! - super\hello - } - -do - class Test - new: => @@if = true - @do: => 1 - test: => @@if and @@do! - test = Test! - test\test! - -do - class Test - new: => @if = true - do: => 1 - test: => @if and @do! - test = Test! - test\test! - -class extends lapis.Application - "/": => json: { status: true } - -nil diff --git a/spec/inputs/class.yue b/spec/inputs/class.yue new file mode 100644 index 0000000..ca8b58c --- /dev/null +++ b/spec/inputs/class.yue @@ -0,0 +1,232 @@ + +class Hello + new: (@test, @world) => + print "creating object.." + hello: => + print @test, @world + __tostring: => "hello world" + +x = Hello 1,2 +x\hello() + +print x + +class Simple + cool: => print "cool" + +class Yikes extends Simple + new: => print "created hello" + +x = Yikes() +x\cool() + + +class Hi + new: (arg) => + print "init arg", arg + + cool: (num) => + print "num", num + + +class Simple extends Hi + new: => super "man" + cool: => super 120302 + +x = Simple() +x\cool() + +print x.__class == Simple + + +class Okay + -- what is going on + something: 20323 + -- yeaha + + +class Biggie extends Okay + something: => + super 1,2,3,4 + super.something another_self, 1,2,3,4 + assert super == Okay + + +class Yeah + okay: => + super\something 1,2,3,4 + + +class What + something: => print "val:", @val + +class Hello extends What + val: 2323 + something: => super\something + +with Hello! + x = \something! + print x + x! + +class CoolSuper + hi: => + super(1,2,3,4) 1,2,3,4 + super.something 1,2,3,4 + _ = super.something(1,2,3,4).world + super\yeah"world".okay hi, hi, hi + _ = something.super + _ = super.super.super.super + _ = super\hello + nil + + +-- selfing +x = @hello +x = @@hello + +@hello "world" +@@hello "world" + +@@one @@two(4,5) @three, @four + +xx = (@hello, @@world, cool) -> + + +-- class properties +class ClassMan + @yeah: 343 + blue: => + @hello: 3434, @world: 23423 + green: => + @red: => + + +x = @ +y = @@ + +@ something + +@@ something + +@ = @ + @ / @ + +@ = 343 +@.hello 2,3,4 + +_ = hello[@].world + + +class Whacko + _ = @hello + if something + print "hello world" + + hello = "world" + @another = "day" + + print "yeah" if something -- this is briken + + +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 + +-- + +global ^ +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 Wowha extends Thing + @butt: -> + super! + _ = super.hello + super\hello! + super\hello + + + @zone: cool { + -> + super! + _ = super.hello + super\hello! + super\hello + } + +do + class Test + new: => @@if = true + @do: => 1 + test: => @@if and @@do! + test = Test! + test\test! + +do + class Test + new: => @if = true + do: => 1 + test: => @if and @do! + test = Test! + test\test! + +class extends lapis.Application + "/": => json: { status: true } + +nil diff --git a/spec/inputs/comprehension.mp b/spec/inputs/comprehension.mp deleted file mode 100644 index b6adb94..0000000 --- a/spec/inputs/comprehension.mp +++ /dev/null @@ -1,52 +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}} } - - --- - -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]]] - -list = [item?\invoke 123 for item in items] - -nil diff --git a/spec/inputs/comprehension.yue b/spec/inputs/comprehension.yue new file mode 100644 index 0000000..b6adb94 --- /dev/null +++ b/spec/inputs/comprehension.yue @@ -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]]] + +list = [item?\invoke 123 for item in items] + +nil diff --git a/spec/inputs/cond.mp b/spec/inputs/cond.mp deleted file mode 100644 index 3dee99b..0000000 --- a/spec/inputs/cond.mp +++ /dev/null @@ -1,190 +0,0 @@ - -you_cool = false - -_ = if cool - if you_cool - one - else if eatdic - yeah - else - _ = two - three -else - no - -_ = if cool then no -_ = if cool then no else yes - -if cool then wow cool else - noso cool - -if working - _ = if cool then if cool then okay else what else nah - - -if yeah then no day elseif cool me then okay ya else u way -if yeah then no dad else if cool you then okay bah else p way - - -if (->)() then what ever - -if nil then flip me else - it be,rad - - -if things great then no way elseif okay sure - what here - - -if things then no chance -elseif okay - what now - - -if things - yes man -elseif okay person then hi there else hmm sure - -if lets go - print "greetings" -elseif "just us" - print "will smith" else show 5555555 - --- - -if something = 10 - print something -else - print "else" - -hello = if something = 10 - print something -else - print "else" - - -hello = 5 + if something = 10 - print something - ---- - -z = false - -_ = if false - one -elseif x = true - two -elseif z = true - three -else - four - - -out = if false - one -elseif x = true - two -elseif z = true - three -else - four - -kzy = -> - if something = true - 1 - elseif another = false - 2 - ---- - -unless true - print "cool!" - -unless true and false - print "cool!" - -unless false then print "cool!" -unless false then print "cool!" else print "no way!" - -unless nil - print "hello" -else - print "world" - --- - -x = unless true - print "cool!" - -x = unless true and false - print "cool!" - -y = unless false then print "cool!" -y = unless false then print "cool!" else print "no way!" - -z = unless nil - print "hello" -else - print "world" - -print unless true - print "cool!" - -print unless true and false - print "cool!" - -print unless false then print "cool!" -print unless false then print "cool!" else print "no way!" - -print unless nil - print "hello" -else - print "world" - --- - -print "hello" unless value - -dddd = {1,2,3} unless value - - --- - -do - j = 100 - unless j = hi! - error "not j!" - ----------------- - -a = 12 -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/cond.yue b/spec/inputs/cond.yue new file mode 100644 index 0000000..3dee99b --- /dev/null +++ b/spec/inputs/cond.yue @@ -0,0 +1,190 @@ + +you_cool = false + +_ = if cool + if you_cool + one + else if eatdic + yeah + else + _ = two + three +else + no + +_ = if cool then no +_ = if cool then no else yes + +if cool then wow cool else + noso cool + +if working + _ = if cool then if cool then okay else what else nah + + +if yeah then no day elseif cool me then okay ya else u way +if yeah then no dad else if cool you then okay bah else p way + + +if (->)() then what ever + +if nil then flip me else + it be,rad + + +if things great then no way elseif okay sure + what here + + +if things then no chance +elseif okay + what now + + +if things + yes man +elseif okay person then hi there else hmm sure + +if lets go + print "greetings" +elseif "just us" + print "will smith" else show 5555555 + +-- + +if something = 10 + print something +else + print "else" + +hello = if something = 10 + print something +else + print "else" + + +hello = 5 + if something = 10 + print something + +--- + +z = false + +_ = if false + one +elseif x = true + two +elseif z = true + three +else + four + + +out = if false + one +elseif x = true + two +elseif z = true + three +else + four + +kzy = -> + if something = true + 1 + elseif another = false + 2 + +--- + +unless true + print "cool!" + +unless true and false + print "cool!" + +unless false then print "cool!" +unless false then print "cool!" else print "no way!" + +unless nil + print "hello" +else + print "world" + +-- + +x = unless true + print "cool!" + +x = unless true and false + print "cool!" + +y = unless false then print "cool!" +y = unless false then print "cool!" else print "no way!" + +z = unless nil + print "hello" +else + print "world" + +print unless true + print "cool!" + +print unless true and false + print "cool!" + +print unless false then print "cool!" +print unless false then print "cool!" else print "no way!" + +print unless nil + print "hello" +else + print "world" + +-- + +print "hello" unless value + +dddd = {1,2,3} unless value + + +-- + +do + j = 100 + unless j = hi! + error "not j!" + +---------------- + +a = 12 +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.mp b/spec/inputs/destructure.mp deleted file mode 100644 index 49e6393..0000000 --- a/spec/inputs/destructure.mp +++ /dev/null @@ -1,117 +0,0 @@ - -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 - -do - {function:{end:endVar}} = thing - -do - {if:{a,b,c}} = thing - -do - {:a, :b} = {a: "Hello", b: "World"} if true - - {days, hours, mins, secs} = [tonumber a for a in *{ - string.match "1 2 3 4", "(.+)%s(.+)%s(.+)%s(.+)" - }] - - {:one, :two, :three} = {w,true for w in foo\gmatch("%S+")} - - {:a},b = a\if(123) + t, 123 - diff --git a/spec/inputs/destructure.yue b/spec/inputs/destructure.yue new file mode 100644 index 0000000..49e6393 --- /dev/null +++ b/spec/inputs/destructure.yue @@ -0,0 +1,117 @@ + +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 + +do + {function:{end:endVar}} = thing + +do + {if:{a,b,c}} = thing + +do + {:a, :b} = {a: "Hello", b: "World"} if true + + {days, hours, mins, secs} = [tonumber a for a in *{ + string.match "1 2 3 4", "(.+)%s(.+)%s(.+)%s(.+)" + }] + + {:one, :two, :three} = {w,true for w in foo\gmatch("%S+")} + + {:a},b = a\if(123) + t, 123 + diff --git a/spec/inputs/do.mp b/spec/inputs/do.mp deleted file mode 100644 index 2fbcbb9..0000000 --- a/spec/inputs/do.mp +++ /dev/null @@ -1,27 +0,0 @@ - -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/do.yue b/spec/inputs/do.yue new file mode 100644 index 0000000..2fbcbb9 --- /dev/null +++ b/spec/inputs/do.yue @@ -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/existential.mp b/spec/inputs/existential.mp deleted file mode 100644 index 3055705..0000000 --- a/spec/inputs/existential.mp +++ /dev/null @@ -1,51 +0,0 @@ - -f1?! - -f2? "arg0",123 - -x = tab?.value - -print abc?["hello world"]?.xyz - -if print and x? - print x - -@?\func 998 - -with abc?!\func?! - if \p? "abc" - return 123 - -if {:x} = a?.if?\then?(123)? @?\function 998 - print x - -res = b.function\do!\while?("OK")\if("def",998)\f? -print res - -solipsism = true if mind? and not world? - -speed = 0 -speed or= 15 - -footprints = yeti or "bear" - -major = 'Computer Science' - -unless major? - signUpForClass 'Introduction to Wines' - -if window? - environment = 'browser (probably)' - -zip = lottery.drawWinner?!.address?.zipcode - -len = utf8?.len or string?.len or (o) -> #o - -a = tb1?\end? 123 + tb2?\then 456 - -b = tb1?\end? or tb2?\then - -with? io.open "test.txt", "w" - \write "hello" - \close! - diff --git a/spec/inputs/existential.yue b/spec/inputs/existential.yue new file mode 100644 index 0000000..3055705 --- /dev/null +++ b/spec/inputs/existential.yue @@ -0,0 +1,51 @@ + +f1?! + +f2? "arg0",123 + +x = tab?.value + +print abc?["hello world"]?.xyz + +if print and x? + print x + +@?\func 998 + +with abc?!\func?! + if \p? "abc" + return 123 + +if {:x} = a?.if?\then?(123)? @?\function 998 + print x + +res = b.function\do!\while?("OK")\if("def",998)\f? +print res + +solipsism = true if mind? and not world? + +speed = 0 +speed or= 15 + +footprints = yeti or "bear" + +major = 'Computer Science' + +unless major? + signUpForClass 'Introduction to Wines' + +if window? + environment = 'browser (probably)' + +zip = lottery.drawWinner?!.address?.zipcode + +len = utf8?.len or string?.len or (o) -> #o + +a = tb1?\end? 123 + tb2?\then 456 + +b = tb1?\end? or tb2?\then + +with? io.open "test.txt", "w" + \write "hello" + \close! + diff --git a/spec/inputs/export.mp b/spec/inputs/export.mp deleted file mode 100644 index 085510e..0000000 --- a/spec/inputs/export.mp +++ /dev/null @@ -1,79 +0,0 @@ - -export a,b,c = 223, 343, 123 -export cool = "dad" - -d,e,f = 3, 2, 1 -export d, e, f - -export class Something - umm: "cool" - -export if this - 232 -else - 4343 - -export What = if this - 232 -else - 4343 - -export y = -> - hallo = 3434 - -export with tmp - j = 2000 - -export cbVal = do - h = 100 - (x)<- f - return x h - -export y = -> - h = 100 - k = 100 - -export switch h - when 100, 150 then 200 - when 200 then 300 - else 0 - -export Constant = switch value - when "good" then 1 - when "better" then 2 - when "best" then 3 - -export item = 123 - |> func - -export x - -f if a then b -f do 123 -f switch a - when b then c -f [i for i = 1,10] -f for i = 1,10 do i -f {k,v for k,v in pairs tb} -f for k,v in pairs tb do k,v -f while a do true -f with a - .b = 123 -f a?.b -f a\b -f class A - -_ = "#{if a then b}" -_ = "#{do 123}" -_ = "#{switch a - when b then c}" -_ = "#{[i for i = 1,10]}" -_ = "#{for i = 1,10 do i}" -_ = "#{{k,v for k,v in pairs tb}}" -_ = "#{for k,v in pairs tb do k,v}" -_ = "#{while a do true}" -_ = "#{with a - .b = 123}" -_ = "#{a?.b}" -_ = "#{a\b}" -_ = "#{class A}" diff --git a/spec/inputs/export.yue b/spec/inputs/export.yue new file mode 100644 index 0000000..085510e --- /dev/null +++ b/spec/inputs/export.yue @@ -0,0 +1,79 @@ + +export a,b,c = 223, 343, 123 +export cool = "dad" + +d,e,f = 3, 2, 1 +export d, e, f + +export class Something + umm: "cool" + +export if this + 232 +else + 4343 + +export What = if this + 232 +else + 4343 + +export y = -> + hallo = 3434 + +export with tmp + j = 2000 + +export cbVal = do + h = 100 + (x)<- f + return x h + +export y = -> + h = 100 + k = 100 + +export switch h + when 100, 150 then 200 + when 200 then 300 + else 0 + +export Constant = switch value + when "good" then 1 + when "better" then 2 + when "best" then 3 + +export item = 123 + |> func + +export x + +f if a then b +f do 123 +f switch a + when b then c +f [i for i = 1,10] +f for i = 1,10 do i +f {k,v for k,v in pairs tb} +f for k,v in pairs tb do k,v +f while a do true +f with a + .b = 123 +f a?.b +f a\b +f class A + +_ = "#{if a then b}" +_ = "#{do 123}" +_ = "#{switch a + when b then c}" +_ = "#{[i for i = 1,10]}" +_ = "#{for i = 1,10 do i}" +_ = "#{{k,v for k,v in pairs tb}}" +_ = "#{for k,v in pairs tb do k,v}" +_ = "#{while a do true}" +_ = "#{with a + .b = 123}" +_ = "#{a?.b}" +_ = "#{a\b}" +_ = "#{class A}" diff --git a/spec/inputs/export_default.mp b/spec/inputs/export_default.mp deleted file mode 100644 index 73f43ac..0000000 --- a/spec/inputs/export_default.mp +++ /dev/null @@ -1,15 +0,0 @@ - -print "OK" - -export default -> - print "hello" - 123 - -(x, fy)<- f 123, "abc" unless isOff -print x -(y, res)<- fy -return if res - abc + y -else - abc - diff --git a/spec/inputs/export_default.yue b/spec/inputs/export_default.yue new file mode 100644 index 0000000..73f43ac --- /dev/null +++ b/spec/inputs/export_default.yue @@ -0,0 +1,15 @@ + +print "OK" + +export default -> + print "hello" + 123 + +(x, fy)<- f 123, "abc" unless isOff +print x +(y, res)<- fy +return if res + abc + y +else + abc + diff --git a/spec/inputs/funcs.mp b/spec/inputs/funcs.mp deleted file mode 100644 index 0e45ff1..0000000 --- a/spec/inputs/funcs.mp +++ /dev/null @@ -1,161 +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 - --- - -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" - - -@ = (n)-> - return 1 if n == 0 - n * @(n-1) - -nil diff --git a/spec/inputs/funcs.yue b/spec/inputs/funcs.yue new file mode 100644 index 0000000..0e45ff1 --- /dev/null +++ b/spec/inputs/funcs.yue @@ -0,0 +1,161 @@ + + +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" + + +@ = (n)-> + return 1 if n == 0 + n * @(n-1) + +nil diff --git a/spec/inputs/global.mp b/spec/inputs/global.mp deleted file mode 100644 index 7d6cfde..0000000 --- a/spec/inputs/global.mp +++ /dev/null @@ -1,77 +0,0 @@ - -do - global a,b,c = 223, 343 - global cool = "dad" - -do - global class Something - umm: "cool" - -do - global a,b,c - a,b,c,d = "hello" - - -do - What = if this - 232 - else - 4343 - - global ^ - - another = 3434 - Another = 7890 - - if inner then Yeah = "10000" - - What = if this - 232 - else - 4343 - - -do - global * - - What = if this - 232 - else - 4343 - - x,y,z = 1,2,3 - - y = -> - hallo = 3434 - - with tmp - j = 2000 - - -do - global * - x = 3434 - if y then - x = 10 - -do - global * - if y then - x = 10 - x = 3434 - -do - do - global * - - k = 1212 - - do - h = 100 - - y = -> - h = 100 - k = 100 - - h = 100 - diff --git a/spec/inputs/global.yue b/spec/inputs/global.yue new file mode 100644 index 0000000..7d6cfde --- /dev/null +++ b/spec/inputs/global.yue @@ -0,0 +1,77 @@ + +do + global a,b,c = 223, 343 + global cool = "dad" + +do + global class Something + umm: "cool" + +do + global a,b,c + a,b,c,d = "hello" + + +do + What = if this + 232 + else + 4343 + + global ^ + + another = 3434 + Another = 7890 + + if inner then Yeah = "10000" + + What = if this + 232 + else + 4343 + + +do + global * + + What = if this + 232 + else + 4343 + + x,y,z = 1,2,3 + + y = -> + hallo = 3434 + + with tmp + j = 2000 + + +do + global * + x = 3434 + if y then + x = 10 + +do + global * + if y then + x = 10 + x = 3434 + +do + do + global * + + k = 1212 + + do + h = 100 + + y = -> + h = 100 + k = 100 + + h = 100 + diff --git a/spec/inputs/goto.mp b/spec/inputs/goto.mp deleted file mode 100644 index 1197251..0000000 --- a/spec/inputs/goto.mp +++ /dev/null @@ -1,41 +0,0 @@ -do - a = 0 - ::start:: - a += 1 - goto done if a == 5 - goto start - ::done:: - -do - for z = 1, 10 do for y = 1, 10 do for x = 1, 10 - if x^2 + y^2 == z^2 - print 'found a Pythagorean triple:', x, y, z - goto done - ::done:: - -do - for z = 1, 10 - for y = 1, 10 do for x = 1, 10 - if x^2 + y^2 == z^2 - print 'found a Pythagorean triple:', x, y, z - print 'now trying next z...' - goto zcontinue - ::zcontinue:: - -do - ::redo:: - for x = 1, 10 do for y = 1, 10 - if not f x, y then goto continue - if not g x, y then goto skip - if not h x, y then goto redo - ::continue:: - ::skip:: - -do - for x in *t - if x % 2 == 0 - print 'list has even number' - goto has - print 'list lacks even number' - ::has:: - diff --git a/spec/inputs/goto.yue b/spec/inputs/goto.yue new file mode 100644 index 0000000..1197251 --- /dev/null +++ b/spec/inputs/goto.yue @@ -0,0 +1,41 @@ +do + a = 0 + ::start:: + a += 1 + goto done if a == 5 + goto start + ::done:: + +do + for z = 1, 10 do for y = 1, 10 do for x = 1, 10 + if x^2 + y^2 == z^2 + print 'found a Pythagorean triple:', x, y, z + goto done + ::done:: + +do + for z = 1, 10 + for y = 1, 10 do for x = 1, 10 + if x^2 + y^2 == z^2 + print 'found a Pythagorean triple:', x, y, z + print 'now trying next z...' + goto zcontinue + ::zcontinue:: + +do + ::redo:: + for x = 1, 10 do for y = 1, 10 + if not f x, y then goto continue + if not g x, y then goto skip + if not h x, y then goto redo + ::continue:: + ::skip:: + +do + for x in *t + if x % 2 == 0 + print 'list has even number' + goto has + print 'list lacks even number' + ::has:: + diff --git a/spec/inputs/import.mp b/spec/inputs/import.mp deleted file mode 100644 index e075404..0000000 --- a/spec/inputs/import.mp +++ /dev/null @@ -1,68 +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] - -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 - - -do - import 'module' - import 'module_x' - import "d-a-s-h-e-s" - import "module.part" - -do - import "player" as Player - import "lpeg" as {:C, :Ct, :Cmt} - import "export" as {Something:{umm:{ch}}} - -do - global * - import 'module' - import 'module_x' - import "org.package.module-y" - -do - import "org.package.module" as {function:func, if:ifVar} diff --git a/spec/inputs/import.yue b/spec/inputs/import.yue new file mode 100644 index 0000000..e075404 --- /dev/null +++ b/spec/inputs/import.yue @@ -0,0 +1,68 @@ + + +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 + + +do + import 'module' + import 'module_x' + import "d-a-s-h-e-s" + import "module.part" + +do + import "player" as Player + import "lpeg" as {:C, :Ct, :Cmt} + import "export" as {Something:{umm:{ch}}} + +do + global * + import 'module' + import 'module_x' + import "org.package.module-y" + +do + import "org.package.module" as {function:func, if:ifVar} diff --git a/spec/inputs/lists.mp b/spec/inputs/lists.mp deleted file mode 100644 index 15eb9ab..0000000 --- a/spec/inputs/lists.mp +++ /dev/null @@ -1,72 +0,0 @@ - -hi = [x*2 for _, x in ipairs{1,2,3,4}] - -items = {1,2,3,4,5,6} - -_ = [z for z in ipairs items when z > 4] - -rad = [{a} for a in ipairs { - 1,2,3,4,5,6, -} when good_number a] - - -_ = [z for z in items for j in list when z > 4] - -require "util" - -dump = (x) -> print util.dump x - -range = (count) -> - i = 0 - return coroutine.wrap -> - while i < count - coroutine.yield i - i = i + 1 - -dump [x for x in range 10] -dump [{x, y} for x in range 5 when x > 2 for y in range 5] - -things = [x + y for x in range 10 when x > 5 for y in range 10 when y > 7] - -print x,y for x in ipairs{1,2,4} for y in ipairs{1,2,3} when x != 2 - -print "hello", x for x in items - -_ = [x for x in x] -x = [x for x in x] - -print x,y for x in ipairs{1,2,4} for y in ipairs{1,2,3} when x != 2 - -double = [x*2 for x in *items] - -print x for x in *double - -cut = [x for x in *items when x > 3] - -hello = [x + y for x in *items for y in *items] - -print z for z in *hello - - --- slice -x = {1, 2, 3, 4, 5, 6, 7} -print y for y in *x[2,-5,2] -print y for y in *x[,3] -print y for y in *x[2,] -print y for y in *x[,,2] -print y for y in *x[2,,2] - -a, b, c = 1, 5, 2 -print y for y in *x[a,b,c] - - -normal = (hello) -> - [x for x in yeah] - - -test = x 1,2,3,4,5 -print thing for thing in *test - --> a = b for row in *rows - - diff --git a/spec/inputs/lists.yue b/spec/inputs/lists.yue new file mode 100644 index 0000000..15eb9ab --- /dev/null +++ b/spec/inputs/lists.yue @@ -0,0 +1,72 @@ + +hi = [x*2 for _, x in ipairs{1,2,3,4}] + +items = {1,2,3,4,5,6} + +_ = [z for z in ipairs items when z > 4] + +rad = [{a} for a in ipairs { + 1,2,3,4,5,6, +} when good_number a] + + +_ = [z for z in items for j in list when z > 4] + +require "util" + +dump = (x) -> print util.dump x + +range = (count) -> + i = 0 + return coroutine.wrap -> + while i < count + coroutine.yield i + i = i + 1 + +dump [x for x in range 10] +dump [{x, y} for x in range 5 when x > 2 for y in range 5] + +things = [x + y for x in range 10 when x > 5 for y in range 10 when y > 7] + +print x,y for x in ipairs{1,2,4} for y in ipairs{1,2,3} when x != 2 + +print "hello", x for x in items + +_ = [x for x in x] +x = [x for x in x] + +print x,y for x in ipairs{1,2,4} for y in ipairs{1,2,3} when x != 2 + +double = [x*2 for x in *items] + +print x for x in *double + +cut = [x for x in *items when x > 3] + +hello = [x + y for x in *items for y in *items] + +print z for z in *hello + + +-- slice +x = {1, 2, 3, 4, 5, 6, 7} +print y for y in *x[2,-5,2] +print y for y in *x[,3] +print y for y in *x[2,] +print y for y in *x[,,2] +print y for y in *x[2,,2] + +a, b, c = 1, 5, 2 +print y for y in *x[a,b,c] + + +normal = (hello) -> + [x for x in yeah] + + +test = x 1,2,3,4,5 +print thing for thing in *test + +-> a = b for row in *rows + + diff --git a/spec/inputs/literals.mp b/spec/inputs/literals.mp deleted file mode 100644 index d4b0326..0000000 --- a/spec/inputs/literals.mp +++ /dev/null @@ -1,46 +0,0 @@ - -_ = { - 121 - 121.2323 - 121.2323e-1 - 121.2323e13434 - 2323E34 - 0x12323 - - 0xfF2323 - 0xabcdef - 0xABCDEF - - .2323 - .2323e-1 - .2323e13434 - - - 1LL - 1ULL - 9332LL - 9332 - 0x2aLL - 0x2aULL - - [[ hello world ]] - - [=[ hello world ]=] - [====[ hello world ]====] - - "another world" - - 'what world' - - - " - hello world - " - - 'yeah - what is going on - here is something cool' - -} -nil - diff --git a/spec/inputs/literals.yue b/spec/inputs/literals.yue new file mode 100644 index 0000000..d4b0326 --- /dev/null +++ b/spec/inputs/literals.yue @@ -0,0 +1,46 @@ + +_ = { + 121 + 121.2323 + 121.2323e-1 + 121.2323e13434 + 2323E34 + 0x12323 + + 0xfF2323 + 0xabcdef + 0xABCDEF + + .2323 + .2323e-1 + .2323e13434 + + + 1LL + 1ULL + 9332LL + 9332 + 0x2aLL + 0x2aULL + + [[ hello world ]] + + [=[ hello world ]=] + [====[ hello world ]====] + + "another world" + + 'what world' + + + " + hello world + " + + 'yeah + what is going on + here is something cool' + +} +nil + diff --git a/spec/inputs/local.mp b/spec/inputs/local.mp deleted file mode 100644 index 33251a9..0000000 --- a/spec/inputs/local.mp +++ /dev/null @@ -1,94 +0,0 @@ - -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/spec/inputs/local.yue b/spec/inputs/local.yue new file mode 100644 index 0000000..33251a9 --- /dev/null +++ b/spec/inputs/local.yue @@ -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/spec/inputs/loops.mp b/spec/inputs/loops.mp deleted file mode 100644 index 8946a2f..0000000 --- a/spec/inputs/loops.mp +++ /dev/null @@ -1,148 +0,0 @@ - -for x=1,10 - print "yeah" - -for x=1,#something - print "yeah" - -for y=100,60,-3 - print "count down", y - -for a=1,10 do print "okay" - -for a=1,10 - for b = 2,43 - print a,b - -for i in iter - for j in yeah - x = 343 + i + j - print i, j - -for x in *something - print x - -for k,v in pairs hello do print k,v - -for x in y, z - print x - -for x in y, z, k - print x - - -x = -> - for x in y - _ = y - -hello = {1,2,3,4,5} - -x = for y in *hello - if y % 2 == 0 - y - -x = -> - for x in *hello - _ = y - -t = for i=10,20 do i * 2 - -hmm = 0 -y = for j = 3,30, 8 - hmm += 1 - j * hmm - -_ = -> - for k=10,40 - _ = "okay" - -_ = -> - return for k=10,40 - "okay" - -while true do print "name" - -while 5 + 5 - print "okay world" - working man - -while also do - i work too - _ = "okay" - -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 - continue if false - print "yes" - break if true - print "no" - -for i = 1, 10 - while true - if not true - continue - break - -a = 1 -repeat - a += 1 - if a == 5 - continue - if a == 6 - break - print a -until a == 10 - -for x=1,10 - continue if x > 3 and x < 7 - print x - - -list = for x=1,10 - continue if x > 3 and x < 7 - x - - -for a in *{1,2,3,4,5,6} - continue if a == 1 - continue if a == 3 - print a - - - -for x=1,10 - continue if x % 2 == 0 - 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 - - diff --git a/spec/inputs/loops.yue b/spec/inputs/loops.yue new file mode 100644 index 0000000..8946a2f --- /dev/null +++ b/spec/inputs/loops.yue @@ -0,0 +1,148 @@ + +for x=1,10 + print "yeah" + +for x=1,#something + print "yeah" + +for y=100,60,-3 + print "count down", y + +for a=1,10 do print "okay" + +for a=1,10 + for b = 2,43 + print a,b + +for i in iter + for j in yeah + x = 343 + i + j + print i, j + +for x in *something + print x + +for k,v in pairs hello do print k,v + +for x in y, z + print x + +for x in y, z, k + print x + + +x = -> + for x in y + _ = y + +hello = {1,2,3,4,5} + +x = for y in *hello + if y % 2 == 0 + y + +x = -> + for x in *hello + _ = y + +t = for i=10,20 do i * 2 + +hmm = 0 +y = for j = 3,30, 8 + hmm += 1 + j * hmm + +_ = -> + for k=10,40 + _ = "okay" + +_ = -> + return for k=10,40 + "okay" + +while true do print "name" + +while 5 + 5 + print "okay world" + working man + +while also do + i work too + _ = "okay" + +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 + continue if false + print "yes" + break if true + print "no" + +for i = 1, 10 + while true + if not true + continue + break + +a = 1 +repeat + a += 1 + if a == 5 + continue + if a == 6 + break + print a +until a == 10 + +for x=1,10 + continue if x > 3 and x < 7 + print x + + +list = for x=1,10 + continue if x > 3 and x < 7 + x + + +for a in *{1,2,3,4,5,6} + continue if a == 1 + continue if a == 3 + print a + + + +for x=1,10 + continue if x % 2 == 0 + 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 + + diff --git a/spec/inputs/macro-export.mp b/spec/inputs/macro-export.mp deleted file mode 100644 index d669975..0000000 --- a/spec/inputs/macro-export.mp +++ /dev/null @@ -1,29 +0,0 @@ -export macro config = (debugging = true)-> - global debugMode = debugging == "true" - global debugMacro = true - "" - -export macro showMacro = (name,res)-> - if debugMacro - "do - txt = #{res} - print '[macro '..#{name}..']' - print txt - txt" - else - res - -export macro asserts = (cond)-> - if debugMode - $showMacro "assert", "assert #{cond}" - else - "" - -export macro assert = (cond)-> - if debugMode - $showMacro "assert", "assert #{cond}" - else - "#{cond}" - -$config! - diff --git a/spec/inputs/macro-export.yue b/spec/inputs/macro-export.yue new file mode 100644 index 0000000..d669975 --- /dev/null +++ b/spec/inputs/macro-export.yue @@ -0,0 +1,29 @@ +export macro config = (debugging = true)-> + global debugMode = debugging == "true" + global debugMacro = true + "" + +export macro showMacro = (name,res)-> + if debugMacro + "do + txt = #{res} + print '[macro '..#{name}..']' + print txt + txt" + else + res + +export macro asserts = (cond)-> + if debugMode + $showMacro "assert", "assert #{cond}" + else + "" + +export macro assert = (cond)-> + if debugMode + $showMacro "assert", "assert #{cond}" + else + "#{cond}" + +$config! + diff --git a/spec/inputs/macro-teal.mp b/spec/inputs/macro-teal.mp deleted file mode 100644 index 37cde1c..0000000 --- a/spec/inputs/macro-teal.mp +++ /dev/null @@ -1,87 +0,0 @@ -$ -> - import "moonp" as {:options} - if options.tl_enabled - options.target_extension = "tl" - package.path ..= "?.lua;./spec/lib/?.lua" - -macro to_lua = (codes)-> - "require('moonp').to_lua(#{codes}, reserve_line_number:false, same_module:true)" - -macro trim = (name)-> - "if result = #{name}\\match '[\\'\"](.*)[\\'\"]' then result else #{name}" - -export macro local = (decl, value = nil)-> - import "moonp" as {options:{:tl_enabled}} - name, type = ($trim decl)\match "(.-):(.*)" - if not (name and type) - error "invalid local varaible declaration for \"#{decl}\"" - value = $to_lua(value)\gsub "^return ", "" - codes = if tl_enabled - "local #{name}:#{$trim type} = #{value}" - else - "local #{name} = #{value}" - { - :codes - type: "text" - locals: {name} - } - -export macro function = (decl, value)-> - import "moonp" as {options:{:tl_enabled}} - import "tl" - decl = $trim decl - name, type = decl\match "(.-)(%(.*)" - if not (name and type) - error "invalid function declaration for \"#{decl}\"" - tokens = tl.lex "function #{decl}" - _, node = tl.parse_program tokens,{},"macro-function" - args = table.concat [arg.tk for arg in *node[1].args],", " - value = "(#{args})#{value}" - codes = if tl_enabled - value = $to_lua(value)\match "function%([^\n]*%)(.*)end" - "local function #{name}#{type}\n#{value}\nend" - else - value = $to_lua(value)\gsub "^return ", "" - "local #{name} = #{value}" - { - :codes - type: "text" - locals: {name} - } - -export macro record = (name, decl)-> - import "moonp" as {options:{:tl_enabled}} - codes = if tl_enabled - "local record #{name} - #{decl} -end" - else - "local #{name} = {}" - { - :codes - type: "text" - locals: {name} - } - -export macro method = (decl, value)-> - import "moonp" as {options:{:tl_enabled}} - import "tl" - decl = $trim decl - tab, sym, func, type = decl\match "(.-)([%.:])(.-)(%(.*)" - if not (tab and sym and func and type) - error "invalid method declaration for \"#{decl}\"" - tokens = tl.lex "function #{decl}" - _, node = tl.parse_program tokens,{},"macro-function" - args = table.concat [arg.tk for arg in *node[1].args],", " - value = "(#{args})->#{value\match "[%-=]>(.*)"}" - codes = if tl_enabled - value = $to_lua(value)\match "^return function%(.-%)\n(.*)end" - "function #{tab}#{sym}#{func}#{type}\n#{value}\nend" - else - value = $to_lua(value)\gsub "^return ", "" - "#{tab}.#{func} = #{value}" - { - :codes - type: "text" - } - diff --git a/spec/inputs/macro-teal.yue b/spec/inputs/macro-teal.yue new file mode 100644 index 0000000..3a9bb2b --- /dev/null +++ b/spec/inputs/macro-teal.yue @@ -0,0 +1,87 @@ +$ -> + import "yue" as {:options} + if options.tl_enabled + options.target_extension = "tl" + package.path ..= "?.lua;./spec/lib/?.lua" + +macro to_lua = (codes)-> + "require('yue').to_lua(#{codes}, reserve_line_number:false, same_module:true)" + +macro trim = (name)-> + "if result = #{name}\\match '[\\'\"](.*)[\\'\"]' then result else #{name}" + +export macro local = (decl, value = nil)-> + import "yue" as {options:{:tl_enabled}} + name, type = ($trim decl)\match "(.-):(.*)" + if not (name and type) + error "invalid local varaible declaration for \"#{decl}\"" + value = $to_lua(value)\gsub "^return ", "" + codes = if tl_enabled + "local #{name}:#{$trim type} = #{value}" + else + "local #{name} = #{value}" + { + :codes + type: "text" + locals: {name} + } + +export macro function = (decl, value)-> + import "yue" as {options:{:tl_enabled}} + import "tl" + decl = $trim decl + name, type = decl\match "(.-)(%(.*)" + if not (name and type) + error "invalid function declaration for \"#{decl}\"" + tokens = tl.lex "function #{decl}" + _, node = tl.parse_program tokens,{},"macro-function" + args = table.concat [arg.tk for arg in *node[1].args],", " + value = "(#{args})#{value}" + codes = if tl_enabled + value = $to_lua(value)\match "function%([^\n]*%)(.*)end" + "local function #{name}#{type}\n#{value}\nend" + else + value = $to_lua(value)\gsub "^return ", "" + "local #{name} = #{value}" + { + :codes + type: "text" + locals: {name} + } + +export macro record = (name, decl)-> + import "yue" as {options:{:tl_enabled}} + codes = if tl_enabled + "local record #{name} + #{decl} +end" + else + "local #{name} = {}" + { + :codes + type: "text" + locals: {name} + } + +export macro method = (decl, value)-> + import "yue" as {options:{:tl_enabled}} + import "tl" + decl = $trim decl + tab, sym, func, type = decl\match "(.-)([%.:])(.-)(%(.*)" + if not (tab and sym and func and type) + error "invalid method declaration for \"#{decl}\"" + tokens = tl.lex "function #{decl}" + _, node = tl.parse_program tokens,{},"macro-function" + args = table.concat [arg.tk for arg in *node[1].args],", " + value = "(#{args})->#{value\match "[%-=]>(.*)"}" + codes = if tl_enabled + value = $to_lua(value)\match "^return function%(.-%)\n(.*)end" + "function #{tab}#{sym}#{func}#{type}\n#{value}\nend" + else + value = $to_lua(value)\gsub "^return ", "" + "#{tab}.#{func} = #{value}" + { + :codes + type: "text" + } + diff --git a/spec/inputs/macro.mp b/spec/inputs/macro.mp deleted file mode 100644 index f0292c7..0000000 --- a/spec/inputs/macro.mp +++ /dev/null @@ -1,258 +0,0 @@ -$ -> - package.moonpath = "?.mp;./spec/inputs/?.mp" - -import "macro-export" as { - $, -- import all macros - $config:$myconfig, -- rename macro $config to $myconfig -} - -$asserts item == nil - -$myconfig false - -v = $assert item == nil - -macro and = (...)-> - values = [value for value in *{...}] - $showMacro "and", "#{ table.concat values, " and " }" - -if $and f1! - print "OK" - -if $and f1!, f2!, f3! - print "OK" - -macro in = (target, ...)-> - values = [value for value in *{...}] - $showMacro "in", table.concat ["#{target} == #{item}" for item in *values], " or " - -if x |> $in "Apple", "Pig", "Dog" - print "exist" - -macro map = (items, action)-> - $showMacro "map", "[#{action} for _ in *#{items}]" - -macro filter = (items, action)-> - $showMacro "filter", "[_ for _ in *#{items} when #{action}]" - -macro reduce = (items, def, action)-> - $showMacro "reduce", "if ##{items} == 0 - #{def} -else - _1 = #{def} - for _2 in *#{items} - _1 = #{action} - _1" - -macro foreach = (items, action)-> - $showMacro "foreach", "for _ in *#{items} - #{action}" - -macro pipe = (...)-> - switch select "#", ... - when 0 then return "" - when 1 then return ... - ops = {...} - last = ops[1] - stmts = for i = 2, #ops - stmt = "\tlocal _#{i} = #{last} |> #{ops[i]}" - last = "_#{i}" - stmt - res = "do -#{table.concat stmts, "\n"} - #{last}" - $showMacro "pipe", res - -{1,2,3} |> $map(_ * 2) |> $filter(_ > 4) |> $foreach print _ - -$foreach $filter($map({1,2,3}, _ * 2), _ > 4), print _ - -val = $pipe( - {1, 2, 3} - $map(_ * 2) - $filter(_ > 4) - $reduce(0, _1 + _2) -) - -macro plus = (a, b)-> "#{a} + #{b}" - -$plus(1,2)\call 123 - -res = 1 |> $plus 2 - -macro curry = (...)-> - args = {...} - len = #args - body = args[len] - def = table.concat ["(#{args[i]})->" for i = 1, len - 1] - "#{def}\n#{body\gsub "^do%s*\n",""}" - -f = $curry x,y,z,do - print x,y,z - -macro get_inner = (var)-> "do - a = 1 - a + 1" - -macro get_inner_hygienic = (var)-> "(-> - local a = 1 - a + 1)!" - -do - a = 8 - a = $get_inner! - a += $get_inner! - print a - -do - a = 8 - a = $get_inner_hygienic! - a += $get_inner_hygienic! - print a - -macro lua = (codes)-> { - :codes - type: "lua" -} - -x = 0 - -$lua [[ -local function f(a) - return a + 1 -end -x = x + f(3) -]] - -$lua[[ -function tb:func() - print(123) -end -]] - -print x - -macro def = (fname, ...)-> - args = {...} - last = table.remove args - { - codes: $showMacro "def", "local function #{fname}(#{table.concat args, ', '}) - #{last} -end" - type: "lua" - } - -sel = (a, b, c)-> if a then b else c - -$def sel, a, b, c, [[ - if a then - return b - else - return c - end -]] - -$def dummy,[[]] - -macro insertComment = (text)-> { - codes: "-- #{text\match '[\'"](.*)[\'"]'}" - type: "lua" -} - -$insertComment "a comment here" - -import 'underscore' as _ - -macro chain = (...)-> - callable = nil - for item in *{...} - callable = callable? and "(#{callable})\\#{item}" or item - $showMacro "chain", callable - -a = $chain( - _{1, 2, 3, 4, -2, 3} - chain! - map => @ * 2 - filter => @ > 3 - value! -) - -$chain( - _{1, 2, 3, 4, -2, 3} - chain! - map => @ * 2 - filter => @ > 3 - each => print @ -) - -result = $chain( - origin.transform.root.gameObject\Parents! - Descendants! - SelectEnable! - SelectVisible! - TagEqual "fx" - Where (x) -> x.name\EndsWith "(Clone)" - Destroy! -) - -macro chainB = (...)-> - switch select "#", ... - when 0 then return "" - when 1 then return ... - items = {...} - last = nil - stmts = for i = 1,#items - stmt = if i == #items - lastStr = last and "#{last}\\" or "" - "\t#{lastStr}#{items[i]}" - else - lastStr = last and "#{last}\\" or "" - "\tlocal _#{i} = #{lastStr}#{items[i]}" - last = "_#{i}" - stmt - res = "do -#{table.concat stmts, '\n'} -" - $showMacro "chainB", res - -$chainB( - origin.transform.root.gameObject\Parents! - Descendants! - SelectEnable! - SelectVisible! - TagEqual "fx" - Where (x) -> x.name\EndsWith "(Clone)" - Destroy! -) - -macro chainC = (...)-> - import "moonp" as {:to_lua} - callable = nil - config = { - implicit_return_root: false - reserve_line_number: false - } - for item in *{...} - if callable? - callable = "#{callable}:#{to_lua(item,config)\gsub '%s*$',''}" - else - callable = to_lua(item,config)\gsub '%s*$','' - { - codes: $showMacro "chainC", callable - type: "lua" - } - -$chainC( - origin.transform.root.gameObject\Parents! - Descendants! - SelectEnable! - SelectVisible! - TagEqual "fx" - Where (x) -> x.name\EndsWith "(Clone)" - Destroy! -) - -macro implicitReturnMacroIsAllowed = -> "print 'abc'\n123" - -$implicitReturnMacroIsAllowed! - diff --git a/spec/inputs/macro.yue b/spec/inputs/macro.yue new file mode 100644 index 0000000..eb3cf75 --- /dev/null +++ b/spec/inputs/macro.yue @@ -0,0 +1,258 @@ +$ -> + package.yuepath = "?.yue;./spec/inputs/?.yue" + +import "macro-export" as { + $, -- import all macros + $config:$myconfig, -- rename macro $config to $myconfig +} + +$asserts item == nil + +$myconfig false + +v = $assert item == nil + +macro and = (...)-> + values = [value for value in *{...}] + $showMacro "and", "#{ table.concat values, " and " }" + +if $and f1! + print "OK" + +if $and f1!, f2!, f3! + print "OK" + +macro in = (target, ...)-> + values = [value for value in *{...}] + $showMacro "in", table.concat ["#{target} == #{item}" for item in *values], " or " + +if x |> $in "Apple", "Pig", "Dog" + print "exist" + +macro map = (items, action)-> + $showMacro "map", "[#{action} for _ in *#{items}]" + +macro filter = (items, action)-> + $showMacro "filter", "[_ for _ in *#{items} when #{action}]" + +macro reduce = (items, def, action)-> + $showMacro "reduce", "if ##{items} == 0 + #{def} +else + _1 = #{def} + for _2 in *#{items} + _1 = #{action} + _1" + +macro foreach = (items, action)-> + $showMacro "foreach", "for _ in *#{items} + #{action}" + +macro pipe = (...)-> + switch select "#", ... + when 0 then return "" + when 1 then return ... + ops = {...} + last = ops[1] + stmts = for i = 2, #ops + stmt = "\tlocal _#{i} = #{last} |> #{ops[i]}" + last = "_#{i}" + stmt + res = "do +#{table.concat stmts, "\n"} + #{last}" + $showMacro "pipe", res + +{1,2,3} |> $map(_ * 2) |> $filter(_ > 4) |> $foreach print _ + +$foreach $filter($map({1,2,3}, _ * 2), _ > 4), print _ + +val = $pipe( + {1, 2, 3} + $map(_ * 2) + $filter(_ > 4) + $reduce(0, _1 + _2) +) + +macro plus = (a, b)-> "#{a} + #{b}" + +$plus(1,2)\call 123 + +res = 1 |> $plus 2 + +macro curry = (...)-> + args = {...} + len = #args + body = args[len] + def = table.concat ["(#{args[i]})->" for i = 1, len - 1] + "#{def}\n#{body\gsub "^do%s*\n",""}" + +f = $curry x,y,z,do + print x,y,z + +macro get_inner = (var)-> "do + a = 1 + a + 1" + +macro get_inner_hygienic = (var)-> "(-> + local a = 1 + a + 1)!" + +do + a = 8 + a = $get_inner! + a += $get_inner! + print a + +do + a = 8 + a = $get_inner_hygienic! + a += $get_inner_hygienic! + print a + +macro lua = (codes)-> { + :codes + type: "lua" +} + +x = 0 + +$lua [[ +local function f(a) + return a + 1 +end +x = x + f(3) +]] + +$lua[[ +function tb:func() + print(123) +end +]] + +print x + +macro def = (fname, ...)-> + args = {...} + last = table.remove args + { + codes: $showMacro "def", "local function #{fname}(#{table.concat args, ', '}) + #{last} +end" + type: "lua" + } + +sel = (a, b, c)-> if a then b else c + +$def sel, a, b, c, [[ + if a then + return b + else + return c + end +]] + +$def dummy,[[]] + +macro insertComment = (text)-> { + codes: "-- #{text\match '[\'"](.*)[\'"]'}" + type: "lua" +} + +$insertComment "a comment here" + +import 'underscore' as _ + +macro chain = (...)-> + callable = nil + for item in *{...} + callable = callable? and "(#{callable})\\#{item}" or item + $showMacro "chain", callable + +a = $chain( + _{1, 2, 3, 4, -2, 3} + chain! + map => @ * 2 + filter => @ > 3 + value! +) + +$chain( + _{1, 2, 3, 4, -2, 3} + chain! + map => @ * 2 + filter => @ > 3 + each => print @ +) + +result = $chain( + origin.transform.root.gameObject\Parents! + Descendants! + SelectEnable! + SelectVisible! + TagEqual "fx" + Where (x) -> x.name\EndsWith "(Clone)" + Destroy! +) + +macro chainB = (...)-> + switch select "#", ... + when 0 then return "" + when 1 then return ... + items = {...} + last = nil + stmts = for i = 1,#items + stmt = if i == #items + lastStr = last and "#{last}\\" or "" + "\t#{lastStr}#{items[i]}" + else + lastStr = last and "#{last}\\" or "" + "\tlocal _#{i} = #{lastStr}#{items[i]}" + last = "_#{i}" + stmt + res = "do +#{table.concat stmts, '\n'} +" + $showMacro "chainB", res + +$chainB( + origin.transform.root.gameObject\Parents! + Descendants! + SelectEnable! + SelectVisible! + TagEqual "fx" + Where (x) -> x.name\EndsWith "(Clone)" + Destroy! +) + +macro chainC = (...)-> + import "moonp" as {:to_lua} + callable = nil + config = { + implicit_return_root: false + reserve_line_number: false + } + for item in *{...} + if callable? + callable = "#{callable}:#{to_lua(item,config)\gsub '%s*$',''}" + else + callable = to_lua(item,config)\gsub '%s*$','' + { + codes: $showMacro "chainC", callable + type: "lua" + } + +$chainC( + origin.transform.root.gameObject\Parents! + Descendants! + SelectEnable! + SelectVisible! + TagEqual "fx" + Where (x) -> x.name\EndsWith "(Clone)" + Destroy! +) + +macro implicitReturnMacroIsAllowed = -> "print 'abc'\n123" + +$implicitReturnMacroIsAllowed! + diff --git a/spec/inputs/multiline_chain.mp b/spec/inputs/multiline_chain.mp deleted file mode 100644 index e1e363f..0000000 --- a/spec/inputs/multiline_chain.mp +++ /dev/null @@ -1,67 +0,0 @@ -x = a - .b - -x - .y = a - \b! - -func 1, arg2 - .value - \get!, arg3 - .value - \get! - -result = origin - .transform.root - .gameObject - \Parents! - \Descendants! - \SelectEnable! - \SelectVisible! - \TagEqual "fx" - \Where (x)-> - if x\IsTargeted! - return false - x.name\EndsWith "(Clone)" - \Destroy! - -origin.transform.root.gameObject - \Parents!\Descendants! - \SelectEnable! - \SelectVisible! - \TagEqual "fx" - \Where (x)-> x.name\EndsWith "(Clone)" - \Destroy! - -with item() - .itemFieldA = 123 - item = x - - \callMethod!\chainCall! - - \callMethod! - \chainCall! - \chainCall! - - switch .itemFieldB - \getValue! - when "Valid", \getItemState! - \itemMethodA!\getValue! - else - \itemMethodB! - \getValue! - - a = if .itemFieldC - .itemFieldD - else - .itemFieldE - - for v in *values - \itemMethodC v - - unless .b - .c = while .itemFieldD - \itemNext! - \get! - -nil diff --git a/spec/inputs/multiline_chain.yue b/spec/inputs/multiline_chain.yue new file mode 100644 index 0000000..d582ed0 --- /dev/null +++ b/spec/inputs/multiline_chain.yue @@ -0,0 +1,98 @@ +x = a + .b + .c + --[[chain item]] .d + +x + .y = a + .b + \c! + +func 1, arg2 + .value + \get!, arg3 + .value + \get! + * 1 + * x? + .y? + .z? + * 3 + +tbb = + k1: a + \b 123 + .c! + k2: + w1: a! + .b + \c! + +tb = f1{} + .a + |> f2? "abc", _ + +f = -> [a + .b + \c 123 for {a} in *vals] + +f1 = -> x, a + \b 123 + .c "abc" + +result = origin + .transform.root + .gameObject + \Parents! + \Descendants! + \SelectEnable! + \SelectVisible! + \TagEqual "fx" + \Where (x)-> + if x\IsTargeted! + return false + x.name\EndsWith "(Clone)" + \Destroy! + +origin.transform.root.gameObject + \Parents!\Descendants! + \SelectEnable! + \SelectVisible! + \TagEqual "fx" + \Where (x)-> x.name\EndsWith "(Clone)" + \Destroy! + +with item + .itemFieldA = 123 + + \callMethod!\chainCall! + + \callMethod! + \chainCall! + \chainCall! + + switch .itemFieldB + \getValue! + when "Valid", \getItemState! + \itemMethodA!\getValue! + else + \itemMethodB! + \getValue! + + a = if .itemFieldC + .itemFieldD + else + .itemFieldE + + for v in *values + \itemMethodC v + + for i = 1, counter + \itemMethodC i + + unless .b + .c = while .itemFieldD + \itemNext! + \get! + +nil diff --git a/spec/inputs/operators.mp b/spec/inputs/operators.mp deleted file mode 100644 index 57e58d6..0000000 --- a/spec/inputs/operators.mp +++ /dev/null @@ -1,72 +0,0 @@ - --- 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/operators.yue b/spec/inputs/operators.yue new file mode 100644 index 0000000..57e58d6 --- /dev/null +++ b/spec/inputs/operators.yue @@ -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/plus.mp b/spec/inputs/plus.mp deleted file mode 100644 index e387090..0000000 --- a/spec/inputs/plus.mp +++ /dev/null @@ -1,40 +0,0 @@ -x\do "work" - -func a\do!\end("OK")\if "abc",123 - -res = b.function\do!\while("OK")\if "def",998 - -c.repeat.if\then("xyz")\else res - -print @for,@@function 123 - -if fcolor = message\match "<%w*>" then message = message\gsub "<%->", fcolor - -message = message\gsub "<%->", fcolor if fcolor = message\match "<%w*>" - -valA = func! if func = getfunc! - -local valA = func! if func = getfunc! - -valB = do - func = getfunc! - func?! - -global backpack = - something: - yeah: 200 - they: -> - print "hello" - yor_feet"small" - pretty: hair - gold: hmm - yow: 1000 - - eat: goo - yeah: dudd - -start = - something: "cold" - -local bathe = - on: "fire" diff --git a/spec/inputs/plus.yue b/spec/inputs/plus.yue new file mode 100644 index 0000000..e387090 --- /dev/null +++ b/spec/inputs/plus.yue @@ -0,0 +1,40 @@ +x\do "work" + +func a\do!\end("OK")\if "abc",123 + +res = b.function\do!\while("OK")\if "def",998 + +c.repeat.if\then("xyz")\else res + +print @for,@@function 123 + +if fcolor = message\match "<%w*>" then message = message\gsub "<%->", fcolor + +message = message\gsub "<%->", fcolor if fcolor = message\match "<%w*>" + +valA = func! if func = getfunc! + +local valA = func! if func = getfunc! + +valB = do + func = getfunc! + func?! + +global backpack = + something: + yeah: 200 + they: -> + print "hello" + yor_feet"small" + pretty: hair + gold: hmm + yow: 1000 + + eat: goo + yeah: dudd + +start = + something: "cold" + +local bathe = + on: "fire" diff --git a/spec/inputs/return.mp b/spec/inputs/return.mp deleted file mode 100644 index f170ffd..0000000 --- a/spec/inputs/return.mp +++ /dev/null @@ -1,55 +0,0 @@ --- 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/spec/inputs/return.yue b/spec/inputs/return.yue new file mode 100644 index 0000000..f170ffd --- /dev/null +++ b/spec/inputs/return.yue @@ -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/spec/inputs/string.mp b/spec/inputs/string.mp deleted file mode 100644 index 5d8f772..0000000 --- a/spec/inputs/string.mp +++ /dev/null @@ -1,66 +0,0 @@ - -hi = "hello" -hello = "what the heckyes" -print hi - -umm = 'umm' - -here, another = "yeah", 'world' - -aye = "YU'M" -you '"hmmm" I said' - -print aye, you - -another = [[ hello world ]] - - -hi_there = [[ - hi there -]] - -well = [==[ "helo" ]==] - -hola = [===[ - eat noots]===] - -mm = [[well trhere]] - -oo = "" - -x = "\\" -x = "a\\b" -x = "\\\n" -x = "\"" - --- - -a = "hello #{hello} hello" -b = "#{hello} hello" -c = "hello #{5+1}" -d = "#{hello world}" -e = "#{1} #{2} #{3}" - -f = [[hello #{world} world]] - --- - -a = 'hello #{hello} hello' -b = '#{hello} hello' -c = 'hello #{hello}' - - --- - -_ = "hello" -"hello"\format 1 -"hello"\format(1,2,3) -"hello"\format(1,2,3) 1,2,3 - -"hello"\world! -"hello"\format!.hello 1,2,3 -"hello"\format 1,2,3 - -something"hello"\world! -something "hello"\world! - diff --git a/spec/inputs/string.yue b/spec/inputs/string.yue new file mode 100644 index 0000000..5d8f772 --- /dev/null +++ b/spec/inputs/string.yue @@ -0,0 +1,66 @@ + +hi = "hello" +hello = "what the heckyes" +print hi + +umm = 'umm' + +here, another = "yeah", 'world' + +aye = "YU'M" +you '"hmmm" I said' + +print aye, you + +another = [[ hello world ]] + + +hi_there = [[ + hi there +]] + +well = [==[ "helo" ]==] + +hola = [===[ + eat noots]===] + +mm = [[well trhere]] + +oo = "" + +x = "\\" +x = "a\\b" +x = "\\\n" +x = "\"" + +-- + +a = "hello #{hello} hello" +b = "#{hello} hello" +c = "hello #{5+1}" +d = "#{hello world}" +e = "#{1} #{2} #{3}" + +f = [[hello #{world} world]] + +-- + +a = 'hello #{hello} hello' +b = '#{hello} hello' +c = 'hello #{hello}' + + +-- + +_ = "hello" +"hello"\format 1 +"hello"\format(1,2,3) +"hello"\format(1,2,3) 1,2,3 + +"hello"\world! +"hello"\format!.hello 1,2,3 +"hello"\format 1,2,3 + +something"hello"\world! +something "hello"\world! + diff --git a/spec/inputs/stub.mp b/spec/inputs/stub.mp deleted file mode 100644 index 60347e7..0000000 --- a/spec/inputs/stub.mp +++ /dev/null @@ -1,18 +0,0 @@ - - -x = { - val: 100 - hello: => - print @val -} - -fn = x\val -print fn! -print x\val! - - --- ... should be bubbled up anon functions -((...)-> x = hello(...)\world)! - -nil - diff --git a/spec/inputs/stub.yue b/spec/inputs/stub.yue new file mode 100644 index 0000000..60347e7 --- /dev/null +++ b/spec/inputs/stub.yue @@ -0,0 +1,18 @@ + + +x = { + val: 100 + hello: => + print @val +} + +fn = x\val +print fn! +print x\val! + + +-- ... should be bubbled up anon functions +((...)-> x = hello(...)\world)! + +nil + diff --git a/spec/inputs/switch.mp b/spec/inputs/switch.mp deleted file mode 100644 index ac3dbea..0000000 --- a/spec/inputs/switch.mp +++ /dev/null @@ -1,64 +0,0 @@ - -switch value - when "cool" - print "hello world" - - -switch value - when "cool" - print "hello world" - else - print "okay rad" - - -switch value - when "cool" - print "hello world" - when "yeah" - _ = [[FFFF]] + [[MMMM]] - when 2323 + 32434 - print "okay" - else - print "okay rad" - -out = switch value - when "cool" then print "hello world" - else print "okay rad" - -out = switch value - when "cool" then xxxx - when "umm" then 34340 - else error "this failed big time" - -with something - switch \value! - when .okay - _ = "world" - else - _ = "yesh" - -fix this -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/spec/inputs/switch.yue b/spec/inputs/switch.yue new file mode 100644 index 0000000..ac3dbea --- /dev/null +++ b/spec/inputs/switch.yue @@ -0,0 +1,64 @@ + +switch value + when "cool" + print "hello world" + + +switch value + when "cool" + print "hello world" + else + print "okay rad" + + +switch value + when "cool" + print "hello world" + when "yeah" + _ = [[FFFF]] + [[MMMM]] + when 2323 + 32434 + print "okay" + else + print "okay rad" + +out = switch value + when "cool" then print "hello world" + else print "okay rad" + +out = switch value + when "cool" then xxxx + when "umm" then 34340 + else error "this failed big time" + +with something + switch \value! + when .okay + _ = "world" + else + _ = "yesh" + +fix this +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/spec/inputs/syntax.mp b/spec/inputs/syntax.mp deleted file mode 100644 index 4df5785..0000000 --- a/spec/inputs/syntax.mp +++ /dev/null @@ -1,418 +0,0 @@ -#!/this/is/ignored - -a = 1 + 2* 3 / 6 - -a, bunch, go, here = another, world - -func arg1, arg2, another, arg3 - -here, we = () ->, yeah -the, different = () -> approach; yeah - -dad() -dad(lord) -hello(one,two)() -(5 + 5)(world) - -fun(a)(b) - -fun(a) b - -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] - -_ = (get[something] + 5)[years] - -i,x = 200, 300 - -yeah = (1 + 5) * 3 -yeah = ((1+5)*3)/2 -yeah = ((1+5)*3)/2 + i % 100 - -whoa = (1+2) * (3+4) * (4+5) - -_ = -> - if something - return 1,2,4 - - print "hello" - -_ = -> - if hello - "heloo", "world" - else - no, way - - -_ = -> 1,2,34 - -return 5 + () -> 4 + 2 - -return 5 + (() -> 4) + 2 - -print 5 + () -> - _ = 34 - good nads - - -something 'else', "ya" - -something'else' -something"else" - -_ = something[[hey]] * 2 -_ = something[======[hey]======] * 2 -_ = something[ [======[hey]======] ] * 2 - - -_ = something'else', 2 -_ = something"else", 2 -_ = something[[else]], 2 -_ = something[ [[else]] ], 2 - -something 'else', 2 -something "else", 2 -something [[else]], 2 - -_ = here(we)"go"[12123] - -split"abc xyz 123"\map"#"\printAll! - -_ = f""[a] -_ = f""\b! -_ = f"".c! - -f ""[a] -f ""\b! -f "".c! - -list{"abc", "xyz", "123"}\map"#"\printAll! - -_ = f{}[a] -_ = f{}\b! -_ = f{}.c! - --- this runs -something = - test: 12323 - what: -> print "hello world" - -print something.test - -frick = hello: "world" - -argon = - num: 100 - world: (self) -> - print self.num - return { - something: -> print "hi from something" - } - somethin: (self, str) -> - print "string is", str - return world: (a,b) -> print "sum", a + b - -something.what() -argon\world().something() - -argon\somethin"200".world(1,2) - -x = -434 - -x = -hello world one two - -hi = -"herfef" - -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" - -if hello then _ = 343 - -print "what" if cool - -((...)-> arg = {...})! - -x = (...) -> - dump {...} - - -x = not true - -y = not(5+5) - - -y = #"hello" - -x = #{#{},#{1},#{1,2}} - -_ = hello, world - -something\hello(what) a,b -something\hello what -something.hello\world a,b -something.hello\world(1,2,3) a,b -something - .hello - \world(1,2,3) a,b - - -x = 1232 -x += 10 + 3 -j -= "hello" -y *= 2 -y /= 100 -m %= 2 -hello ..= "world" - -@@something += 10 -@something += 10 - -a["hello"] += 10 -a["hello#{tostring ff}"] += 10 -a[four].x += 10 - -x = 0 -_ = (if ntype(v) == "fndef" then x += 1) for v in *values - - -hello = - something: world - if: "hello" - else: 3434 - function: "okay" - good: 230203 - - -div class: "cool" - -_ = 5 + what wack -what whack + 5 - -_ = 5 - what wack -what whack - 5 - -x = hello - world - something - -((something = with what - \cool 100) -> - print something)! - -if something - _ = 03589 - --- okay what about this - -else - _ = 3434 - - -if something - _ = yeah - - -elseif "ymmm" - - print "cool" - -else - - _ = okay - - --- test names containing keywords -x = notsomething -y = ifsomething -z = x and b -z = x andb - - --- undelimited tables - -while 10 > something - something: "world" - print "yeah" - -x = - okay: sure - -yeah - okay: man - sure: sir - -hello "no comma" - yeah: dada - another: world - -hello "comma", - something: hello_world - frick: you - --- creates one table -another hello, one, - two, three, four, yeah: man - okay: yeah - fine: alright - -another hello, one, - two, three, four, - yeah: man, okay: yeah - * fine: alright, - okay: 1 - * fine: alright, - okay: 2 - -another hello, one, two, three, four, - yeah: man - okay: yeah - -another hello, one, two, three, four, yeah: man - okay: yeah - -ajax url, - (data) -> - process data, - (error) -> - print error - --- -a += 3 - 5 -a *= 3 + 5 -a *= 3 -a >>= 3 -a <<= 3 -a /= func "cool" - ---- - -x.then = "hello" -x.while.true = "hello" -x - .while - .true = "hello" - --- - -x or= "hello" -x and= "hello" - --- - -z = a-b -z = a -b -z = a - b -z = a- b - - --- cooool - -str = --[[ -This is a multi line comment. -It's OK. -]] strA \ -- comment 1 - .. strB \ -- comment 2 - .. strC - -func --[[port]] 3000, --[[ip]] "192.168.1.1" - -f = -> - a,b, \ - c,d, \ - e,f - -f = -> - a,b \ - ,c,d \ - ,e,f - -with obj - invoke( - --[[arg1]] \func!, - --[[arg2]] 123, - --[[arg3]] "abc" - ) - -invokeA( - invokeB( - invokeC 123 - ) -) - -123 - |> invokeC - |> invokeB - |> invokeA - -v = { - a -1 - a( --1) - a \ -- 1 - a-1 - a - 1 - a - -1 - a- -1 - a - --[[123]]1 - - a ~1 - a( -~1) - a \ -~ 1 - a~1 - a ~ 1 - a ~ -1 - a~ -1 - a ~ --[[123]]1 -} - -do - a = 1 \ - + 2 \ - * 3 / - 4 - - _1 = f1 -1 \ - + 2 \ - + 3 - - _2 = f1 - 1 \ - + 2 \ - + 3 - - f2 = (x)-> print x \ - +1 - - a = f2! - -1 |> f2 - - a = f2! \ - - 1 |> f2 - - _1 \ - ,_2 \ - ,_3 \ - ,_4 = 1 \ - ,f 2 \ - ,3 \ - ,f 4, \ - 4 - -nil - diff --git a/spec/inputs/syntax.yue b/spec/inputs/syntax.yue new file mode 100644 index 0000000..4df5785 --- /dev/null +++ b/spec/inputs/syntax.yue @@ -0,0 +1,418 @@ +#!/this/is/ignored + +a = 1 + 2* 3 / 6 + +a, bunch, go, here = another, world + +func arg1, arg2, another, arg3 + +here, we = () ->, yeah +the, different = () -> approach; yeah + +dad() +dad(lord) +hello(one,two)() +(5 + 5)(world) + +fun(a)(b) + +fun(a) b + +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] + +_ = (get[something] + 5)[years] + +i,x = 200, 300 + +yeah = (1 + 5) * 3 +yeah = ((1+5)*3)/2 +yeah = ((1+5)*3)/2 + i % 100 + +whoa = (1+2) * (3+4) * (4+5) + +_ = -> + if something + return 1,2,4 + + print "hello" + +_ = -> + if hello + "heloo", "world" + else + no, way + + +_ = -> 1,2,34 + +return 5 + () -> 4 + 2 + +return 5 + (() -> 4) + 2 + +print 5 + () -> + _ = 34 + good nads + + +something 'else', "ya" + +something'else' +something"else" + +_ = something[[hey]] * 2 +_ = something[======[hey]======] * 2 +_ = something[ [======[hey]======] ] * 2 + + +_ = something'else', 2 +_ = something"else", 2 +_ = something[[else]], 2 +_ = something[ [[else]] ], 2 + +something 'else', 2 +something "else", 2 +something [[else]], 2 + +_ = here(we)"go"[12123] + +split"abc xyz 123"\map"#"\printAll! + +_ = f""[a] +_ = f""\b! +_ = f"".c! + +f ""[a] +f ""\b! +f "".c! + +list{"abc", "xyz", "123"}\map"#"\printAll! + +_ = f{}[a] +_ = f{}\b! +_ = f{}.c! + +-- this runs +something = + test: 12323 + what: -> print "hello world" + +print something.test + +frick = hello: "world" + +argon = + num: 100 + world: (self) -> + print self.num + return { + something: -> print "hi from something" + } + somethin: (self, str) -> + print "string is", str + return world: (a,b) -> print "sum", a + b + +something.what() +argon\world().something() + +argon\somethin"200".world(1,2) + +x = -434 + +x = -hello world one two + +hi = -"herfef" + +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" + +if hello then _ = 343 + +print "what" if cool + +((...)-> arg = {...})! + +x = (...) -> + dump {...} + + +x = not true + +y = not(5+5) + + +y = #"hello" + +x = #{#{},#{1},#{1,2}} + +_ = hello, world + +something\hello(what) a,b +something\hello what +something.hello\world a,b +something.hello\world(1,2,3) a,b +something + .hello + \world(1,2,3) a,b + + +x = 1232 +x += 10 + 3 +j -= "hello" +y *= 2 +y /= 100 +m %= 2 +hello ..= "world" + +@@something += 10 +@something += 10 + +a["hello"] += 10 +a["hello#{tostring ff}"] += 10 +a[four].x += 10 + +x = 0 +_ = (if ntype(v) == "fndef" then x += 1) for v in *values + + +hello = + something: world + if: "hello" + else: 3434 + function: "okay" + good: 230203 + + +div class: "cool" + +_ = 5 + what wack +what whack + 5 + +_ = 5 - what wack +what whack - 5 + +x = hello - world - something + +((something = with what + \cool 100) -> + print something)! + +if something + _ = 03589 + +-- okay what about this + +else + _ = 3434 + + +if something + _ = yeah + + +elseif "ymmm" + + print "cool" + +else + + _ = okay + + +-- test names containing keywords +x = notsomething +y = ifsomething +z = x and b +z = x andb + + +-- undelimited tables + +while 10 > something + something: "world" + print "yeah" + +x = + okay: sure + +yeah + okay: man + sure: sir + +hello "no comma" + yeah: dada + another: world + +hello "comma", + something: hello_world + frick: you + +-- creates one table +another hello, one, + two, three, four, yeah: man + okay: yeah + fine: alright + +another hello, one, + two, three, four, + yeah: man, okay: yeah + * fine: alright, + okay: 1 + * fine: alright, + okay: 2 + +another hello, one, two, three, four, + yeah: man + okay: yeah + +another hello, one, two, three, four, yeah: man + okay: yeah + +ajax url, + (data) -> + process data, + (error) -> + print error + +-- +a += 3 - 5 +a *= 3 + 5 +a *= 3 +a >>= 3 +a <<= 3 +a /= func "cool" + +--- + +x.then = "hello" +x.while.true = "hello" +x + .while + .true = "hello" + +-- + +x or= "hello" +x and= "hello" + +-- + +z = a-b +z = a -b +z = a - b +z = a- b + + +-- cooool + +str = --[[ +This is a multi line comment. +It's OK. +]] strA \ -- comment 1 + .. strB \ -- comment 2 + .. strC + +func --[[port]] 3000, --[[ip]] "192.168.1.1" + +f = -> + a,b, \ + c,d, \ + e,f + +f = -> + a,b \ + ,c,d \ + ,e,f + +with obj + invoke( + --[[arg1]] \func!, + --[[arg2]] 123, + --[[arg3]] "abc" + ) + +invokeA( + invokeB( + invokeC 123 + ) +) + +123 + |> invokeC + |> invokeB + |> invokeA + +v = { + a -1 + a( +-1) + a \ +- 1 + a-1 + a - 1 + a - +1 + a- +1 + a - --[[123]]1 + + a ~1 + a( +~1) + a \ +~ 1 + a~1 + a ~ 1 + a ~ +1 + a~ +1 + a ~ --[[123]]1 +} + +do + a = 1 \ + + 2 \ + * 3 / + 4 + + _1 = f1 -1 \ + + 2 \ + + 3 + + _2 = f1 - 1 \ + + 2 \ + + 3 + + f2 = (x)-> print x \ + +1 + + a = f2! + -1 |> f2 + + a = f2! \ + - 1 |> f2 + + _1 \ + ,_2 \ + ,_3 \ + ,_4 = 1 \ + ,f 2 \ + ,3 \ + ,f 4, \ + 4 + +nil + diff --git a/spec/inputs/tables.mp b/spec/inputs/tables.mp deleted file mode 100644 index 2c6dff9..0000000 --- a/spec/inputs/tables.mp +++ /dev/null @@ -1,223 +0,0 @@ - -backpack = - something: - yeah: 200 - they: -> - print "hello" - yor_feet"small" - pretty: hair - gold: hmm - yow: 1000 - - eat: goo - yeah: dudd - - -start = - something: "cold" - -bathe = - on: "fire" - -another = - [4]: 232 - ["good food"]: "is the best" - -fwip = - something: hello"what", number: 2323, - what: yo "momma", "yeah", - fruit: basket - nuts: day - - -frick = hello: "world" - -frack, best = hello: "world", rice: 3434, "what" - -ya = { 1,2,3, key: 100, 343, "hello", umm: 232 } - - -x = { 1,2, - 4343, 343 ,343 } - - -g, p = { - 1,2, nowy: "yes", 3,4, - hey: 232, another: "day" -}, 234 - -annother = { - 1,2,3 - 3,4,5 - 6,7,8 -} - -yeah = { - [232]: 3434, "helo" - ice: "cake" -} - --- confusing stuff... -whatabout = { - hello world, another - what, about, now - - hello"world", yeah - hello "world", yeah -} - -x = - -- yeah - something: => "hello" - cool: -- umm - --so ething - bed: { - 2323,2323 - } - red: 2343 -- here - -- what - name: (node) => @value node -- here - -- comment me --- okay - - -x = { :something, something: something } - -y = { - :hi, :there, :how, :you - :thing -} - -call_me "hello", :x, :y, :z - -t = { - a: 'a' - [b]: 'b' -} - -xam = { - hello: 1234 - "hello": 12354 - [[hello]]: 12354 - ["hello"]: 12354 - [ [[hello]] ]: 12354 -} - - -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 => -} - --- both of these have desirable 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 - -heroine = - name: "Christina" - age: 18 - job: "Princess" - likes: - * name: "kittens" - img: "/image/kittens.png" - * name: "flower" - img: "/image/flower.png" - items: - * name: "ring" - amount: 2 - * name: "necklace" - amount: 1 - status: - desc: "weak" - * attribute: "health" - value: 50 - * attribute: "mana" - value: 100 - -inventory = - equipment: - * "sword" - * "shield" - items: - * name: "potion" - count: 10 - * name: "bread" - count: 3 - -items = - * func! - * with tb - .abc = 123 - * {1, 2, 3} - * f {1, 2, 3} - * f - * 1 - * 2 - * 3 - * [i for i = 1, 3] - * "#{if a then b}" - -pairs = - * - * "king" - * "queen" - * - * "hero" - * "princess" - -items = - * - name: "ring" - amount: 2 - * - name: "necklace" - amount: 1 - -nil - diff --git a/spec/inputs/tables.yue b/spec/inputs/tables.yue new file mode 100644 index 0000000..2c6dff9 --- /dev/null +++ b/spec/inputs/tables.yue @@ -0,0 +1,223 @@ + +backpack = + something: + yeah: 200 + they: -> + print "hello" + yor_feet"small" + pretty: hair + gold: hmm + yow: 1000 + + eat: goo + yeah: dudd + + +start = + something: "cold" + +bathe = + on: "fire" + +another = + [4]: 232 + ["good food"]: "is the best" + +fwip = + something: hello"what", number: 2323, + what: yo "momma", "yeah", + fruit: basket + nuts: day + + +frick = hello: "world" + +frack, best = hello: "world", rice: 3434, "what" + +ya = { 1,2,3, key: 100, 343, "hello", umm: 232 } + + +x = { 1,2, + 4343, 343 ,343 } + + +g, p = { + 1,2, nowy: "yes", 3,4, + hey: 232, another: "day" +}, 234 + +annother = { + 1,2,3 + 3,4,5 + 6,7,8 +} + +yeah = { + [232]: 3434, "helo" + ice: "cake" +} + +-- confusing stuff... +whatabout = { + hello world, another + what, about, now + + hello"world", yeah + hello "world", yeah +} + +x = + -- yeah + something: => "hello" + cool: -- umm + --so ething + bed: { + 2323,2323 + } + red: 2343 -- here + -- what + name: (node) => @value node -- here + -- comment me +-- okay + + +x = { :something, something: something } + +y = { + :hi, :there, :how, :you + :thing +} + +call_me "hello", :x, :y, :z + +t = { + a: 'a' + [b]: 'b' +} + +xam = { + hello: 1234 + "hello": 12354 + [[hello]]: 12354 + ["hello"]: 12354 + [ [[hello]] ]: 12354 +} + + +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 => +} + +-- both of these have desirable 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 + +heroine = + name: "Christina" + age: 18 + job: "Princess" + likes: + * name: "kittens" + img: "/image/kittens.png" + * name: "flower" + img: "/image/flower.png" + items: + * name: "ring" + amount: 2 + * name: "necklace" + amount: 1 + status: + desc: "weak" + * attribute: "health" + value: 50 + * attribute: "mana" + value: 100 + +inventory = + equipment: + * "sword" + * "shield" + items: + * name: "potion" + count: 10 + * name: "bread" + count: 3 + +items = + * func! + * with tb + .abc = 123 + * {1, 2, 3} + * f {1, 2, 3} + * f + * 1 + * 2 + * 3 + * [i for i = 1, 3] + * "#{if a then b}" + +pairs = + * + * "king" + * "queen" + * + * "hero" + * "princess" + +items = + * + name: "ring" + amount: 2 + * + name: "necklace" + amount: 1 + +nil + diff --git a/spec/inputs/teal-lang.mp b/spec/inputs/teal-lang.mp deleted file mode 100644 index 3368b95..0000000 --- a/spec/inputs/teal-lang.mp +++ /dev/null @@ -1,39 +0,0 @@ -$ -> - package.moonpath = "?.mp;./spec/inputs/?.mp" - -import "macro-teal" as {$} - -$local "a:{string:number}", {value:123} -$local "b:number", a.value - -$function "add(a:number, b:number):number", -> a + b - -s = add(a.value, b) -print(s) - -$record Point, - x: number - y: number - -$method "Point.new(x:number, y:number):Point", -> - $local "point:Point", setmetatable {}, __index: Point - point.x = x or 0 - point.y = y or 0 - point - -$method "Point:move(dx:number, dy:number)", -> - @x += dx - @y += dy - -$local "p:Point", Point.new 100, 100 - -p\move 50, 50 - -$function "filter(tab:{string}, handler:function(item:string):boolean):{string}", -> - [item for item in *tab when handler item] - -$function "cond(item:string):boolean", -> item ~= "a" - -res = filter {"a", "b", "c", "a"}, cond -for s in *res - print s diff --git a/spec/inputs/teal-lang.yue b/spec/inputs/teal-lang.yue new file mode 100644 index 0000000..1993203 --- /dev/null +++ b/spec/inputs/teal-lang.yue @@ -0,0 +1,39 @@ +$ -> + package.yuepath = "?.yue;./spec/inputs/?.yue" + +import "macro-teal" as {$} + +$local "a:{string:number}", {value:123} +$local "b:number", a.value + +$function "add(a:number, b:number):number", -> a + b + +s = add(a.value, b) +print(s) + +$record Point, + x: number + y: number + +$method "Point.new(x:number, y:number):Point", -> + $local "point:Point", setmetatable {}, __index: Point + point.x = x or 0 + point.y = y or 0 + point + +$method "Point:move(dx:number, dy:number)", -> + @x += dx + @y += dy + +$local "p:Point", Point.new 100, 100 + +p\move 50, 50 + +$function "filter(tab:{string}, handler:function(item:string):boolean):{string}", -> + [item for item in *tab when handler item] + +$function "cond(item:string):boolean", -> item ~= "a" + +res = filter {"a", "b", "c", "a"}, cond +for s in *res + print s diff --git a/spec/inputs/unless_else.mp b/spec/inputs/unless_else.mp deleted file mode 100644 index b421d4d..0000000 --- a/spec/inputs/unless_else.mp +++ /dev/null @@ -1,5 +0,0 @@ -if a - unless b - print "hi" - elseif c - print "not hi" diff --git a/spec/inputs/unless_else.yue b/spec/inputs/unless_else.yue new file mode 100644 index 0000000..b421d4d --- /dev/null +++ b/spec/inputs/unless_else.yue @@ -0,0 +1,5 @@ +if a + unless b + print "hi" + elseif c + print "not hi" diff --git a/spec/inputs/using.mp b/spec/inputs/using.mp deleted file mode 100644 index fe0a433..0000000 --- a/spec/inputs/using.mp +++ /dev/null @@ -1,22 +0,0 @@ - -hello = "hello" -world = "world" - -_ = (using nil) -> - hello = 3223 - -_ = (a using nil) -> - hello = 3223 - a = 323 - -_ = (a,b,c using a,b,c) -> - a,b,c = 1,2,3 - world = 12321 - -(a,e,f using a,b,c, hello) -> - a,b,c = 1,2,3 - hello = 12321 - world = "yeah" - - - diff --git a/spec/inputs/using.yue b/spec/inputs/using.yue new file mode 100644 index 0000000..fe0a433 --- /dev/null +++ b/spec/inputs/using.yue @@ -0,0 +1,22 @@ + +hello = "hello" +world = "world" + +_ = (using nil) -> + hello = 3223 + +_ = (a using nil) -> + hello = 3223 + a = 323 + +_ = (a,b,c using a,b,c) -> + a,b,c = 1,2,3 + world = 12321 + +(a,e,f using a,b,c, hello) -> + a,b,c = 1,2,3 + hello = 12321 + world = "yeah" + + + diff --git a/spec/inputs/whitespace.mp b/spec/inputs/whitespace.mp deleted file mode 100644 index 329280f..0000000 --- a/spec/inputs/whitespace.mp +++ /dev/null @@ -1,140 +0,0 @@ - -_ = { - 1, 2 -} - -_ = { 1, 2 -} - -_ = { 1, 2 } - -_ = {1,2} - -_ = { -1,2 - -} - -_ = { something 1,2, - 4,5,6, - 3,4,5 -} - -_ = { - a 1,2,3, - 4,5,6 - 1,2,3 -} - - -_ = { - b 1,2,3, - 4,5,6 - 1,2,3, - 1,2,3 -} - -_ = { 1,2,3 } - -_ = { 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 - - -_ = { - hello 1,2, - 3,4, - 5, 6 -} - -x = { - hello 1,2,3,4, - 5,6,7 - 1,2,3,4 -} - -if hello 1,2,3, - world, - world - print "hello" - -if hello 1,2,3, - world, - world - print "hello" - - --- - -a( - one, two, three -) - -b( - one, - two, - three -) - - -c(one, two, - three, four) - --- - -v = -> - a, \-- v1 - b, \-- v2 - c -- v3 - -v1, v2, \ - v3 = -> - a; \-- end of function for v1 - b, \-- v2 - c -- v3 - -a, b, \ - c, d, \ -e, f = 1, \ - f2 - :abc; \-- arg2 - 3, \ - 4, \ - f5 abc; \-- arg5 - 6 - -for a, \-- destruct 1 - b, \-- destruct 2 - --[[destruct 3]] c \ - in pairs tb - print a, \ - b, \ - c - -for i = 1, \-- initial - 10, \-- stop - -1 -- step - print i - -local a,\ - b,\ - c - -nil diff --git a/spec/inputs/whitespace.yue b/spec/inputs/whitespace.yue new file mode 100644 index 0000000..329280f --- /dev/null +++ b/spec/inputs/whitespace.yue @@ -0,0 +1,140 @@ + +_ = { + 1, 2 +} + +_ = { 1, 2 +} + +_ = { 1, 2 } + +_ = {1,2} + +_ = { +1,2 + +} + +_ = { something 1,2, + 4,5,6, + 3,4,5 +} + +_ = { + a 1,2,3, + 4,5,6 + 1,2,3 +} + + +_ = { + b 1,2,3, + 4,5,6 + 1,2,3, + 1,2,3 +} + +_ = { 1,2,3 } + +_ = { 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 + + +_ = { + hello 1,2, + 3,4, + 5, 6 +} + +x = { + hello 1,2,3,4, + 5,6,7 + 1,2,3,4 +} + +if hello 1,2,3, + world, + world + print "hello" + +if hello 1,2,3, + world, + world + print "hello" + + +-- + +a( + one, two, three +) + +b( + one, + two, + three +) + + +c(one, two, + three, four) + +-- + +v = -> + a, \-- v1 + b, \-- v2 + c -- v3 + +v1, v2, \ + v3 = -> + a; \-- end of function for v1 + b, \-- v2 + c -- v3 + +a, b, \ + c, d, \ +e, f = 1, \ + f2 + :abc; \-- arg2 + 3, \ + 4, \ + f5 abc; \-- arg5 + 6 + +for a, \-- destruct 1 + b, \-- destruct 2 + --[[destruct 3]] c \ + in pairs tb + print a, \ + b, \ + c + +for i = 1, \-- initial + 10, \-- stop + -1 -- step + print i + +local a,\ + b,\ + c + +nil diff --git a/spec/inputs/with.mp b/spec/inputs/with.mp deleted file mode 100644 index d88e109..0000000 --- a/spec/inputs/with.mp +++ /dev/null @@ -1,123 +0,0 @@ - -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 tb - .x = item - .field - \func 123 - -do - with dad - .if "yes" - y = .end.of.function diff --git a/spec/inputs/with.yue b/spec/inputs/with.yue new file mode 100644 index 0000000..d88e109 --- /dev/null +++ b/spec/inputs/with.yue @@ -0,0 +1,123 @@ + +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 tb + .x = item + .field + \func 123 + +do + with dad + .if "yes" + y = .end.of.function diff --git a/src/MoonP/ast.cpp b/src/MoonP/ast.cpp deleted file mode 100644 index 4929021..0000000 --- a/src/MoonP/ast.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* Copyright (c) 2012, Achilleas Margaritis, modified by Jin Li -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/ - -#include - -#include "MoonP/ast.hpp" - - -namespace parserlib { - - -traversal ast_node::traverse(const std::function& func) { - return func(this); -} - -ast_node* ast_node::getByTypeIds(int* begin, int* end) { - ast_node* current = this; - auto it = begin; - while (it != end) { - ast_node* findNode = nullptr; - int i = *it; - current->visitChild([&](ast_node* node) { - if (node->getId() == i) { - findNode = node; - return true; - } - return false; - }); - if (findNode) { - current = findNode; - } else { - current = nullptr; - break; - } - ++it; - } - return current; -} - -bool ast_node::visitChild(const std::function&) { - return false; -} - - -/** Asks all members to construct themselves from the stack. - The members are asked to construct themselves in reverse order. - from a node stack. - @param st stack. -*/ -void ast_container::construct(ast_stack &st) { - for(ast_member_vector::reverse_iterator it = m_members.rbegin(); - it != m_members.rend(); - ++it) - { - ast_member* member = *it; - member->construct(st); - } -} - -traversal ast_container::traverse(const std::function& func) { - traversal action = func(this); - switch (action) { - case traversal::Stop: return traversal::Stop; - case traversal::Return: return traversal::Continue; - default: break; - } - const auto& members = this->members(); - for (auto member : members) { - switch (member->get_type()) { - case ast_holder_type::Pointer: { - _ast_ptr* ptr = static_cast<_ast_ptr*>(member); - if (ptr->get() && ptr->get()->traverse(func) == traversal::Stop) { - return traversal::Stop; - } - break; - } - case ast_holder_type::List: { - _ast_list* list = static_cast<_ast_list*>(member); - for (auto obj : list->objects()) { - if (obj->traverse(func) == traversal::Stop) { - return traversal::Stop; - } - } - break; - } - } - } - return traversal::Continue; -} - -bool ast_container::visitChild(const std::function& func) { - const auto& members = this->members(); - for (auto member : members) { - switch (member->get_type()) { - case ast_holder_type::Pointer: { - _ast_ptr* ptr = static_cast<_ast_ptr*>(member); - if (ptr->get()) { - if (func(ptr->get())) return true; - } - break; - } - case ast_holder_type::List: { - _ast_list* list = static_cast<_ast_list*>(member); - for (auto obj : list->objects()) { - if (obj) { - if (func(obj)) return true; - } - } - break; - } - } - } - return false; -} - - -/** parses the given input. - @param i input. - @param g root rule of grammar. - @param el list of errors. - @param ud user data, passed to the parse procedures. - @return pointer to ast node created, or null if there was an error. - The return object must be deleted by the caller. -*/ -ast_node* parse(input& i, rule& g, error_list& el, void* ud) { - ast_stack st; - if (!parse(i, g, el, &st, ud)) { - for (auto node : st) { - delete node; - } - st.clear(); - return nullptr; - } - assert(st.size() == 1); - return st.front(); -} - - -} //namespace parserlib diff --git a/src/MoonP/ast.hpp b/src/MoonP/ast.hpp deleted file mode 100644 index 104202d..0000000 --- a/src/MoonP/ast.hpp +++ /dev/null @@ -1,571 +0,0 @@ -/* Copyright (c) 2012, Achilleas Margaritis, modified by Jin Li -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/ - -#pragma once - - -#include -#include -#include -#include - -#include "MoonP/parser.hpp" - - -namespace parserlib { - - -class ast_node; -template class ast_ptr; -template class ast_list; -template class ast; - - -/** type of AST node stack. -*/ -typedef std::vector ast_stack; -typedef std::list node_container; - - -template struct Counter { enum { value = Counter::value }; }; -template<> struct Counter<0> { enum { value = 0 }; }; - -#define COUNTER_READ Counter<__LINE__>::value -#define COUNTER_INC template<> struct Counter<__LINE__> { enum { value = Counter<__LINE__-1>::value + 1}; } - -class ast_node; -template -constexpr typename std::enable_if::value,int>::type -id(); - -enum class traversal { - Continue, - Return, - Stop -}; - -/** Base class for AST nodes. -*/ -class ast_node : public input_range { -public: - ast_node() : _ref(0) {} - - void retain() { - ++_ref; - } - - void release() { - --_ref; - if (_ref == 0) { - delete this; - } - } - - /** interface for filling the contents of the node - from a node stack. - @param st stack. - */ - virtual void construct(ast_stack&) {} - - /** interface for visiting AST tree use. - */ - virtual traversal traverse(const std::function& func); - - template - struct select_last { - using type = typename decltype((std::enable_if{}, ...))::type; - }; - template - using select_last_t = typename select_last::type; - - template - select_last_t* getByPath() { - int types[] = {id()...}; - return static_cast*>(getByTypeIds(std::begin(types), std::end(types))); - } - - virtual bool visitChild(const std::function& func); - - virtual int getId() const = 0; - - template - inline ast_ptr new_ptr() const { - auto item = new T; - item->m_begin.m_line = m_begin.m_line; - item->m_end.m_line = m_begin.m_line; - return ast_ptr(item); - } -private: - int _ref; - ast_node* getByTypeIds(int* begin, int* end); -}; - -template -constexpr typename std::enable_if::value,int>::type -id() { return 0; } - -template -T* ast_cast(ast_node* node) { - return node && id() == node->getId() ? static_cast(node) : nullptr; -} - -template -T* ast_to(ast_node* node) { - assert(node->getId() == id()); - return static_cast(node); -} - -template -bool ast_is(ast_node* node) { - if (!node) return false; - bool result = false; - int i = node->getId(); - using swallow = bool[]; - (void)swallow{result || (result = id() == i)...}; - return result; -} - -class ast_member; - -/** type of ast member vector. -*/ -typedef std::vector ast_member_vector; - - -/** base class for AST nodes with children. -*/ -class ast_container : public ast_node { -public: - void add_members(std::initializer_list members) { - for (auto member : members) { - m_members.push_back(member); - } - } - - /** returns the vector of AST members. - @return the vector of AST members. - */ - const ast_member_vector& members() const { - return m_members; - } - - /** Asks all members to construct themselves from the stack. - The members are asked to construct themselves in reverse order. - from a node stack. - @param st stack. - */ - virtual void construct(ast_stack& st) override; - - virtual traversal traverse(const std::function& func) override; - - virtual bool visitChild(const std::function& func) override; -private: - ast_member_vector m_members; - - friend class ast_member; -}; - -enum class ast_holder_type { - Pointer, - List -}; - -/** Base class for children of ast_container. -*/ -class ast_member { -public: - virtual ~ast_member() {} - - /** interface for filling the the member from a node stack. - @param st stack. - */ - virtual void construct(ast_stack& st) = 0; - - virtual bool accept(ast_node* node) = 0; - - virtual ast_holder_type get_type() const = 0; -}; - - -class _ast_ptr : public ast_member { -public: - _ast_ptr(ast_node* node) : m_ptr(node) { - if (node) node->retain(); - } - - virtual ~_ast_ptr() { - if (m_ptr) { - m_ptr->release(); - m_ptr = nullptr; - } - } - - ast_node* get() const { - return m_ptr; - } - - template - T* as() const { - return ast_cast(m_ptr); - } - - template - T* to() const { - assert(m_ptr && m_ptr->getId() == id()); - return static_cast(m_ptr); - } - - template - bool is() const { - return m_ptr && m_ptr->getId() == id(); - } - - void set(ast_node* node) { - if (node == m_ptr) { - return; - } else if (!node) { - if (m_ptr) m_ptr->release(); - m_ptr = nullptr; - } else { - assert(accept(node)); - if (m_ptr) m_ptr->release(); - m_ptr = node; - node->retain(); - } - } - - virtual ast_holder_type get_type() const override { - return ast_holder_type::Pointer; - } -protected: - ast_node* m_ptr; -}; - -/** pointer to an AST object. - It assumes ownership of the object. - It pops an object of the given type from the stack. - @tparam Required if true, the object is required. - @tparam T type of object to control. -*/ -template class ast_ptr : public _ast_ptr { -public: - ast_ptr(T* node = nullptr) : _ast_ptr(node) {} - - ast_ptr(const ast_ptr& other) : _ast_ptr(other.get()) {} - - ast_ptr& operator=(const ast_ptr& other) { - set(other.get()); - return *this; - } - - /** gets the underlying ptr value. - @return the underlying ptr value. - */ - T* get() const { - return static_cast(m_ptr); - } - - /** auto conversion to the underlying object ptr. - @return the underlying ptr value. - */ - operator T*() const { - return static_cast(m_ptr); - } - - /** member access. - @return the underlying ptr value. - */ - T* operator->() const { - assert(m_ptr); - return static_cast(m_ptr); - } - - /** Pops a node from the stack. - @param st stack. - @exception std::logic_error thrown if the node is not of the appropriate type; - thrown only if Required == true or if the stack is empty. - */ - virtual void construct(ast_stack& st) override { - // check the stack node - if (st.empty()) { - if (!Required) return; - throw std::logic_error("Invalid AST stack."); - } - ast_node* node = st.back(); - if (!ast_ptr::accept(node)) { - // if the object is not required, simply return - if (!Required) return; - // else if the object is mandatory, throw an exception - throw std::logic_error("Invalid AST node."); - } - st.pop_back(); - m_ptr = node; - node->retain(); - } -private: - virtual bool accept(ast_node* node) override { - return node && (std::is_same() || id() == node->getId()); - } -}; - -template class ast_sel : public _ast_ptr { -public: - ast_sel() : _ast_ptr(nullptr) {} - - ast_sel(const ast_sel& other) : _ast_ptr(other.get()) {} - - ast_sel& operator=(const ast_sel& other) { - set(other.get()); - return *this; - } - - operator ast_node*() const { - return m_ptr; - } - - ast_node* operator->() const { - assert(m_ptr); - return m_ptr; - } - - virtual void construct(ast_stack& st) override { - if (st.empty()) { - if (!Required) return; - throw std::logic_error("Invalid AST stack."); - } - ast_node* node = st.back(); - if (!ast_sel::accept(node)) { - if (!Required) return; - throw std::logic_error("Invalid AST node."); - } - st.pop_back(); - m_ptr = node; - node->retain(); - } -private: - virtual bool accept(ast_node* node) override { - if (!node) return false; - using swallow = bool[]; - bool result = false; - (void)swallow{result || (result = id() == node->getId())...}; - return result; - } -}; - -class _ast_list : public ast_member { -public: - ~_ast_list() { - clear(); - } - - inline ast_node* back() const { - return m_objects.back(); - } - - inline ast_node* front() const { - return m_objects.front(); - } - - inline size_t size() const { - return m_objects.size(); - } - - inline bool empty() const { - return m_objects.empty(); - } - - void push_back(ast_node* node) { - assert(node && accept(node)); - m_objects.push_back(node); - node->retain(); - } - - void push_front(ast_node* node) { - assert(node && accept(node)); - m_objects.push_front(node); - node->retain(); - } - - void pop_front() { - auto node = m_objects.front(); - m_objects.pop_front(); - node->release(); - } - - void pop_back() { - auto node = m_objects.back(); - m_objects.pop_back(); - node->release(); - } - - bool swap(ast_node* node, ast_node* other) { - for (auto it = m_objects.begin(); it != m_objects.end(); ++it) { - if (*it == node) { - *it = other; - other->retain(); - node->release(); - return true; - } - } - return false; - } - - const node_container& objects() const { - return m_objects; - } - - void clear() { - for(ast_node* obj : m_objects) { - if (obj) obj->release(); - } - m_objects.clear(); - } - - void dup(const _ast_list& src) { - for(ast_node* obj : src.m_objects) { - m_objects.push_back(obj); - obj->retain(); - } - } - - virtual ast_holder_type get_type() const override { - return ast_holder_type::List; - } -protected: - node_container m_objects; -}; - -/** A list of objects. - It pops objects of the given type from the ast stack, until no more objects can be popped. - It assumes ownership of objects. - @tparam Required if true, the object is required. - @tparam T type of object to control. -*/ -template class ast_list : public _ast_list { -public: - ast_list() { } - - ast_list(const ast_list& other) { - dup(other); - } - - ast_list& operator=(const ast_list& other) { - clear(); - dup(other); - return *this; - } - - /** Pops objects of type T from the stack until no more objects can be popped. - @param st stack. - */ - virtual void construct(ast_stack &st) override { - while (!st.empty()) { - ast_node* node = st.back(); - // if the object was not not of the appropriate type, - // end the list parsing - if (!ast_list::accept(node)) { - if (Required && m_objects.empty()) { - throw std::logic_error("Invalid AST node."); - } - return; - } - st.pop_back(); - // insert the object in the list, in reverse order - m_objects.push_front(node); - node->retain(); - } - if (Required && m_objects.empty()) { - throw std::logic_error("Invalid AST stack."); - } - } -private: - virtual bool accept(ast_node* node) override { - return node && (std::is_same() || id() == node->getId()); - } -}; - -template class ast_sel_list : public _ast_list { -public: - ast_sel_list() { } - - ast_sel_list(const ast_sel_list& other) { - dup(other); - } - - ast_sel_list& operator=(const ast_sel_list& other) { - clear(); - dup(other); - return *this; - } - - virtual void construct(ast_stack &st) override { - while (!st.empty()) { - ast_node* node = st.back(); - if (!ast_sel_list::accept(node)) { - if (Required && m_objects.empty()) { - throw std::logic_error("Invalid AST node."); - } - return; - } - st.pop_back(); - m_objects.push_front(node); - node->retain(); - } - if (Required && m_objects.empty()) { - throw std::logic_error("Invalid AST stack."); - } - } -private: - virtual bool accept(ast_node* node) override { - if (!node) return false; - using swallow = bool[]; - bool result = false; - (void)swallow{result || (result = id() == node->getId())...}; - return result; - } -}; - -/** AST function which creates an object of type T - and pushes it to the node stack. -*/ -template class ast { -public: - /** constructor. - @param r rule to attach the AST function to. - */ - ast(rule& r) { - r.set_parse_proc(&_parse_proc); - } -private: - //parse proc - static void _parse_proc(const pos& b, const pos& e, void* d) { - ast_stack* st = reinterpret_cast(d); - T* obj = new T; - obj->m_begin = b; - obj->m_end = e; - obj->construct(*st); - st->push_back(obj); - } -}; - - -/** parses the given input. - @param i input. - @param g root rule of grammar. - @param el list of errors. - @param ud user data, passed to the parse procedures. - @return pointer to ast node created, or null if there was an error. - The return object must be deleted by the caller. -*/ -ast_node* parse(input& i, rule& g, error_list& el, void* ud); - - -} //namespace parserlib diff --git a/src/MoonP/moon_ast.h b/src/MoonP/moon_ast.h deleted file mode 100644 index 6b738d4..0000000 --- a/src/MoonP/moon_ast.h +++ /dev/null @@ -1,750 +0,0 @@ -/* Copyright (c) 2021 Jin Li, http://www.luvfight.me - -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. */ - -#pragma once - -#include "MoonP/ast.hpp" - -namespace parserlib { - -#define AST_LEAF(type) \ -COUNTER_INC; \ -class type##_t : public ast_node \ -{ \ -public: \ - virtual int getId() const override { return COUNTER_READ; } - -#define AST_NODE(type) \ -COUNTER_INC; \ -class type##_t : public ast_container \ -{ \ -public: \ - virtual int getId() const override { return COUNTER_READ; } - -#define AST_MEMBER(type, ...) \ - type##_t() { \ - add_members({__VA_ARGS__}); \ - } - -#define AST_END(type) \ -}; \ -template<> constexpr int id() { return COUNTER_READ; } - -AST_LEAF(Num) -AST_END(Num) - -AST_LEAF(Name) -AST_END(Name) - -AST_NODE(Variable) - ast_ptr name; - AST_MEMBER(Variable, &name) -AST_END(Variable) - -AST_NODE(LabelName) - ast_ptr name; - AST_MEMBER(LabelName, &name) -AST_END(LabelName) - -AST_NODE(LuaKeyword) - ast_ptr name; - AST_MEMBER(LuaKeyword, &name) -AST_END(LuaKeyword) - -AST_LEAF(self) -AST_END(self) - -AST_NODE(self_name) - ast_ptr name; - AST_MEMBER(self_name, &name) -AST_END(self_name) - -AST_LEAF(self_class) -AST_END(self_class) - -AST_NODE(self_class_name) - ast_ptr name; - AST_MEMBER(self_class_name, &name) -AST_END(self_class_name) - -AST_NODE(SelfName) - ast_sel name; - AST_MEMBER(SelfName, &name) -AST_END(SelfName) - -AST_NODE(KeyName) - ast_sel name; - AST_MEMBER(KeyName, &name) -AST_END(KeyName) - -AST_LEAF(VarArg) -AST_END(VarArg) - -AST_LEAF(local_flag) -AST_END(local_flag) - -AST_LEAF(Seperator) -AST_END(Seperator) - -AST_NODE(NameList) - ast_ptr sep; - ast_list names; - AST_MEMBER(NameList, &sep, &names) -AST_END(NameList) - -class ExpListLow_t; -class TableBlock_t; - -AST_NODE(local_values) - ast_ptr nameList; - ast_sel valueList; - AST_MEMBER(local_values, &nameList, &valueList) -AST_END(local_values) - -AST_NODE(Local) - ast_sel item; - std::list forceDecls; - std::list decls; - bool collected = false; - bool defined = false; - AST_MEMBER(Local, &item) -AST_END(Local) - -class Assign_t; - -AST_NODE(LocalAttrib) - ast_ptr attrib; - ast_ptr nameList; - ast_ptr assign; - AST_MEMBER(LocalAttrib, &attrib, &nameList, &assign) -AST_END(LocalAttrib) - -AST_NODE(colon_import_name) - ast_ptr name; - AST_MEMBER(colon_import_name, &name) -AST_END(colon_import_name) - -class Exp_t; -class TableLit_t; - -AST_LEAF(import_literal_inner) -AST_END(import_literal_inner) - -AST_NODE(ImportLiteral) - ast_ptr sep; - ast_sel_list inners; - AST_MEMBER(ImportLiteral, &sep, &inners) -AST_END(ImportLiteral) - -AST_NODE(ImportFrom) - ast_ptr sep; - ast_sel_list names; - ast_ptr exp; - AST_MEMBER(ImportFrom, &sep, &names, &exp) -AST_END(ImportFrom) - -class MacroName_t; - -AST_NODE(macro_name_pair) - ast_ptr key; - ast_ptr value; - AST_MEMBER(macro_name_pair, &key, &value) -AST_END(macro_name_pair) - -AST_LEAF(import_all_macro) -AST_END(import_all_macro) - -class variable_pair_t; -class normal_pair_t; - -AST_NODE(ImportTabLit) - ast_ptr sep; - ast_sel_list items; - AST_MEMBER(ImportTabLit, &sep, &items) -AST_END(ImportTabLit) - -AST_NODE(ImportAs) - ast_ptr literal; - ast_sel target; - AST_MEMBER(ImportAs, &literal, &target) -AST_END(ImportAs) - -AST_NODE(Import) - ast_sel content; - AST_MEMBER(Import, &content) -AST_END(Import) - -AST_NODE(Label) - ast_ptr label; - AST_MEMBER(Label, &label) -AST_END(Label) - -AST_NODE(Goto) - ast_ptr label; - AST_MEMBER(Goto, &label) -AST_END(Goto) - -class FnArgsDef_t; - -AST_LEAF(fn_arrow_back) -AST_END(fn_arrow_back) - -class ChainValue_t; - -AST_NODE(Backcall) - ast_ptr argsDef; - ast_ptr arrow; - ast_ptr value; - AST_MEMBER(Backcall, &argsDef, &arrow, &value) -AST_END(Backcall) - -AST_NODE(ExpListLow) - ast_ptr sep; - ast_list exprs; - AST_MEMBER(ExpListLow, &sep, &exprs) -AST_END(ExpListLow) - -AST_NODE(ExpList) - ast_ptr sep; - ast_list exprs; - AST_MEMBER(ExpList, &sep, &exprs) -AST_END(ExpList) - -AST_NODE(Return) - bool allowBlockMacroReturn = false; - ast_ptr valueList; - AST_MEMBER(Return, &valueList) -AST_END(Return) - -class existential_op_t; -class Assign_t; -class Block_t; -class Statement_t; - -AST_NODE(With) - ast_ptr eop; - ast_ptr valueList; - ast_ptr assigns; - ast_sel body; - AST_MEMBER(With, &eop, &valueList, &assigns, &body) -AST_END(With) - -AST_NODE(SwitchCase) - ast_ptr valueList; - ast_sel body; - AST_MEMBER(SwitchCase, &valueList, &body) -AST_END(SwitchCase) - -AST_NODE(Switch) - ast_ptr target; - ast_ptr sep; - ast_list branches; - ast_sel lastBranch; - AST_MEMBER(Switch, &target, &sep, &branches, &lastBranch) -AST_END(Switch) - -AST_NODE(IfCond) - ast_ptr condition; - ast_ptr assign; - AST_MEMBER(IfCond, &condition, &assign) -AST_END(IfCond) - -AST_NODE(If) - ast_ptr sep; - ast_sel_list nodes; - AST_MEMBER(If, &sep, &nodes) -AST_END(If) - -AST_NODE(Unless) - ast_ptr sep; - ast_sel_list nodes; - AST_MEMBER(Unless, &sep, &nodes) -AST_END(Unless) - -AST_NODE(While) - ast_ptr condition; - ast_sel body; - AST_MEMBER(While, &condition, &body) -AST_END(While) - -class Body_t; - -AST_NODE(Repeat) - ast_ptr body; - ast_ptr condition; - AST_MEMBER(Repeat, &body, &condition) -AST_END(Repeat) - -AST_NODE(for_step_value) - ast_ptr value; - AST_MEMBER(for_step_value, &value) -AST_END(for_step_value) - -AST_NODE(For) - ast_ptr varName; - ast_ptr startValue; - ast_ptr stopValue; - ast_ptr stepValue; - ast_sel body; - AST_MEMBER(For, &varName, &startValue, &stopValue, &stepValue, &body) -AST_END(For) - -class AssignableNameList_t; -class star_exp_t; - -AST_NODE(ForEach) - ast_ptr nameList; - ast_sel loopValue; - ast_sel body; - AST_MEMBER(ForEach, &nameList, &loopValue, &body) -AST_END(ForEach) - -AST_NODE(Do) - ast_ptr body; - AST_MEMBER(Do, &body) -AST_END(Do) - -class CompInner_t; -class Statement_t; - -AST_NODE(Comprehension) - ast_sel value; - ast_ptr forLoop; - AST_MEMBER(Comprehension, &value, &forLoop) -AST_END(Comprehension) - -AST_NODE(comp_value) - ast_ptr value; - AST_MEMBER(comp_value, &value) -AST_END(comp_value) - -AST_NODE(TblComprehension) - ast_ptr key; - ast_ptr value; - ast_ptr forLoop; - AST_MEMBER(TblComprehension, &key, &value, &forLoop) -AST_END(TblComprehension) - -AST_NODE(star_exp) - ast_ptr value; - AST_MEMBER(star_exp, &value) -AST_END(star_exp) - -AST_NODE(CompForEach) - ast_ptr nameList; - ast_sel loopValue; - AST_MEMBER(CompForEach, &nameList, &loopValue) -AST_END(CompForEach) - -AST_NODE(CompFor) - ast_ptr varName; - ast_ptr startValue; - ast_ptr stopValue; - ast_ptr stepValue; - AST_MEMBER(CompFor, &varName, &startValue, &stopValue, &stepValue) -AST_END(CompFor) - -AST_NODE(CompInner) - ast_ptr sep; - ast_sel_list items; - AST_MEMBER(CompInner, &sep, &items) -AST_END(CompInner) - -class TableBlock_t; - -AST_NODE(Assign) - ast_ptr sep; - ast_sel_list values; - AST_MEMBER(Assign, &sep, &values) -AST_END(Assign) - -AST_LEAF(update_op) -AST_END(update_op) - -AST_NODE(Update) - ast_ptr op; - ast_ptr value; - AST_MEMBER(Update, &op, &value) -AST_END(Update) - -AST_LEAF(BinaryOperator) -AST_END(BinaryOperator) - -AST_LEAF(unary_operator) -AST_END(unary_operator) - -class AssignableChain_t; - -AST_NODE(Assignable) - ast_sel item; - AST_MEMBER(Assignable, &item) -AST_END(Assignable) - -class unary_exp_t; - -AST_NODE(exp_op_value) - ast_ptr op; - ast_list backcalls; - AST_MEMBER(exp_op_value, &op, &backcalls) -AST_END(exp_op_value) - -AST_NODE(Exp) - ast_ptr sep; - ast_list backcalls; - ast_list opValues; - AST_MEMBER(Exp, &sep, &backcalls, &opValues) -AST_END(Exp) - -class Parens_t; -class MacroName_t; - -AST_NODE(Callable) - ast_sel item; - AST_MEMBER(Callable, &item) -AST_END(Callable) - -AST_NODE(variable_pair) - ast_ptr name; - AST_MEMBER(variable_pair, &name) -AST_END(variable_pair) - -class DoubleString_t; -class SingleString_t; -class LuaString_t; - -AST_NODE(normal_pair) - ast_sel key; - ast_sel value; - AST_MEMBER(normal_pair, &key, &value) -AST_END(normal_pair) - -AST_NODE(simple_table) - ast_ptr sep; - ast_sel_list pairs; - AST_MEMBER(simple_table, &sep, &pairs) -AST_END(simple_table) - -class String_t; -class const_value_t; -class ClassDecl_t; -class unary_value_t; -class TableLit_t; -class FunLit_t; - -AST_NODE(SimpleValue) - ast_sel value; - AST_MEMBER(SimpleValue, &value) -AST_END(SimpleValue) - -AST_LEAF(LuaStringOpen) -AST_END(LuaStringOpen) - -AST_LEAF(LuaStringContent) -AST_END(LuaStringContent) - -AST_LEAF(LuaStringClose) -AST_END(LuaStringClose) - -AST_NODE(LuaString) - ast_ptr open; - ast_ptr content; - ast_ptr close; - AST_MEMBER(LuaString, &open, &content, &close) -AST_END(LuaString) - -AST_LEAF(SingleString) -AST_END(SingleString) - -AST_LEAF(double_string_inner) -AST_END(double_string_inner) - -AST_NODE(double_string_content) - ast_sel content; - AST_MEMBER(double_string_content, &content) -AST_END(double_string_content) - -AST_NODE(DoubleString) - ast_ptr sep; - ast_list segments; - AST_MEMBER(DoubleString, &sep, &segments) -AST_END(DoubleString) - -AST_NODE(String) - ast_sel str; - AST_MEMBER(String, &str) -AST_END(String) - -AST_NODE(DotChainItem) - ast_ptr name; - AST_MEMBER(DotChainItem, &name) -AST_END(DotChainItem) - -AST_NODE(ColonChainItem) - ast_sel name; - bool switchToDot = false; - AST_MEMBER(ColonChainItem, &name) -AST_END(ColonChainItem) - -class default_value_t; - -AST_NODE(Slice) - ast_sel startValue; - ast_sel stopValue; - ast_sel stepValue; - AST_MEMBER(Slice, &startValue, &stopValue, &stepValue) -AST_END(Slice) - -AST_NODE(Parens) - ast_ptr expr; - AST_MEMBER(Parens, &expr) -AST_END(Parens) - -AST_NODE(Invoke) - ast_ptr sep; - ast_sel_list args; - AST_MEMBER(Invoke, &sep, &args) -AST_END(Invoke) - -AST_LEAF(existential_op) -AST_END(existential_op) - -class InvokeArgs_t; - -AST_NODE(ChainValue) - ast_ptr sep; - ast_sel_list items; - AST_MEMBER(ChainValue, &sep, &items) -AST_END(ChainValue) - -AST_NODE(AssignableChain) - ast_ptr sep; - ast_sel_list items; - AST_MEMBER(AssignableChain, &sep, &items) -AST_END(AssignableChain) - -AST_NODE(Value) - ast_sel item; - AST_MEMBER(Value, &item) -AST_END(Value) - -AST_LEAF(default_value) -AST_END(default_value) - -AST_NODE(TableLit) - ast_ptr sep; - ast_sel_list values; - AST_MEMBER(TableLit, &sep, &values) -AST_END(TableLit) - -AST_NODE(TableBlockIndent) - ast_ptr sep; - ast_sel_list values; - AST_MEMBER(TableBlockIndent, &sep, &values) -AST_END(TableBlockIndent) - -AST_NODE(TableBlock) - ast_ptr sep; - ast_sel_list values; - AST_MEMBER(TableBlock, &sep, &values) -AST_END(TableBlock) - -AST_NODE(class_member_list) - ast_ptr sep; - ast_sel_list values; - AST_MEMBER(class_member_list, &sep, &values) -AST_END(class_member_list) - -AST_NODE(ClassBlock) - ast_ptr sep; - ast_sel_list contents; - AST_MEMBER(ClassBlock, &sep, &contents) -AST_END(ClassBlock) - -AST_NODE(ClassDecl) - ast_ptr name; - ast_ptr extend; - ast_ptr body; - AST_MEMBER(ClassDecl, &name, &extend, &body) -AST_END(ClassDecl) - -AST_NODE(global_values) - ast_ptr nameList; - ast_sel valueList; - AST_MEMBER(global_values, &nameList, &valueList) -AST_END(global_values) - -AST_LEAF(global_op) -AST_END(global_op) - -AST_NODE(Global) - ast_sel item; - AST_MEMBER(Global, &item) -AST_END(Global) - -AST_LEAF(export_default) -AST_END(export_default) - -class Macro_t; - -AST_NODE(Export) - ast_ptr def; - ast_sel target; - ast_ptr assign; - AST_MEMBER(Export, &def, &target, &assign) -AST_END(Export) - -AST_NODE(FnArgDef) - ast_sel name; - ast_ptr defaultValue; - AST_MEMBER(FnArgDef, &name, &defaultValue) -AST_END(FnArgDef) - -AST_NODE(FnArgDefList) - ast_ptr sep; - ast_list definitions; - ast_ptr varArg; - AST_MEMBER(FnArgDefList, &sep, &definitions, &varArg) -AST_END(FnArgDefList) - -AST_NODE(outer_var_shadow) - ast_ptr varList; - AST_MEMBER(outer_var_shadow, &varList) -AST_END(outer_var_shadow) - -AST_NODE(FnArgsDef) - ast_ptr defList; - ast_ptr shadowOption; - AST_MEMBER(FnArgsDef, &defList, &shadowOption) -AST_END(FnArgsDef) - -AST_LEAF(fn_arrow) -AST_END(fn_arrow) - -AST_NODE(FunLit) - ast_ptr argsDef; - ast_ptr arrow; - ast_ptr body; - AST_MEMBER(FunLit, &argsDef, &arrow, &body) -AST_END(FunLit) - -AST_NODE(MacroName) - ast_ptr name; - AST_MEMBER(MacroName, &name) -AST_END(MacroName) - -AST_NODE(MacroLit) - ast_ptr argsDef; - ast_ptr body; - AST_MEMBER(MacroLit, &argsDef, &body) -AST_END(MacroLit) - -AST_NODE(Macro) - ast_ptr name; - ast_ptr macroLit; - AST_MEMBER(Macro, &name, ¯oLit) -AST_END(Macro) - -AST_NODE(NameOrDestructure) - ast_sel item; - AST_MEMBER(NameOrDestructure, &item) -AST_END(NameOrDestructure) - -AST_NODE(AssignableNameList) - ast_ptr sep; - ast_list items; - AST_MEMBER(AssignableNameList, &sep, &items) -AST_END(AssignableNameList) - -AST_NODE(InvokeArgs) - ast_ptr sep; - ast_sel_list args; - AST_MEMBER(InvokeArgs, &sep, &args) -AST_END(InvokeArgs) - -AST_LEAF(const_value) -AST_END(const_value) - -AST_NODE(unary_value) - ast_list ops; - ast_ptr value; - AST_MEMBER(unary_value, &ops, &value) -AST_END(unary_value) - -AST_NODE(unary_exp) - ast_list ops; - ast_list expos; - AST_MEMBER(unary_exp, &ops, &expos) -AST_END(unary_exp) - -AST_NODE(ExpListAssign) - ast_ptr expList; - ast_sel action; - AST_MEMBER(ExpListAssign, &expList, &action) -AST_END(ExpListAssign) - -AST_NODE(if_line) - ast_ptr condition; - ast_ptr assign; - AST_MEMBER(if_line, &condition, &assign) -AST_END(if_line) - -AST_NODE(unless_line) - ast_ptr condition; - AST_MEMBER(unless_line, &condition) -AST_END(unless_line) - -AST_LEAF(BreakLoop) -AST_END(BreakLoop) - -AST_NODE(BackcallBody) - ast_ptr sep; - ast_list values; - AST_MEMBER(BackcallBody, &sep, &values) -AST_END(BackcallBody) - -AST_NODE(statement_appendix) - ast_sel item; - AST_MEMBER(statement_appendix, &item) -AST_END(statement_appendix) - -AST_LEAF(statement_sep) -AST_END(statement_sep) - -AST_NODE(Statement) - ast_sel content; - ast_ptr appendix; - ast_ptr needSep; - AST_MEMBER(Statement, &content, &appendix, &needSep) -AST_END(Statement) - -class Block_t; - -AST_NODE(Body) - ast_sel content; - AST_MEMBER(Body, &content) -AST_END(Body) - -AST_NODE(Block) - ast_ptr sep; - ast_list statements; - AST_MEMBER(Block, &sep, &statements) -AST_END(Block) - -AST_NODE(File) - ast_ptr block; - AST_MEMBER(File, &block) -AST_END(File) - -} // namespace parserlib diff --git a/src/MoonP/moon_compiler.cpp b/src/MoonP/moon_compiler.cpp deleted file mode 100644 index 24e4a97..0000000 --- a/src/MoonP/moon_compiler.cpp +++ /dev/null @@ -1,5622 +0,0 @@ -/* Copyright (c) 2021 Jin Li, http://www.luvfight.me - -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.*/ - -#include -#include -#include -#include -#include -#include -#include - -#include "MoonP/moon_parser.h" -#include "MoonP/moon_compiler.h" - -#ifndef MOONP_NO_MACRO - -extern "C" { -#include "lua.h" -#include "lauxlib.h" -#include "lualib.h" -} // extern "C" - -// name of table stored in lua registry -#define MOONP_MODULE "__moon_modules__" - -#if LUA_VERSION_NUM > 501 - #ifndef LUA_COMPAT_5_1 - #define lua_objlen lua_rawlen - #endif // LUA_COMPAT_5_1 -#endif // LUA_VERSION_NUM - -#endif // MOONP_NO_MACRO - -namespace MoonP { -using namespace std::string_view_literals; -using namespace parserlib; - -#define BLOCK_START do { -#define BLOCK_END } while (false); -#define BREAK_IF(cond) if (cond) break - -#define _DEFER(code,line) std::shared_ptr _defer_##line(nullptr, [&](auto){code;}) -#define DEFER(code) _DEFER(code,__LINE__) - -typedef std::list str_list; - -inline std::string s(std::string_view sv) { - return std::string(sv); -} - -const std::string_view version = "0.6.3"sv; -const std::string_view extension = "mp"sv; - -class MoonCompilerImpl { -public: -#ifndef MOONP_NO_MACRO - MoonCompilerImpl(lua_State* sharedState, - const std::function& luaOpen, - bool sameModule, - std::string_view moduleName = {}): - L(sharedState), - _luaOpen(luaOpen), - _moduleName(moduleName) { - BLOCK_START - BREAK_IF(!sameModule); - BREAK_IF(!L); - _sameModule = true; - int top = lua_gettop(L); - DEFER(lua_settop(L, top)); - lua_pushliteral(L, MOONP_MODULE); // MOONP_MODULE - lua_rawget(L, LUA_REGISTRYINDEX); // reg[MOONP_MODULE], tb - BREAK_IF(lua_istable(L, -1) == 0); - int idx = static_cast(lua_objlen(L, -1)); // idx = #tb, tb - BREAK_IF(idx == 0); - _useModule = true; - BLOCK_END - } - - ~MoonCompilerImpl() { - if (L && _stateOwner) { - lua_close(L); - L = nullptr; - } - } -#endif // MOONP_NO_MACRO - - CompileInfo compile(std::string_view codes, const MoonConfig& config) { - _config = config; -#ifndef MOONP_NO_MACRO - if (L) passOptions(); -#endif // MOONP_NO_MACRO - _info = _parser.parse(codes); - std::unique_ptr globals; - std::unique_ptr options; - if (!config.options.empty()) { - options = std::make_unique(config.options); - } - DEFER(clear()); - if (_info.node) { - try { - str_list out; - pushScope(); - _enableReturn.push(_info.moduleName.empty()); - _varArgs.push(true); - transformBlock(_info.node.to()->block, out, - config.implicitReturnRoot ? ExpUsage::Return : ExpUsage::Common, - nullptr, true); - popScope(); - if (config.lintGlobalVariable) { - globals = std::make_unique(); - for (const auto& var : _globals) { - int line,col; - std::tie(line,col) = var.second; - globals->push_back({var.first, line, col}); - } - } -#ifndef MOONP_NO_MACRO - if (L) { - int top = lua_gettop(L); - DEFER(lua_settop(L, top)); - if (!options) { - options = std::make_unique(); - } - pushMoonp("options"sv); - lua_pushnil(L); // options startKey - while (lua_next(L, -2) != 0) { // options key value - size_t len = 0; - auto pstr = lua_tolstring(L, -2, &len); - std::string key{pstr, len}; - pstr = lua_tolstring(L, -1, &len); - std::string value{pstr, len}; - (*options)[key] = value; - lua_pop(L, 1); // options key - } - } -#endif // MOONP_NO_MACRO - return {std::move(out.back()), Empty, std::move(globals), std::move(options)}; - } catch (const std::logic_error& error) { - return {Empty, error.what(), std::move(globals), std::move(options)}; - } - } else { - return {Empty, std::move(_info.error), std::move(globals), std::move(options)}; - } - } - - void clear() { - _indentOffset = 0; - _scopes.clear(); - _codeCache.clear(); - _buf.str(""); - _buf.clear(); - _joinBuf.str(""); - _joinBuf.clear(); - _globals.clear(); - _info = {}; - _varArgs = {}; - _withVars = {}; - _continueVars = {}; - _enableReturn = {}; -#ifndef MOONP_NO_MACRO - if (_useModule) { - _useModule = false; - if (!_sameModule) { - int top = lua_gettop(L); - DEFER(lua_settop(L, top)); - lua_pushliteral(L, MOONP_MODULE); // MOONP_MODULE - lua_rawget(L, LUA_REGISTRYINDEX); // reg[MOONP_MODULE], tb - int idx = static_cast(lua_objlen(L, -1)); - lua_pushnil(L); // tb nil - lua_rawseti(L, -2, idx); // tb[idx] = nil, tb - } - } -#endif // MOONP_NO_MACRO - } -private: -#ifndef MOONP_NO_MACRO - bool _stateOwner = false; - bool _useModule = false; - bool _sameModule = false; - lua_State* L = nullptr; - std::function _luaOpen; -#endif // MOONP_NO_MACRO - MoonConfig _config; - MoonParser _parser; - ParseInfo _info; - int _indentOffset = 0; - std::stack _varArgs; - std::stack _enableReturn; - std::stack _withVars; - std::stack _continueVars; - std::list> _codeCache; - std::unordered_map> _globals; - std::ostringstream _buf; - std::ostringstream _joinBuf; - const std::string _newLine = "\n"; - std::string _moduleName; - - enum class LocalMode { - None = 0, - Capital = 1, - Any = 2 - }; - enum class GlobalMode { - None = 0, - Capital = 1, - Any = 2 - }; - struct Scope { - GlobalMode mode = GlobalMode::None; - std::unique_ptr> vars; - std::unique_ptr> allows; - std::unique_ptr> globals; - }; - std::list _scopes; - static const std::string Empty; - - enum class MemType { - Builtin, - Common, - Property - }; - - struct ClassMember { - std::string item; - MemType type; - ast_node* node; - }; - - struct DestructItem { - bool isVariable = false; - std::string name; - std::string structure; - }; - - struct Destructure { - std::string value; - std::list items; - }; - - enum class ExpUsage { - Return, - Assignment, - Common, - Closure - }; - - void pushScope() { - _scopes.emplace_back(); - _scopes.back().vars = std::make_unique>(); - } - - void popScope() { - _scopes.pop_back(); - } - - bool isDefined(const std::string& name) const { - bool isDefined = false; - int mode = int(std::isupper(name[0]) ? GlobalMode::Capital : GlobalMode::Any); - const auto& current = _scopes.back(); - if (int(current.mode) >= mode) { - if (current.globals) { - if (current.globals->find(name) != current.globals->end()) { - isDefined = true; - current.vars->insert(name); - } - } else { - isDefined = true; - current.vars->insert(name); - } - } - decltype(_scopes.back().allows.get()) allows = nullptr; - for (auto it = _scopes.rbegin(); it != _scopes.rend(); ++it) { - if (it->allows) allows = it->allows.get(); - } - bool checkShadowScopeOnly = false; - if (allows) { - checkShadowScopeOnly = allows->find(name) == allows->end(); - } - for (auto it = _scopes.rbegin(); it != _scopes.rend(); ++it) { - auto vars = it->vars.get(); - if (vars->find(name) != vars->end()) { - isDefined = true; - break; - } - if (checkShadowScopeOnly && it->allows) break; - } - return isDefined; - } - - bool isSolidDefined(const std::string& name) const { - bool isDefined = false; - for (auto it = _scopes.rbegin(); it != _scopes.rend(); ++it) { - auto vars = it->vars.get(); - if (vars->find(name) != vars->end()) { - isDefined = true; - break; - } - } - return isDefined; - } - - void markVarShadowed() { - auto& scope = _scopes.back(); - scope.allows = std::make_unique>(); - } - - void markVarGlobal(GlobalMode mode, bool specified) { - auto& scope = _scopes.back(); - scope.mode = mode; - if (specified && !scope.globals) { - scope.globals = std::make_unique>(); - } - } - - void addGlobalVar(const std::string& name) { - auto& scope = _scopes.back(); - scope.globals->insert(name); - } - - void addToAllowList(const std::string& name) { - auto& scope = _scopes.back(); - scope.allows->insert(name); - } - - void forceAddToScope(const std::string& name) { - auto& scope = _scopes.back(); - scope.vars->insert(name); - } - - Scope& currentScope() { - return _scopes.back(); - } - - bool addToScope(const std::string& name) { - bool defined = isDefined(name); - if (!defined) { - auto& scope = currentScope(); - scope.vars->insert(name); - } - return !defined; - } - - std::string getUnusedName(std::string_view name) const { - int index = 0; - std::string newName; - do { - newName = s(name) + std::to_string(index); - index++; - } while (isSolidDefined(newName)); - return newName; - } - - const std::string nll(ast_node* node) const { - if (_config.reserveLineNumber) { - return s(" -- "sv) + std::to_string(node->m_begin.m_line + _config.lineOffset) + _newLine; - } else { - return _newLine; - } - } - - const std::string nlr(ast_node* node) const { - if (_config.reserveLineNumber) { - return s(" -- "sv) + std::to_string(node->m_end.m_line + _config.lineOffset) + _newLine; - } else { - return _newLine; - } - } - - void incIndentOffset() { - _indentOffset++; - } - - void decIndentOffset() { - _indentOffset--; - } - - std::string indent() const { - if (_config.useSpaceOverTab) { - return std::string((_scopes.size() - 1 + _indentOffset) * 2, ' '); - } else { - return std::string(_scopes.size() - 1 + _indentOffset, '\t'); - } - } - - std::string indent(int offset) const { - if (_config.useSpaceOverTab) { - return std::string((_scopes.size() - 1 + _indentOffset + offset) * 2, ' '); - } else { - return std::string(_scopes.size() - 1 + _indentOffset + offset, '\t'); - } - } - - std::string clearBuf() { - std::string str = _buf.str(); - _buf.str(""); - _buf.clear(); - return str; - } - - std::string join(const str_list& items) { - if (items.empty()) return Empty; - else if (items.size() == 1) return items.front(); - for (const auto& item : items) { - _joinBuf << item; - } - auto result = _joinBuf.str(); - _joinBuf.str(""); - _joinBuf.clear(); - return result; - } - - std::string join(const str_list& items, std::string_view sep) { - if (items.empty()) return Empty; - else if (items.size() == 1) return items.front(); - std::string sepStr = s(sep); - auto begin = ++items.begin(); - _joinBuf << items.front(); - for (auto it = begin; it != items.end(); ++it) { - _joinBuf << sepStr << *it; - } - auto result = _joinBuf.str(); - _joinBuf.str(""); - _joinBuf.clear(); - return result; - } - - unary_exp_t* singleUnaryExpFrom(ast_node* item) const { - Exp_t* exp = nullptr; - switch (item->getId()) { - case id(): - exp = static_cast(item); - break; - case id(): { - auto expList = static_cast(item); - if (expList->exprs.size() == 1) { - exp = static_cast(expList->exprs.front()); - } - break; - } - case id(): { - auto expList = static_cast(item); - if (expList->exprs.size() == 1) { - exp = static_cast(expList->exprs.front()); - } - break; - } - case id(): { - auto unary = static_cast(item); - if (unary->expos.size() == 1) { - return unary; - } - return nullptr; - } - default: break; - } - if (!exp) return nullptr; - BLOCK_START - BREAK_IF(!exp->opValues.empty()); - BREAK_IF(exp->backcalls.size() != 1); - auto unary = static_cast(exp->backcalls.back()); - BREAK_IF(unary->expos.size() != 1); - return unary; - BLOCK_END - return nullptr; - } - - Value_t* singleValueFrom(ast_node* item) const { - if (auto unary = singleUnaryExpFrom(item)) { - if (unary->ops.empty()) { - return static_cast(unary->expos.back()); - } - } - return nullptr; - } - - ast_ptr newExp(Value_t* value, ast_node* x) { - auto unary = x->new_ptr(); - unary->expos.push_back(value); - auto exp = x->new_ptr(); - exp->backcalls.push_back(unary); - return exp; - } - - ast_ptr newExp(Value_t* left, BinaryOperator_t* op, Value_t* right, ast_node* x) { - auto lunary = x->new_ptr(); - lunary->expos.push_back(left); - auto opValue = x->new_ptr(); - { - auto runary = x->new_ptr(); - runary->expos.push_back(right); - opValue->op.set(op); - opValue->backcalls.push_back(runary); - } - auto exp = x->new_ptr(); - exp->backcalls.push_back(lunary); - exp->opValues.push_back(opValue); - return exp; - } - - ast_ptr newExp(unary_exp_t* unary, ast_node* x) { - auto exp = x->new_ptr(); - exp->backcalls.push_back(unary); - return exp; - } - - SimpleValue_t* simpleSingleValueFrom(ast_node* expList) const { - auto value = singleValueFrom(expList); - if (value && value->item.is()) { - return static_cast(value->item.get()); - } - return nullptr; - } - - Statement_t* lastStatementFrom(ast_node* body) const { - switch (body->getId()) { - case id(): - return lastStatementFrom(static_cast(body)); - case id(): { - return static_cast(body); - } - default: assert(false); break; - } - return nullptr; - } - - Statement_t* lastStatementFrom(const node_container& stmts) const { - if (!stmts.empty()) { - auto it = stmts.end(); --it; - while (!static_cast(*it)->content && it != stmts.begin()) { - --it; - } - return static_cast(*it); - } - return nullptr; - } - - Statement_t* lastStatementFrom(Body_t* body) const { - if (auto stmt = body->content.as()) { - return stmt; - } else { - const auto& stmts = body->content.to()->statements.objects(); - return lastStatementFrom(stmts); - } - } - - Statement_t* lastStatementFrom(Block_t* block) const { - const auto& stmts = block->statements.objects(); - return lastStatementFrom(stmts); - } - - Exp_t* lastExpFromAssign(ast_node* action) { - switch (action->getId()) { - case id(): { - auto update = static_cast(action); - return update->value; - } - case id(): { - auto assign = static_cast(action); - return ast_cast(assign->values.back()); - } - } - return nullptr; - } - - Exp_t* lastExpFromStatement(Statement_t* stmt) { - if (!stmt->content) return nullptr; - switch (stmt->content->getId()) { - case id(): { - auto expListAssign = static_cast(stmt->content.get()); - if (auto action = expListAssign->action.get()) { - return lastExpFromAssign(action); - } else { - return static_cast(expListAssign->expList->exprs.back()); - } - } - case id(): { - auto exportNode = static_cast(stmt->content.get()); - if (auto action = exportNode->assign.get()) { - return lastExpFromAssign(action); - } else { - switch (exportNode->target->getId()) { - case id(): return exportNode->target.to(); - case id(): return static_cast(exportNode->target.to()->exprs.back()); - } - } - } - case id(): { - if (auto localValues = static_cast(stmt->content.get())->item.as()) { - if (auto expList = localValues->valueList.as()) { - return static_cast(expList->exprs.back()); - } - } - } - case id(): { - if (auto globalValues = static_cast(stmt->content.get())->item.as()) { - if (auto expList = globalValues->valueList.as()) { - return static_cast(expList->exprs.back()); - } - } - } - } - return nullptr; - } - - template - ast_ptr toAst(std::string_view codes, ast_node* parent) { - auto res = _parser.parse(s(codes)); - int line = parent->m_begin.m_line; - int col = parent->m_begin.m_line; - res.node->traverse([&](ast_node* node) { - node->m_begin.m_line = line; - node->m_end.m_line = line; - node->m_begin.m_col = col; - node->m_end.m_col = col; - return traversal::Continue; - }); - _codeCache.push_back(std::move(res.codes)); - return ast_ptr(res.node.template to()); - } - - bool isChainValueCall(ChainValue_t* chainValue) const { - return ast_is(chainValue->items.back()); - } - - enum class ChainType { - Common, - EndWithColon, - EndWithEOP, - HasEOP, - HasKeyword, - Macro - }; - - ChainType specialChainValue(ChainValue_t* chainValue) const { - if (isMacroChain(chainValue)) { - return ChainType::Macro; - } - if (ast_is(chainValue->items.back())) { - return ChainType::EndWithColon; - } - if (ast_is(chainValue->items.back())) { - return ChainType::EndWithEOP; - } - ChainType type = ChainType::Common; - for (auto item : chainValue->items.objects()) { - if (auto colonChain = ast_cast(item)) { - if (ast_is(colonChain->name)) { - type = ChainType::HasKeyword; - } - } else if (ast_is(item)) { - return ChainType::HasEOP; - } - } - return type; - } - - std::string singleVariableFrom(ChainValue_t* chainValue) { - BLOCK_START - BREAK_IF(!chainValue); - BREAK_IF(chainValue->items.size() != 1); - auto callable = ast_cast(chainValue->items.front()); - BREAK_IF(!callable); - ast_node* var = callable->item.as(); - if (!var) { - if (auto self = callable->item.as()) { - var = self->name.as(); - } - } - BREAK_IF(!var); - str_list tmp; - transformCallable(callable, tmp); - return tmp.back(); - BLOCK_END - return Empty; - } - - std::string singleVariableFrom(ast_node* expList) { - if (!ast_is(expList)) return Empty; - BLOCK_START - auto value = singleValueFrom(expList); - BREAK_IF(!value); - auto chainValue = value->item.as(); - BREAK_IF(!chainValue); - BREAK_IF(chainValue->items.size() != 1); - auto callable = ast_cast(chainValue->items.front()); - BREAK_IF(!callable || !(callable->item.is() || callable->getByPath())); - str_list tmp; - transformCallable(callable, tmp); - return tmp.back(); - BLOCK_END - return Empty; - } - - Variable_t* variableFrom(Exp_t* exp) { - BLOCK_START - auto value = singleValueFrom(exp); - BREAK_IF(!value); - auto chainValue = value->item.as(); - BREAK_IF(!chainValue); - BREAK_IF(chainValue->items.size() != 1); - auto callable = ast_cast(chainValue->items.front()); - BREAK_IF(!callable); - return callable->item.as(); - BLOCK_END - return nullptr; - } - - bool isAssignable(const node_container& chainItems) const { - if (chainItems.size() == 1) { - auto firstItem = chainItems.back(); - if (auto callable = ast_cast(firstItem)) { - switch (callable->item->getId()) { - case id(): - case id(): - return true; - } - } else if (firstItem->getId() == id()) { - return true; - } - } else { - auto lastItem = chainItems.back(); - switch (lastItem->getId()) { - case id(): - case id(): - return true; - } - } - return false; - } - - bool isAssignable(Exp_t* exp) const { - if (auto value = singleValueFrom(exp)) { - auto item = value->item.get(); - switch (item->getId()) { - case id(): - return true; - case id(): { - auto simpleValue = static_cast(item); - if (simpleValue->value.is()) { - return true; - } - return false; - } - case id(): { - auto chainValue = static_cast(item); - return isAssignable(chainValue->items.objects()); - } - } - } - return false; - } - - bool isAssignable(Assignable_t* assignable) const { - if (auto assignableChain = ast_cast(assignable->item)) { - return isAssignable(assignableChain->items.objects()); - } - return true; - } - - void checkAssignable(ExpList_t* expList) const { - for (auto exp_ : expList->exprs.objects()) { - Exp_t* exp = static_cast(exp_); - if (!isAssignable(exp)) { - throw std::logic_error(_info.errorMessage("left hand expression is not assignable"sv, exp)); - } - } - } - - bool isPureBackcall(Exp_t* exp) const { - return exp->opValues.empty() && exp->backcalls.size() > 1; - } - - bool isMacroChain(ChainValue_t* chainValue) const { - const auto& chainList = chainValue->items.objects(); - BLOCK_START - auto callable = ast_cast(chainList.front()); - BREAK_IF(!callable); - BREAK_IF(!callable->item.is()); - if (chainList.size() == 1 || - !ast_is(*(++chainList.begin()))) { - throw std::logic_error(_info.errorMessage("macro expression must be followed by arguments list"sv, callable)); - } - return true; - BLOCK_END - return false; - } - - void transformStatement(Statement_t* statement, str_list& out) { - auto x = statement; - if (statement->appendix) { - if (auto assignment = assignmentFrom(statement)) { - auto preDefine = getPredefine(assignment); - if (!preDefine.empty()) out.push_back(preDefine + nll(statement)); - } else if (auto local = statement->content.as()) { - if (!local->defined) { - local->defined = true; - transformLocalDef(local, out); - } - } - auto appendix = statement->appendix.get(); - switch (appendix->item->getId()) { - case id(): { - auto if_line = appendix->item.to(); - auto ifNode = x->new_ptr(); - - auto ifCond = x->new_ptr(); - ifCond->condition.set(if_line->condition); - ifCond->assign.set(if_line->assign); - ifNode->nodes.push_back(ifCond); - - auto stmt = x->new_ptr(); - stmt->content.set(statement->content); - ifNode->nodes.push_back(stmt); - - statement->appendix.set(nullptr); - auto simpleValue = x->new_ptr(); - simpleValue->value.set(ifNode); - auto value = x->new_ptr(); - value->item.set(simpleValue); - auto exp = newExp(value, x); - auto expList = x->new_ptr(); - expList->exprs.push_back(exp); - auto expListAssign = x->new_ptr(); - expListAssign->expList.set(expList); - statement->content.set(expListAssign); - break; - } - case id(): { - auto unless_line = appendix->item.to(); - auto unless = x->new_ptr(); - - auto ifCond = x->new_ptr(); - ifCond->condition.set(unless_line->condition); - unless->nodes.push_back(ifCond); - - auto stmt = x->new_ptr(); - stmt->content.set(statement->content); - unless->nodes.push_back(stmt); - - statement->appendix.set(nullptr); - auto simpleValue = x->new_ptr(); - simpleValue->value.set(unless); - auto value = x->new_ptr(); - value->item.set(simpleValue); - auto exp = newExp(value, x); - auto exprList = x->new_ptr(); - exprList->exprs.push_back(exp); - auto expListAssign = x->new_ptr(); - expListAssign->expList.set(exprList); - statement->content.set(expListAssign); - break; - } - case id(): { - auto compInner = appendix->item.to(); - auto comp = x->new_ptr(); - comp->forLoop.set(compInner); - auto stmt = x->new_ptr(); - stmt->content.set(statement->content); - comp->value.set(stmt); - auto simpleValue = x->new_ptr(); - simpleValue->value.set(comp); - auto value = x->new_ptr(); - value->item.set(simpleValue); - auto exp = newExp(value, x); - auto expList = x->new_ptr(); - expList->exprs.push_back(exp); - auto expListAssign = x->new_ptr(); - expListAssign->expList.set(expList); - statement->content.set(expListAssign); - statement->appendix.set(nullptr); - break; - } - default: assert(false); break; - } - } - auto content = statement->content.get(); - if (!content) { - out.push_back(Empty); - return; - } - switch (content->getId()) { - case id(): transformImport(static_cast(content), out); break; - case id(): transformWhile(static_cast(content), out); break; - case id(): transformRepeat(static_cast(content), out); break; - case id(): transformFor(static_cast(content), out); break; - case id(): transformForEach(static_cast(content), out); break; - case id(): transformReturn(static_cast(content), out); break; - case id(): transformLocal(static_cast(content), out); break; - case id(): transformGlobal(static_cast(content), out); break; - case id(): transformExport(static_cast(content), out); break; - case id(): transformMacro(static_cast(content), out, false); break; - case id(): transformBreakLoop(static_cast(content), out); break; - case id(): transformLabel(static_cast(content), out); break; - case id(): transformGoto(static_cast(content), out); break; - case id(): transformLocalAttrib(static_cast(content), out); break; - case id(): throw std::logic_error(_info.errorMessage("backcall chain must be following a value"sv, x)); break; - case id(): { - auto expListAssign = static_cast(content); - if (expListAssign->action) { - transformAssignment(expListAssign, out); - } else { - auto expList = expListAssign->expList.get(); - if (expList->exprs.objects().empty()) { - out.push_back(Empty); - break; - } - if (auto singleValue = singleValueFrom(expList)) { - if (auto simpleValue = singleValue->item.as()) { - auto value = simpleValue->value.get(); - bool specialSingleValue = true; - switch (value->getId()) { - case id(): transformIf(static_cast(value), out, ExpUsage::Common); break; - case id(): transformClassDecl(static_cast(value), out, ExpUsage::Common); break; - case id(): transformUnless(static_cast(value), out, ExpUsage::Common); break; - case id(): transformSwitch(static_cast(value), out, ExpUsage::Common); break; - case id(): transformWith(static_cast(value), out); break; - case id(): transformForEach(static_cast(value), out); break; - case id(): transformFor(static_cast(value), out); break; - case id(): transformWhile(static_cast(value), out); break; - case id(): transformDo(static_cast(value), out, ExpUsage::Common); break; - case id(): transformCompCommon(static_cast(value), out); break; - default: specialSingleValue = false; break; - } - if (specialSingleValue) { - break; - } - } - if (auto chainValue = singleValue->item.as()) { - if (isChainValueCall(chainValue)) { - transformChainValue(chainValue, out, ExpUsage::Common); - break; - } - } - } else if (expList->exprs.size() == 1){ - auto exp = static_cast(expList->exprs.back()); - if (isPureBackcall(exp)) { - transformExp(exp, out, ExpUsage::Common); - break; - } - } - throw std::logic_error(_info.errorMessage("expression list is not supported here"sv, expList)); - } - break; - } - default: assert(false); break; - } - if (statement->needSep && !out.back().empty()) { - auto index = std::string::npos; - if (_config.reserveLineNumber) { - index = out.back().rfind(" -- "sv); - } else { - index = out.back().find_last_not_of('\n'); - if (index != std::string::npos) index++; - } - if (index != std::string::npos) { - auto ending = out.back().substr(0, index); - auto ind = ending.find_last_of(" \t\n"sv); - if (ind != std::string::npos) { - ending = ending.substr(ind + 1); - } - if (Keywords.find(ending) == Keywords.end()) { - out.back().insert(index, ";"sv); - } - } - } - } - - str_list getAssignVars(ExpListAssign_t* assignment) { - str_list vars; - if (!assignment->action.is()) return vars; - for (auto exp : assignment->expList->exprs.objects()) { - auto var = singleVariableFrom(exp); - vars.push_back(var.empty() ? Empty : var); - } - return vars; - } - - str_list getAssignVars(With_t* with) { - str_list vars; - for (auto exp : with->valueList->exprs.objects()) { - auto var = singleVariableFrom(exp); - vars.push_back(var.empty() ? Empty : var); - } - return vars; - } - - str_list getAssignDefs(ExpList_t* expList) { - str_list preDefs; - for (auto exp_ : expList->exprs.objects()) { - auto exp = static_cast(exp_); - if (auto value = singleValueFrom(exp)) { - if (auto chain = value->item.as()) { - BLOCK_START - BREAK_IF(chain->items.size() != 1); - auto callable = ast_cast(chain->items.front()); - BREAK_IF(!callable); - std::string name; - if (auto var = callable->item.as()) { - name = _parser.toString(var); - } else if (auto self = callable->item.as()) { - if (self->name.is()) name = "self"sv; - } - BREAK_IF(name.empty()); - if (!isDefined(name)) { - preDefs.push_back(name); - } - BLOCK_END - } - } else { - throw std::logic_error(_info.errorMessage("left hand expression is not assignable"sv, exp)); - } - } - return preDefs; - } - - str_list transformAssignDefs(ExpList_t* expList, bool markDefined = true) { - str_list defs; - for (auto exp_ : expList->exprs.objects()) { - auto exp = static_cast(exp_); - if (auto value = singleValueFrom(exp)) { - if (auto chain = value->item.as()) { - BLOCK_START - BREAK_IF(chain->items.size() != 1); - auto callable = ast_cast(chain->items.front()); - BREAK_IF(!callable); - std::string name; - if (auto var = callable->item.as()) { - name = _parser.toString(var); - } else if (auto self = callable->item.as()) { - if (self->name.is()) name = "self"sv; - } - BREAK_IF(name.empty()); - if (!markDefined || addToScope(name)) { - defs.push_back(name); - } - BLOCK_END - } - } else { - throw std::logic_error(_info.errorMessage("left hand expression is not assignable"sv, exp)); - } - } - return defs; - } - - std::string getPredefine(const str_list& defs) { - if (defs.empty()) return Empty; - return indent() + s("local "sv) + join(defs, ", "sv); - } - - std::string getDestrucureDefine(ExpListAssign_t* assignment) { - auto info = extractDestructureInfo(assignment, true); - if (!info.first.empty()) { - for (const auto& destruct : info.first) { - str_list defs; - for (const auto& item : destruct.items) { - if (item.isVariable && addToScope(item.name)) { - defs.push_back(item.name); - } - } - if (!defs.empty()) _buf << indent() << "local "sv << join(defs,", "sv); - } - } - return clearBuf(); - } - - std::string getPredefine(ExpListAssign_t* assignment) { - auto preDefine = getDestrucureDefine(assignment); - if (preDefine.empty()) { - preDefine = getPredefine(transformAssignDefs(assignment->expList)); - } - return preDefine; - } - - ExpList_t* expListFrom(Statement_t* statement) { - if (auto expListAssign = statement->content.as()) { - if (!expListAssign->action) { - return expListAssign->expList.get(); - } - } - return nullptr; - } - - ExpListAssign_t* assignmentFrom(Statement_t* statement) { - if (auto expListAssign = statement->content.as()) { - if (expListAssign->action) { - return expListAssign; - } - } - return nullptr; - } - - void transformAssignment(ExpListAssign_t* assignment, str_list& out) { - checkAssignable(assignment->expList); - BLOCK_START - auto assign = ast_cast(assignment->action); - BREAK_IF(!assign); - BREAK_IF(assign->values.objects().size() != 1); - auto value = assign->values.objects().back(); - if (ast_is(value)) { - if (auto val = simpleSingleValueFrom(value)) { - value = val->value.get(); - } - } - switch (value->getId()) { - case id(): - case id(): { - auto expList = assignment->expList.get(); - str_list temp; - auto defs = transformAssignDefs(expList); - if (!defs.empty()) temp.push_back(getPredefine(defs) + nll(expList)); - switch (value->getId()) { - case id(): transformIf(static_cast(value), temp, ExpUsage::Assignment, expList); break; - case id(): transformUnless(static_cast(value), temp, ExpUsage::Assignment, expList); break; - } - out.push_back(join(temp)); - return; - } - case id(): { - auto switchNode = static_cast(value); - auto assignList = assignment->expList.get(); - std::string preDefine = getPredefine(assignment); - transformSwitch(switchNode, out, ExpUsage::Assignment, assignList); - out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); - return; - } - case id(): { - auto withNode = static_cast(value); - auto expList = assignment->expList.get(); - std::string preDefine = getPredefine(assignment); - transformWith(withNode, out, expList); - out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); - return; - } - case id(): { - auto expList = assignment->expList.get(); - auto doNode = static_cast(value); - std::string preDefine = getPredefine(assignment); - transformDo(doNode, out, ExpUsage::Assignment, expList); - out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); - return; - } - case id(): { - auto expList = assignment->expList.get(); - std::string preDefine = getPredefine(assignment); - transformComprehension(static_cast(value), out, ExpUsage::Assignment, expList); - out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); - return; - } - case id(): { - auto expList = assignment->expList.get(); - std::string preDefine = getPredefine(assignment); - transformTblComprehension(static_cast(value), out, ExpUsage::Assignment, expList); - out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); - return; - } - case id(): { - auto expList = assignment->expList.get(); - std::string preDefine = getPredefine(assignment); - transformForInPlace(static_cast(value), out, expList); - out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); - return; - } - case id(): { - auto expList = assignment->expList.get(); - std::string preDefine = getPredefine(assignment); - transformForEachInPlace(static_cast(value), out, expList); - out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); - return; - } - case id(): { - auto expList = assignment->expList.get(); - std::string preDefine = getPredefine(assignment); - transformClassDecl(static_cast(value), out, ExpUsage::Assignment, expList); - out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); - return; - } - case id(): { - auto expList = assignment->expList.get(); - std::string preDefine = getPredefine(assignment); - transformWhileInPlace(static_cast(value), out, expList); - out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); - return; - } - } - auto exp = ast_cast(value); - BREAK_IF(!exp); - if (isPureBackcall(exp)) { - auto expList = assignment->expList.get(); - transformExp(exp, out, ExpUsage::Assignment, expList); - return; - } - auto singleVal = singleValueFrom(exp); - BREAK_IF(!singleVal); - if (auto chainValue = singleVal->item.as()) { - auto type = specialChainValue(chainValue); - auto expList = assignment->expList.get(); - switch (type) { - case ChainType::HasEOP: - case ChainType::EndWithColon: { - std::string preDefine = getPredefine(assignment); - transformChainValue(chainValue, out, ExpUsage::Assignment, expList); - out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); - return; - } - case ChainType::HasKeyword: - case ChainType::Macro: - transformChainValue(chainValue, out, ExpUsage::Assignment, expList); - return; - case ChainType::Common: - case ChainType::EndWithEOP: - break; - } - } - BLOCK_END - auto info = extractDestructureInfo(assignment); - if (info.first.empty()) { - transformAssignmentCommon(assignment, out); - } else { - str_list temp; - for (const auto& destruct : info.first) { - if (destruct.items.size() == 1) { - auto& pair = destruct.items.front(); - _buf << indent(); - if (pair.isVariable && !isDefined(pair.name)) { - _buf << s("local "sv); - } - _buf << pair.name << " = "sv << info.first.front().value << pair.structure << nll(assignment); - addToScope(pair.name); - temp.push_back(clearBuf()); - } else if (_parser.match(destruct.value)) { - str_list defs, names, values; - for (const auto& item : destruct.items) { - if (item.isVariable && addToScope(item.name)) { - defs.push_back(item.name); - } - names.push_back(item.name); - values.push_back(item.structure); - } - for (auto& v : values) v.insert(0, destruct.value); - if (defs.empty()) { - _buf << indent() << join(names, ", "sv) << " = "sv << join(values, ", "sv) << nll(assignment); - } else { - _buf << indent() << "local "sv; - if (defs.size() != names.size()) { - _buf << join(defs,", "sv) << nll(assignment) << indent(); - } - _buf << join(names, ", "sv) << " = "sv << join(values, ", "sv) << nll(assignment); - } - temp.push_back(clearBuf()); - } else { - str_list defs, names, values; - for (const auto& item : destruct.items) { - if (item.isVariable && addToScope(item.name)) { - defs.push_back(item.name); - } - names.push_back(item.name); - values.push_back(item.structure); - } - if (!defs.empty()) _buf << indent() << "local "sv << join(defs,", "sv) << nll(assignment); - _buf << indent() << "do"sv << nll(assignment); - pushScope(); - auto objVar = getUnusedName("_obj_"sv); - for (auto& v : values) v.insert(0, objVar); - _buf << indent() << "local "sv << objVar << " = "sv << destruct.value << nll(assignment); - _buf << indent() << join(names, ", "sv) << " = "sv << join(values, ", "sv) << nll(assignment); - popScope(); - _buf << indent() << "end"sv << nll(assignment); - temp.push_back(clearBuf()); - } - } - if (info.second) { - transformAssignmentCommon(info.second, temp); - } - out.push_back(join(temp)); - } - } - - void transformAssignItem(ast_node* value, str_list& out) { - switch (value->getId()) { - case id(): transformWithClosure(static_cast(value), out); break; - case id(): transformIf(static_cast(value), out, ExpUsage::Closure); break; - case id(): transformSwitch(static_cast(value), out, ExpUsage::Closure); break; - case id(): transformTableBlock(static_cast(value), out); break; - case id(): transformExp(static_cast(value), out, ExpUsage::Closure); break; - default: assert(false); break; - } - } - - std::list destructFromExp(ast_node* node) { - const node_container* tableItems = nullptr; - if (ast_is(node)) { - auto item = singleValueFrom(node)->item.get(); - if (!item) throw std::logic_error(_info.errorMessage("invalid destructure value"sv, node)); - auto tbA = item->getByPath(); - if (tbA) { - tableItems = &tbA->values.objects(); - } else { - auto tbB = ast_cast(item); - if (tbB) tableItems = &tbB->pairs.objects(); - } - } else if (auto table = ast_cast(node)) { - tableItems = &table->values.objects(); - } - std::list pairs; - int index = 0; - for (auto pair : *tableItems) { - switch (pair->getId()) { - case id(): { - ++index; - if (!isAssignable(static_cast(pair))) { - throw std::logic_error(_info.errorMessage("can't destructure value"sv, pair)); - } - auto value = singleValueFrom(pair); - auto item = value->item.get(); - if (ast_is(item) || - item->getByPath()) { - auto subPairs = destructFromExp(pair); - for (auto& p : subPairs) { - pairs.push_back({p.isVariable, p.name, - s("["sv) + std::to_string(index) + s("]"sv) + p.structure}); - } - } else { - bool lintGlobal = _config.lintGlobalVariable; - _config.lintGlobalVariable = false; - auto exp = static_cast(pair); - auto varName = singleVariableFrom(exp); - bool isVariable = !varName.empty(); - if (!isVariable) { - str_list temp; - transformExp(exp, temp, ExpUsage::Closure); - varName = std::move(temp.back()); - } - _config.lintGlobalVariable = lintGlobal; - pairs.push_back({ - isVariable, - varName, - s("["sv) + std::to_string(index) + s("]"sv) - }); - } - break; - } - case id(): { - auto vp = static_cast(pair); - auto name = _parser.toString(vp->name); - if (Keywords.find(name) != Keywords.end()) { - pairs.push_back({true, name, s("[\""sv) + name + s("\"]"sv)}); - } else { - pairs.push_back({true, name, s("."sv) + name}); - } - break; - } - case id(): { - auto np = static_cast(pair); - auto key = np->key->getByPath(); - if (!key) throw std::logic_error(_info.errorMessage("invalid key for destructure"sv, np)); - if (auto exp = np->value.as()) { - if (!isAssignable(exp)) throw std::logic_error(_info.errorMessage("can't destructure value"sv, exp)); - auto item = singleValueFrom(exp)->item.get(); - if (ast_is(item) || - item->getByPath()) { - auto subPairs = destructFromExp(exp); - auto name = _parser.toString(key); - for (auto& p : subPairs) { - if (Keywords.find(name) != Keywords.end()) { - pairs.push_back({p.isVariable, p.name, - s("[\""sv) + name + s("\"]"sv) + p.structure}); - } else { - pairs.push_back({p.isVariable, p.name, - s("."sv) + name + p.structure}); - } - } - } else { - bool lintGlobal = _config.lintGlobalVariable; - _config.lintGlobalVariable = false; - auto varName = singleVariableFrom(exp); - bool isVariable = !varName.empty(); - if (!isVariable) { - str_list temp; - transformExp(exp, temp, ExpUsage::Closure); - varName = std::move(temp.back()); - } - _config.lintGlobalVariable = lintGlobal; - auto name = _parser.toString(key); - if (Keywords.find(name) != Keywords.end()) { - pairs.push_back({ - isVariable, - varName, - s("[\""sv) + name + s("\"]"sv) - }); - } else { - pairs.push_back({ - isVariable, - varName, - s("."sv) + name - }); - } - } - break; - } - if (np->value.is()) { - auto subPairs = destructFromExp(pair); - for (auto& p : subPairs) { - pairs.push_back({p.isVariable, p.name, - s("."sv) + _parser.toString(key) + p.structure}); - } - } - break; - } - default: assert(false); break; - } - } - return pairs; - } - - std::pair, ast_ptr> - extractDestructureInfo(ExpListAssign_t* assignment, bool varDefOnly = false) { - auto x = assignment; - std::list destructs; - if (!assignment->action.is()) return { destructs, nullptr }; - auto exprs = assignment->expList->exprs.objects(); - auto values = assignment->action.to()->values.objects(); - size_t size = std::max(exprs.size(),values.size()); - ast_ptr var; - if (exprs.size() < size) { - var = toAst("_"sv, x); - while (exprs.size() < size) exprs.emplace_back(var); - } - ast_ptr nullNode; - if (values.size() < size) { - nullNode = toAst("nil"sv, x); - while (values.size() < size) values.emplace_back(nullNode); - } - using iter = node_container::iterator; - std::vector> destructPairs; - str_list temp; - for (auto i = exprs.begin(), j = values.begin(); i != exprs.end(); ++i, ++j) { - auto expr = *i; - auto value = singleValueFrom(expr); - ast_node* destructNode = value->getByPath(); - if (destructNode || (destructNode = value->item.as())) { - destructPairs.push_back({i,j}); - auto& destruct = destructs.emplace_back(); - if (!varDefOnly) { - pushScope(); - transformAssignItem(*j, temp); - destruct.value = temp.back(); - temp.pop_back(); - popScope(); - } - auto pairs = destructFromExp(expr); - destruct.items = std::move(pairs); - if (destruct.items.size() == 1 && !singleValueFrom(*j)) { - destruct.value.insert(0, "("sv); - destruct.value.append(")"sv); - } - } - } - for (const auto& p : destructPairs) { - exprs.erase(p.first); - values.erase(p.second); - } - ast_ptr newAssignment; - if (!destructPairs.empty() && !exprs.empty()) { - auto x = assignment; - auto expList = x->new_ptr(); - auto newAssign = x->new_ptr(); - newAssign->expList.set(expList); - for (auto expr : exprs) expList->exprs.push_back(expr); - auto assign = x->new_ptr(); - for (auto value : values) assign->values.push_back(value); - newAssign->action.set(assign); - newAssignment = newAssign; - } - return {std::move(destructs), newAssignment}; - } - - void transformAssignmentCommon(ExpListAssign_t* assignment, str_list& out) { - auto x = assignment; - str_list temp; - auto expList = assignment->expList.get(); - auto action = assignment->action.get(); - switch (action->getId()) { - case id(): { - if (expList->exprs.size() > 1) throw std::logic_error(_info.errorMessage("can not apply update to multiple values"sv, expList)); - auto update = static_cast(action); - auto leftExp = static_cast(expList->exprs.objects().front()); - auto leftValue = singleValueFrom(leftExp); - if (!leftValue) throw std::logic_error(_info.errorMessage("left hand expression is not assignable"sv, leftExp)); - if (auto chain = leftValue->item.as()) { - auto tmpChain = x->new_ptr(); - for (auto item : chain->items.objects()) { - bool itemAdded = false; - BLOCK_START - auto exp = ast_cast(item); - BREAK_IF(!exp); - auto var = singleVariableFrom(exp); - BREAK_IF(!var.empty()); - auto upVar = getUnusedName("_update_"sv); - auto assignment = x->new_ptr(); - assignment->expList.set(toAst(upVar, x)); - auto assign = x->new_ptr(); - assign->values.push_back(exp); - assignment->action.set(assign); - transformAssignment(assignment, temp); - tmpChain->items.push_back(toAst(upVar, x)); - itemAdded = true; - BLOCK_END - if (!itemAdded) tmpChain->items.push_back(item); - } - chain->items.clear(); - chain->items.dup(tmpChain->items); - } - transformValue(leftValue, temp); - auto left = std::move(temp.back()); - temp.pop_back(); - transformExp(update->value, temp, ExpUsage::Closure); - auto right = std::move(temp.back()); - temp.pop_back(); - if (!singleValueFrom(update->value)) { - right = s("("sv) + right + s(")"sv); - } - _buf << join(temp) << indent() << left << " = "sv << left << - " "sv << _parser.toString(update->op) << " "sv << right << nll(assignment); - out.push_back(clearBuf()); - break; - } - case id(): { - bool oneLined = true; - auto assign = static_cast(action); - for (auto val : assign->values.objects()) { - if (auto value = singleValueFrom(val)) { - if (auto spValue = value->item.as()) { - if (spValue->value.is()) { - oneLined = false; - break; - } - } - } - } - auto defs = getAssignDefs(expList); - if (oneLined && defs.size() == expList->exprs.objects().size()) { - for (auto value : assign->values.objects()) { - transformAssignItem(value, temp); - } - std::string preDefine = getPredefine(defs); - for (const auto& def : defs) { - addToScope(def); - } - if (preDefine.empty()) { - transformExpList(expList, temp); - std::string left = std::move(temp.back()); - temp.pop_back(); - out.push_back(indent() + left + s(" = "sv) + join(temp, ", "sv) + nll(assignment)); - } else { - out.push_back(preDefine + s(" = "sv) + join(temp, ", "sv) + nll(assignment)); - } - } else { - std::string preDefine = getPredefine(defs); - for (const auto& def : defs) { - addToScope(def); - } - transformExpList(expList, temp); - std::string left = temp.back(); - temp.pop_back(); - for (auto value : assign->values.objects()) { - transformAssignItem(value, temp); - } - out.push_back((preDefine.empty() ? Empty : preDefine + nll(assignment)) + indent() + left + s(" = "sv) + join(temp, ", "sv) + nll(assignment)); - } - break; - } - default: assert(false); break; - } - } - - void transformCond(const node_container& nodes, str_list& out, ExpUsage usage, bool unless, ExpList_t* assignList) { - std::vector> ns(false); - for (auto it = nodes.rbegin(); it != nodes.rend(); ++it) { - ns.push_back(*it); - if (auto cond = ast_cast(*it)) { - if (*it != nodes.front() && cond->assign) { - auto x = *it; - auto newIf = x->new_ptr(); - for (auto j = ns.rbegin(); j != ns.rend(); ++j) { - newIf->nodes.push_back(*j); - } - ns.clear(); - auto simpleValue = x->new_ptr(); - simpleValue->value.set(newIf); - auto value = x->new_ptr(); - value->item.set(simpleValue); - auto exp = newExp(value, x); - auto expList = x->new_ptr(); - expList->exprs.push_back(exp); - auto expListAssign = x->new_ptr(); - expListAssign->expList.set(expList); - auto stmt = x->new_ptr(); - stmt->content.set(expListAssign); - ns.push_back(stmt.get()); - } - } - } - if (nodes.size() != ns.size()) { - auto x = ns.back(); - auto newIf = x->new_ptr(); - for (auto j = ns.rbegin(); j != ns.rend(); ++j) { - newIf->nodes.push_back(*j); - } - transformCond(newIf->nodes.objects(), out, usage, unless, assignList); - return; - } - str_list temp; - if (usage == ExpUsage::Closure) { - temp.push_back(s("(function()"sv) + nll(nodes.front())); - pushScope(); - _enableReturn.push(true); - } - std::list> ifCondPairs; - ifCondPairs.emplace_back(); - for (auto node : nodes) { - switch (node->getId()) { - case id(): - ifCondPairs.back().first = static_cast(node); - break; - case id(): - case id(): - ifCondPairs.back().second = node; - ifCondPairs.emplace_back(); - break; - default: assert(false); break; - } - } - auto assign = ifCondPairs.front().first->assign.get(); - bool storingValue = false; - ast_ptr extraAssignment; - if (assign) { - auto exp = ifCondPairs.front().first->condition.get(); - auto x = exp; - bool lintGlobal = _config.lintGlobalVariable; - _config.lintGlobalVariable = false; - auto var = singleVariableFrom(exp); - _config.lintGlobalVariable = lintGlobal; - if (var.empty()) { - storingValue = true; - auto desVar = getUnusedName("_des_"sv); - if (assign->values.objects().size() == 1) { - auto var = singleVariableFrom(assign->values.objects().front()); - if (!var.empty()) { - desVar = var; - storingValue = false; - } - } - if (storingValue) { - if (usage != ExpUsage::Closure) { - temp.push_back(indent() + s("do"sv) + nll(assign)); - pushScope(); - } - auto expList = toAst(desVar, x); - auto assignment = x->new_ptr(); - assignment->expList.set(expList); - assignment->action.set(assign); - transformAssignment(assignment, temp); - } - { - auto expList = x->new_ptr(); - expList->exprs.push_back(exp); - auto assignOne = x->new_ptr(); - auto valExp = toAst(desVar, x); - assignOne->values.push_back(valExp); - auto assignment = x->new_ptr(); - assignment->expList.set(expList); - assignment->action.set(assignOne); - extraAssignment.set(assignment); - ifCondPairs.front().first->condition.set(valExp); - } - } else { - if (!isDefined(var)) { - storingValue = true; - if (usage != ExpUsage::Closure) { - temp.push_back(indent() + s("do"sv) + nll(assign)); - pushScope(); - } - } - auto expList = x->new_ptr(); - expList->exprs.push_back(exp); - auto assignment = x->new_ptr(); - assignment->expList.set(expList); - assignment->action.set(assign); - transformAssignment(assignment, temp); - } - } - for (const auto& pair : ifCondPairs) { - if (pair.first) { - str_list tmp; - auto condition = pair.first->condition.get(); - if (unless) { - if (auto value = singleValueFrom(condition)) { - transformValue(value, tmp); - } else { - transformExp(condition, tmp, ExpUsage::Closure); - tmp.back() = s("("sv) + tmp.back() + s(")"sv); - } - tmp.back().insert(0, s("not "sv)); - unless = false; - } else { - transformExp(condition, tmp, ExpUsage::Closure); - } - _buf << indent(); - if (pair != ifCondPairs.front()) { - _buf << "else"sv; - } - _buf << "if "sv << tmp.back() << " then"sv << nll(condition); - temp.push_back(clearBuf()); - } - if (pair.second) { - if (!pair.first) { - temp.push_back(indent() + s("else"sv) + nll(pair.second)); - } - pushScope(); - if (pair == ifCondPairs.front() && extraAssignment) { - transformAssignment(extraAssignment, temp); - } - transform_plain_body(pair.second, temp, usage, assignList); - popScope(); - } - if (!pair.first) { - temp.push_back(indent() + s("end"sv) + nll(nodes.front())); - break; - } - } - if (storingValue && usage != ExpUsage::Closure) { - popScope(); - temp.push_back(indent() + s("end"sv) + nlr(nodes.front())); - } - if (usage == ExpUsage::Closure) { - _enableReturn.pop(); - popScope(); - temp.push_back(indent() + s("end)()"sv)); - } - out.push_back(join(temp)); - } - - void transformIf(If_t* ifNode, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { - transformCond(ifNode->nodes.objects(), out, usage, false, assignList); - } - - void transformUnless(Unless_t* unless, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { - transformCond(unless->nodes.objects(), out, usage, true, assignList); - } - - void transformExpList(ExpList_t* expList, str_list& out) { - str_list temp; - for (auto exp : expList->exprs.objects()) { - transformExp(static_cast(exp), temp, ExpUsage::Closure); - } - out.push_back(join(temp, ", "sv)); - } - - void transformExpListLow(ExpListLow_t* expListLow, str_list& out) { - str_list temp; - for (auto exp : expListLow->exprs.objects()) { - transformExp(static_cast(exp), temp, ExpUsage::Closure); - } - out.push_back(join(temp, ", "sv)); - } - - void transform_backcall_exp(const node_container& values, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { - if (values.size() == 1 && usage == ExpUsage::Closure) { - transform_unary_exp(static_cast(values.front()), out); - } else { - auto x = values.front(); - auto arg = newExp(static_cast(x), x); - auto begin = values.begin(); begin++; - for (auto it = begin; it != values.end(); ++it) { - auto unary = static_cast(*it); - auto value = static_cast(singleUnaryExpFrom(unary) ? unary->expos.back() : nullptr); - if (values.back() == *it && !unary->ops.empty() && usage == ExpUsage::Common) { - throw std::logic_error(_info.errorMessage("expression list is not supported here"sv, x)); - } - if (!value) throw std::logic_error(_info.errorMessage("backcall operator must be followed by chain value"sv, *it)); - if (auto chainValue = value->item.as()) { - if (isChainValueCall(chainValue)) { - auto last = chainValue->items.back(); - _ast_list* args = nullptr; - if (auto invoke = ast_cast(last)) { - args = &invoke->args; - } else { - args = &(ast_to(last)->args); - } - bool findPlaceHolder = false; - for (auto a : args->objects()) { - bool lintGlobal = _config.lintGlobalVariable; - _config.lintGlobalVariable = false; - auto name = singleVariableFrom(a); - _config.lintGlobalVariable = lintGlobal; - if (name == "_"sv) { - if (!findPlaceHolder) { - args->swap(a, arg); - findPlaceHolder = true; - } else { - throw std::logic_error(_info.errorMessage("backcall placeholder can be used only in one place"sv, a)); - } - } - } - if (!findPlaceHolder) { - args->push_front(arg); - } - } else { - auto invoke = x->new_ptr(); - invoke->args.push_front(arg); - chainValue->items.push_back(invoke); - } - arg.set(newExp(unary, x)); - } else { - throw std::logic_error(_info.errorMessage("backcall operator must be followed by chain value"sv, value)); - } - } - switch (usage) { - case ExpUsage::Assignment: { - auto assignment = x->new_ptr(); - auto assign = x->new_ptr(); - assign->values.push_back(arg); - assignment->action.set(assign); - assignment->expList.set(assignList); - transformAssignment(assignment, out); - return; - } - case ExpUsage::Common: { - auto value = singleValueFrom(arg); - if (value && value->item.is()) { - transformChainValue(value->item.to(), out, ExpUsage::Common); - } else { - transformExp(arg, out, ExpUsage::Closure); - out.back().insert(0, indent()); - out.back().append(nlr(x)); - } - return; - } - case ExpUsage::Return: { - auto ret = x->new_ptr(); - auto expListLow = x->new_ptr(); - expListLow->exprs.push_back(arg); - ret->valueList.set(expListLow); - transformReturn(ret, out); - return; - } - case ExpUsage::Closure: { - transformExp(arg, out, ExpUsage::Closure); - return; - } - default: assert(false); return; - } - } - } - - void transformExp(Exp_t* exp, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { - if (exp->opValues.empty()) { - transform_backcall_exp(exp->backcalls.objects(), out, usage, assignList); - return; - } - assert(usage == ExpUsage::Closure); - str_list temp; - transform_backcall_exp(exp->backcalls.objects(), temp, ExpUsage::Closure); - for (auto _opValue : exp->opValues.objects()) { - auto opValue = static_cast(_opValue); - transformBinaryOperator(opValue->op, temp); - transform_backcall_exp(opValue->backcalls.objects(), temp, ExpUsage::Closure); - } - out.push_back(join(temp, " "sv)); - } - - void transformValue(Value_t* value, str_list& out) { - auto item = value->item.get(); - switch (item->getId()) { - case id(): transformSimpleValue(static_cast(item), out); break; - case id(): transform_simple_table(static_cast(item), out); break; - case id(): transformChainValue(static_cast(item), out, ExpUsage::Closure); break; - case id(): transformString(static_cast(item), out); break; - default: assert(false); break; - } - } - - void transformCallable(Callable_t* callable, str_list& out, const ast_sel& invoke = {}) { - auto item = callable->item.get(); - switch (item->getId()) { - case id(): { - transformVariable(static_cast(item), out); - if (_config.lintGlobalVariable && !isDefined(out.back())) { - if (_globals.find(out.back()) == _globals.end()) { - _globals[out.back()] = {item->m_begin.m_line, item->m_begin.m_col}; - } - } - break; - } - case id(): { - transformSelfName(static_cast(item), out, invoke); - if (_config.lintGlobalVariable) { - std::string self("self"sv); - if (!isDefined(self)) { - if (_globals.find(self) == _globals.end()) { - _globals[self] = {item->m_begin.m_line, item->m_begin.m_col}; - } - } - } - break; - } - case id(): - if (_varArgs.empty() || !_varArgs.top()) { - throw std::logic_error(_info.errorMessage("cannot use '...' outside a vararg function near '...'"sv, item)); - } - out.push_back(s("..."sv)); - break; - case id(): transformParens(static_cast(item), out); break; - default: assert(false); break; - } - } - - void transformParens(Parens_t* parans, str_list& out) { - str_list temp; - transformExp(parans->expr, temp, ExpUsage::Closure); - out.push_back(s("("sv) + temp.front() + s(")"sv)); - } - - void transformSimpleValue(SimpleValue_t* simpleValue, str_list& out) { - auto value = simpleValue->value.get(); - switch (value->getId()) { - case id(): transform_const_value(static_cast(value), out); break; - case id(): transformIf(static_cast(value), out, ExpUsage::Closure); break; - case id(): transformUnless(static_cast(value), out, ExpUsage::Closure); break; - case id(): transformSwitch(static_cast(value), out, ExpUsage::Closure); break; - case id(): transformWithClosure(static_cast(value), out); break; - case id(): transformClassDeclClosure(static_cast(value), out); break; - case id(): transformForEachClosure(static_cast(value), out); break; - case id(): transformForClosure(static_cast(value), out); break; - case id(): transformWhileClosure(static_cast(value), out); break; - case id(): transformDo(static_cast(value), out, ExpUsage::Closure); break; - case id(): transform_unary_value(static_cast(value), out); break; - case id(): transformTblComprehension(static_cast(value), out, ExpUsage::Closure); break; - case id(): transformTableLit(static_cast(value), out); break; - case id(): transformComprehension(static_cast(value), out, ExpUsage::Closure); break; - case id(): transformFunLit(static_cast(value), out); break; - case id(): transformNum(static_cast(value), out); break; - default: assert(false); break; - } - } - - void transformFunLit(FunLit_t* funLit, str_list& out) { - _enableReturn.push(true); - _varArgs.push(false); - str_list temp; - bool isFatArrow = _parser.toString(funLit->arrow) == "=>"sv; - pushScope(); - if (isFatArrow) { - forceAddToScope(s("self"sv)); - } - if (auto argsDef = funLit->argsDef.get()) { - transformFnArgsDef(argsDef, temp); - if (funLit->body) { - transformBody(funLit->body, temp, ExpUsage::Return); - } else { - temp.push_back(Empty); - } - auto it = temp.begin(); - auto& args = *it; - auto& initArgs = *(++it); - auto& bodyCodes = *(++it); - _buf << "function("sv; - if (isFatArrow) { - _buf << "self"sv; - if (!args.empty()) _buf << ", "sv; - } - _buf << args << ')'; - if (!initArgs.empty() || !bodyCodes.empty()) { - _buf << nlr(argsDef) << initArgs << bodyCodes; - popScope(); - _buf << indent() << "end"sv; - } else { - popScope(); - _buf << " end"sv; - } - } else { - if (funLit->body) { - transformBody(funLit->body, temp, ExpUsage::Return); - } else { - temp.push_back(Empty); - } - auto& bodyCodes = temp.back(); - _buf << "function("sv << - (isFatArrow ? s("self"sv) : Empty) << - ')'; - if (!bodyCodes.empty()) { - _buf << nll(funLit) << bodyCodes; - popScope(); - _buf << indent() << "end"sv; - } else { - popScope(); - _buf << " end"sv; - } - } - out.push_back(clearBuf()); - _enableReturn.pop(); - _varArgs.pop(); - } - - void transformBody(Body_t* body, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { - auto x = body; - if (auto stmt = body->content.as()) { - auto block = x->new_ptr(); - block->statements.push_back(stmt); - transformBlock(block, out, usage, assignList); - } else { - transformBlock(body->content.to(), out, usage, assignList); - } - } - - void transformBlock(Block_t* block, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr, bool isRoot = false) { - const auto& nodes = block->statements.objects(); - LocalMode mode = LocalMode::None; - Local_t* any = nullptr, *capital = nullptr; - for (auto it = nodes.begin(); it != nodes.end(); ++it) { - auto node = *it; - auto stmt = static_cast(node); - if (auto backcallBody = stmt->content.as()) { - auto x = stmt; - bool cond = false; - BLOCK_START - BREAK_IF(it == nodes.begin()); - auto last = it; --last; - auto lst = static_cast(*last); - if (lst->appendix) { - throw std::logic_error(_info.errorMessage("statement decorator must be placed at the end of backcall chain"sv, lst->appendix.get())); - } - lst->appendix.set(stmt->appendix); - stmt->appendix.set(nullptr); - lst->needSep.set(stmt->needSep); - stmt->needSep.set(nullptr); - auto exp = lastExpFromStatement(lst); - BREAK_IF(!exp); - for (auto val : backcallBody->values.objects()) { - exp->backcalls.push_back(val); - } - cond = true; - BLOCK_END - if (!cond) throw std::logic_error(_info.errorMessage("backcall chain must be following a value"sv, x)); - stmt->content.set(nullptr); - auto next = it; ++next; - BLOCK_START - BREAK_IF(next == nodes.end()); - BREAK_IF(!static_cast(*next)->content.as()); - throw std::logic_error(_info.errorMessage("indent mismatch in backcall chain"sv, *next)); - BLOCK_END - } else if (auto backcall = stmt->content.as()) { - auto x = *nodes.begin(); - auto newBlock = x->new_ptr(); - if (it != nodes.begin()) { - for (auto i = nodes.begin(); i != it; ++i) { - newBlock->statements.push_back(*i); - } - } - x = backcall; - ast_ptr arg; - { - auto block = x->new_ptr(); - auto next = it; ++next; - if (next != nodes.end()) { - for (auto i = next; i != nodes.end(); ++i) { - block->statements.push_back(*i); - } - } - auto body = x->new_ptr(); - body->content.set(block); - auto funLit = x->new_ptr(); - funLit->argsDef.set(backcall->argsDef); - auto arrow = _parser.toString(backcall->arrow); - funLit->arrow.set(toAst(arrow == "<-"sv ? "->"sv : "=>"sv, x)); - funLit->body.set(body); - auto simpleValue = x->new_ptr(); - simpleValue->value.set(funLit); - auto value = x->new_ptr(); - value->item.set(simpleValue); - arg = newExp(value, x); - } - if (isChainValueCall(backcall->value)) { - auto last = backcall->value->items.back(); - _ast_list* args = nullptr; - if (auto invoke = ast_cast(last)) { - args = &invoke->args; - } else { - args = &(ast_to(last)->args); - } - bool findPlaceHolder = false; - for (auto a : args->objects()) { - bool lintGlobal = _config.lintGlobalVariable; - _config.lintGlobalVariable = false; - auto name = singleVariableFrom(a); - _config.lintGlobalVariable = lintGlobal; - if (name == "_"sv) { - if (!findPlaceHolder) { - args->swap(a, arg); - findPlaceHolder = true; - } else { - throw std::logic_error(_info.errorMessage("backcall placeholder can be used only in one place"sv, a)); - } - } - } - if (!findPlaceHolder) { - args->push_back(arg); - } - } else { - auto invoke = x->new_ptr(); - invoke->args.push_back(arg); - backcall->value->items.push_back(invoke); - } - auto newStmt = x->new_ptr(); - { - auto chainValue = backcall->value.get(); - auto value = x->new_ptr(); - value->item.set(chainValue); - auto exp = newExp(value, x); - auto expList = x->new_ptr(); - expList->exprs.push_back(exp); - auto expListAssign = x->new_ptr(); - expListAssign->expList.set(expList); - newStmt->content.set(expListAssign); - newStmt->appendix.set(stmt->appendix); - newBlock->statements.push_back(newStmt); - } - transformBlock(newBlock, out, usage, assignList, isRoot); - return; - } - if (auto local = stmt->content.as()) { - if (!local->collected) { - local->collected = true; - switch (local->item->getId()) { - case id(): { - auto flag = local->item.to(); - LocalMode newMode = _parser.toString(flag) == "*"sv ? LocalMode::Any : LocalMode::Capital; - if (int(newMode) > int(mode)) { - mode = newMode; - } - if (mode == LocalMode::Any) { - if (!any) any = local; - if (!capital) capital = local; - } else { - if (!capital) capital = local; - } - break; - } - case id(): { - auto values = local->item.to(); - for (auto name : values->nameList->names.objects()) { - local->forceDecls.push_back(_parser.toString(name)); - } - break; - } - } - } - } else if (mode != LocalMode::None) { - ClassDecl_t* classDecl = nullptr; - if (auto assignment = assignmentFrom(stmt)) { - auto vars = getAssignVars(assignment); - for (const auto& var : vars) { - if (var.empty()) continue; - if (std::isupper(var[0]) && capital) { - capital->decls.push_back(var); - } else if (any) { - any->decls.push_back(var); - } - } - auto info = extractDestructureInfo(assignment, true); - if (!info.first.empty()) { - for (const auto& destruct : info.first) - for (const auto& item : destruct.items) - if (item.isVariable) { - if (std::isupper(item.name[0]) && capital) { capital->decls.push_back(item.name); - } else if (any) { - any->decls.push_back(item.name); - } - } - } - if (info.second) { - auto defs = transformAssignDefs(info.second->expList, false); - for (const auto& def : defs) { - if (std::isupper(def[0]) && capital) { capital->decls.push_back(def); - } else if (any) { - any->decls.push_back(def); - } - } - } - BLOCK_START - auto assign = assignment->action.as(); - BREAK_IF(!assign); - BREAK_IF(assign->values.objects().size() != 1); - auto exp = ast_cast(assign->values.objects().front()); - BREAK_IF(!exp); - auto value = singleValueFrom(exp); - classDecl = value->getByPath(); - BLOCK_END - } else if (auto expList = expListFrom(stmt)) { - auto value = singleValueFrom(expList); - classDecl = value->getByPath(); - } - if (classDecl) { - if (auto variable = classDecl->name->item.as()) { - auto className = _parser.toString(variable); - if (!className.empty()) { - if (std::isupper(className[0]) && capital) { - capital->decls.push_back(className); - } else if (any) { - any->decls.push_back(className); - } - } - } - } - } - } - if (isRoot && !_info.moduleName.empty()) { - block->statements.push_front(toAst(_info.moduleName + s(_info.exportDefault ? "=nil"sv : "={}"sv), block)); - } - switch (usage) { - case ExpUsage::Closure: - case ExpUsage::Return: { - BLOCK_START - BREAK_IF(isRoot && !_info.moduleName.empty()); - auto last = lastStatementFrom(nodes); - BREAK_IF(!last); - auto x = last; - auto expList = expListFrom(last); - BREAK_IF(!expList || - (last->appendix && - last->appendix->item.is())); - auto expListLow = x->new_ptr(); - expListLow->exprs.dup(expList->exprs); - auto returnNode = x->new_ptr(); - returnNode->valueList.set(expListLow); - returnNode->allowBlockMacroReturn = true; - last->content.set(returnNode); - last->needSep.set(nullptr); - auto bLast = ++nodes.rbegin(); - if (bLast != nodes.rend()) { - bool isMacro = false; - BLOCK_START - BREAK_IF(expListLow->exprs.size() != 1); - auto exp = static_cast(expListLow->exprs.back()); - BREAK_IF(!exp->opValues.empty()); - auto chainValue = exp->getByPath(); - BREAK_IF(!chainValue); - isMacro = isMacroChain(chainValue); - BLOCK_END - if (!isMacro) { - ast_to(*bLast)->needSep.set(nullptr); - } - } - BLOCK_END - break; - } - case ExpUsage::Assignment: { - auto last = lastStatementFrom(block); - if (!last) return; - bool lastAssignable = expListFrom(last) || ast_is(last->content); - if (lastAssignable) { - auto x = last; - auto newAssignment = x->new_ptr(); - newAssignment->expList.set(assignList); - auto assign = x->new_ptr(); - if (auto valueList = last->content.as()) { - assign->values.dup(valueList->expList->exprs); - } else { - auto simpleValue = x->new_ptr(); - simpleValue->value.set(last->content); - auto value = x->new_ptr(); - value->item.set(simpleValue); - auto exp = newExp(value, x); - assign->values.push_back(exp); - } - newAssignment->action.set(assign); - last->content.set(newAssignment); - last->needSep.set(nullptr); - auto bLast = ++nodes.rbegin(); - if (bLast != nodes.rend()) { - static_cast(*bLast)->needSep.set(nullptr); - } - } - break; - } - default: break; - } - if (!nodes.empty()) { - str_list temp; - for (auto node : nodes) { - transformStatement(static_cast(node), temp); - } - out.push_back(join(temp)); - } else { - out.push_back(Empty); - } - if (isRoot && !_info.moduleName.empty()) { - out.back().append(indent() + s("return "sv) + _info.moduleName + nlr(block)); - } - } - -#ifndef MOONP_NO_MACRO - void passOptions() { - if (!_config.options.empty()) { - pushMoonp("options"sv); // options - for (const auto& option : _config.options) { - lua_pushlstring(L, option.second.c_str(), option.second.size()); - lua_setfield(L, -2, option.first.c_str()); - } - lua_pop(L, 1); - } - } - - void pushCurrentModule() { - if (_useModule) { - lua_pushliteral(L, MOONP_MODULE); // MOONP_MODULE - lua_rawget(L, LUA_REGISTRYINDEX); // reg[MOONP_MODULE], tb - int idx = static_cast(lua_objlen(L, -1)); // idx = #tb, tb - lua_rawgeti(L, -1, idx); // tb[idx], tb cur - lua_remove(L, -2); // cur - return; - } - _useModule = true; - if (!L) { - L = luaL_newstate(); - if (_luaOpen) { - _luaOpen(static_cast(L)); - } - passOptions(); - _stateOwner = true; - } - lua_pushliteral(L, MOONP_MODULE); // MOONP_MODULE - lua_rawget(L, LUA_REGISTRYINDEX); // reg[MOONP_MODULE], tb - if (lua_isnil(L, -1) != 0) { // tb == nil - lua_pop(L, 1); - lua_newtable(L); // tb - lua_pushliteral(L, MOONP_MODULE); // tb MOONP_MODULE - lua_pushvalue(L, -2); // tb MOONP_MODULE tb - lua_rawset(L, LUA_REGISTRYINDEX); // reg[MOONP_MODULE] = tb, tb - } // tb - int idx = static_cast(lua_objlen(L, -1)); // idx = #tb, tb - lua_newtable(L); // tb cur - lua_pushvalue(L, -1); // tb cur cur - lua_rawseti(L, -3, idx + 1); // tb[idx + 1] = cur, tb cur - lua_remove(L, -2); // cur - } - - void pushMoonp(std::string_view name) { - lua_getglobal(L, "package"); // package - lua_getfield(L, -1, "loaded"); // package loaded - lua_getfield(L, -1, "moonp"); // package loaded moonp - lua_pushlstring(L, &name.front(), name.size()); // package loaded moonp name - lua_gettable(L, -2); // loaded[name], package loaded moonp item - lua_insert(L, -4); // item package loaded moonp - lua_pop(L, 3); // item - } - - bool isModuleLoaded(std::string_view name) { - int top = lua_gettop(L); - DEFER(lua_settop(L, top)); - lua_pushliteral(L, MOONP_MODULE); // MOONP_MODULE - lua_rawget(L, LUA_REGISTRYINDEX); // modules - lua_pushlstring(L, &name.front(), name.size()); - lua_rawget(L, -2); // modules module - if (lua_isnil(L, -1) != 0) { - return false; - } - return true; - } - - void pushModuleTable(std::string_view name) { - lua_pushliteral(L, MOONP_MODULE); // MOONP_MODULE - lua_rawget(L, LUA_REGISTRYINDEX); // modules - lua_pushlstring(L, &name.front(), name.size()); - lua_rawget(L, -2); // modules module - if (lua_isnil(L, -1) != 0) { - lua_pop(L, 1); - lua_newtable(L); // modules module - lua_pushlstring(L, &name.front(), name.size()); - lua_pushvalue(L, -2); // modules module name module - lua_rawset(L, -4); // modules[name] = module, modules module - } - lua_remove(L, -2); // module - } - - void pushOptions(int lineOffset) { - lua_newtable(L); - lua_pushliteral(L, "lint_global"); - lua_pushboolean(L, 0); - lua_rawset(L, -3); - lua_pushliteral(L, "implicit_return_root"); - lua_pushboolean(L, 1); - lua_rawset(L, -3); - lua_pushliteral(L, "reserve_line_number"); - lua_pushboolean(L, 1); - lua_rawset(L, -3); - lua_pushliteral(L, "same_module"); - lua_pushboolean(L, 1); - lua_rawset(L, -3); - lua_pushliteral(L, "line_offset"); - lua_pushinteger(L, lineOffset); - lua_rawset(L, -3); - } - - void transformMacro(Macro_t* macro, str_list& out, bool exporting) { - if (_scopes.size() > 1) { - throw std::logic_error(_info.errorMessage("can not define macro outside the root block"sv, macro)); - } - auto macroName = _parser.toString(macro->name); - auto argsDef = macro->macroLit->argsDef.get(); - str_list newArgs; - if (argsDef) { - for (auto def_ : argsDef->definitions.objects()) { - auto def = static_cast(def_); - if (def->name.is()) { - throw std::logic_error(_info.errorMessage("self name is not supported for macro function argument"sv, def->name)); - } else { - std::string defVal; - if (def->defaultValue) { - defVal = _parser.toString(def->defaultValue); - Utils::trim(defVal); - defVal.insert(0, "=[==========["sv); - defVal.append("]==========]"sv); - } - newArgs.emplace_back(_parser.toString(def->name) + defVal); - } - } - if (argsDef->varArg) { - newArgs.emplace_back(_parser.toString(argsDef->varArg)); - } - } - _buf << "("sv << join(newArgs, ","sv) << ")->"sv; - _buf << _parser.toString(macro->macroLit->body); - auto macroCodes = clearBuf(); - _buf << "=(macro "sv << macroName << ")"; - auto chunkName = clearBuf(); - pushCurrentModule(); // cur - int top = lua_gettop(L) - 1; - DEFER(lua_settop(L, top)); - pushMoonp("loadstring"sv); // cur loadstring - lua_pushlstring(L, macroCodes.c_str(), macroCodes.size()); // cur loadstring codes - lua_pushlstring(L, chunkName.c_str(), chunkName.size()); // cur loadstring codes chunk - pushOptions(macro->m_begin.m_line - 1); // cur loadstring codes chunk options - if (lua_pcall(L, 3, 2, 0) != 0) { // loadstring(codes,chunk,options), cur f err - std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage(s("failed to load macro codes\n"sv) + err, macro->macroLit)); - } // cur f err - if (lua_isnil(L, -2) != 0) { // f == nil, cur f err - std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage(s("failed to load macro codes, at (macro "sv) + macroName + s("): "sv) + err, macro->macroLit)); - } - lua_pop(L, 1); // cur f - pushMoonp("pcall"sv); // cur f pcall - lua_insert(L, -2); // cur pcall f - if (lua_pcall(L, 1, 2, 0) != 0) { // f(), cur success macro - std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage(s("failed to generate macro function\n"sv) + err, macro->macroLit)); - } // cur success res - if (lua_toboolean(L, -2) == 0) { - std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage(s("failed to generate macro function\n"sv) + err, macro->macroLit)); - } // cur true macro - lua_remove(L, -2); // cur macro - if (exporting && !_moduleName.empty()) { - pushModuleTable(_moduleName); // cur macro module - lua_pushlstring(L, macroName.c_str(), macroName.size()); // cur macro module name - lua_pushvalue(L, -3); // cur macro module name macro - lua_rawset(L, -3); // cur macro module - lua_pop(L, 1); - } // cur macro - lua_pushlstring(L, macroName.c_str(), macroName.size()); // cur macro name - lua_insert(L, -2); // cur name macro - lua_rawset(L, -3); // cur[name] = macro, cur - out.push_back(Empty); - } -#else - void transformMacro(Macro_t* macro, str_list&, bool) { - throw std::logic_error(_info.errorMessage("macro feature not supported"sv, macro)); - } -#endif // MOONP_NO_MACRO - - void transformReturn(Return_t* returnNode, str_list& out) { - if (!_enableReturn.top()) { - ast_node* target = returnNode->valueList.get(); - if (!target) target = returnNode; - throw std::logic_error(_info.errorMessage("illegal return statement here"sv, target)); - } - if (auto valueList = returnNode->valueList.get()) { - if (valueList->exprs.size() == 1) { - auto exp = static_cast(valueList->exprs.back()); - if (isPureBackcall(exp)) { - transformExp(exp, out, ExpUsage::Return); - return; - } - } - if (auto singleValue = singleValueFrom(valueList)) { - if (auto simpleValue = singleValue->item.as()) { - auto value = simpleValue->value.get(); - switch (value->getId()) { - case id(): - transformComprehension(static_cast(value), out, ExpUsage::Return); - return; - case id(): - transformTblComprehension(static_cast(value), out, ExpUsage::Return); - return; - case id(): - transformWith(static_cast(value), out, nullptr, true); - return; - case id(): - transformClassDecl(static_cast(value), out, ExpUsage::Return); - return; - case id(): - transformDo(static_cast(value), out, ExpUsage::Return); - return; - case id(): - transformSwitch(static_cast(value), out, ExpUsage::Return); - return; - case id(): - transformWhileInPlace(static_cast(value), out); - return; - case id(): - transformForInPlace(static_cast(value), out); - return; - case id(): - transformForEachInPlace(static_cast(value), out); - return; - case id(): - transformIf(static_cast(value), out, ExpUsage::Return); - return; - case id(): - transformUnless(static_cast(value), out, ExpUsage::Return); - return; - } - } else if (auto chainValue = singleValue->item.as()) { - if (specialChainValue(chainValue) != ChainType::Common) { - transformChainValue(chainValue, out, ExpUsage::Return, nullptr, returnNode->allowBlockMacroReturn); - return; - } - } - transformValue(singleValue, out); - out.back() = indent() + s("return "sv) + out.back() + nlr(returnNode); - return; - } else { - str_list temp; - transformExpListLow(valueList, temp); - out.push_back(indent() + s("return "sv) + temp.back() + nlr(returnNode)); - } - } else { - out.push_back(indent() + s("return"sv) + nll(returnNode)); - } - } - - void transformFnArgsDef(FnArgsDef_t* argsDef, str_list& out) { - if (!argsDef->defList) { - out.push_back(Empty); - out.push_back(Empty); - } else { - transformFnArgDefList(argsDef->defList, out); - } - if (argsDef->shadowOption) { - transform_outer_var_shadow(argsDef->shadowOption); - } - } - - void transform_outer_var_shadow(outer_var_shadow_t* shadow) { - markVarShadowed(); - if (shadow->varList) { - for (auto name : shadow->varList->names.objects()) { - addToAllowList(_parser.toString(name)); - } - } - } - - void transformFnArgDefList(FnArgDefList_t* argDefList, str_list& out) { - auto x = argDefList; - struct ArgItem { - std::string name; - std::string assignSelf; - }; - std::list argItems; - str_list temp; - std::string varNames; - bool assignSelf = false; - for (auto _def : argDefList->definitions.objects()) { - auto def = static_cast(_def); - auto& arg = argItems.emplace_back(); - switch (def->name->getId()) { - case id(): arg.name = _parser.toString(def->name); break; - case id(): { - assignSelf = true; - auto selfName = static_cast(def->name.get()); - switch (selfName->name->getId()) { - case id(): { - auto clsName = static_cast(selfName->name.get()); - arg.name = _parser.toString(clsName->name); - arg.assignSelf = s("self.__class."sv) + arg.name; - break; - } - case id(): - arg.name = "self.__class"sv; - break; - case id(): { - auto sfName = static_cast(selfName->name.get()); - arg.name = _parser.toString(sfName->name); - arg.assignSelf = s("self."sv) + arg.name; - break; - } - case id(): - arg.name = "self"sv; - break; - default: assert(false); break; - } - break; - } - default: assert(false); break; - } - forceAddToScope(arg.name); - if (def->defaultValue) { - pushScope(); - auto expList = toAst(arg.name, x); - auto assign = x->new_ptr(); - assign->values.push_back(def->defaultValue.get()); - auto assignment = x->new_ptr(); - assignment->expList.set(expList); - assignment->action.set(assign); - transformAssignment(assignment, temp); - popScope(); - _buf << indent() << "if "sv << arg.name << " == nil then"sv << nll(def); - _buf << temp.back(); - _buf << indent() << "end"sv << nll(def); - temp.back() = clearBuf(); - } - if (varNames.empty()) varNames = arg.name; - else varNames.append(s(", "sv) + arg.name); - } - if (argDefList->varArg) { - auto& arg = argItems.emplace_back(); - arg.name = "..."sv; - if (varNames.empty()) varNames = arg.name; - else varNames.append(s(", "sv) + arg.name); - _varArgs.top() = true; - } - std::string initCodes = join(temp); - if (assignSelf) { - auto sjoin = [](const decltype(argItems)& items, int index) { - std::string result; - for (auto it = items.begin(); it != items.end(); ++it) { - if (it->assignSelf.empty()) continue; - if (result.empty()) result = (&it->name)[index]; - else result.append(s(", "sv) + (&it->name)[index]); - } - return result; - }; - std::string sleft = sjoin(argItems, 1); - std::string sright = sjoin(argItems, 0); - initCodes.append(indent() + sleft + s(" = "sv) + sright + nll(argDefList)); - } - out.push_back(varNames); - out.push_back(initCodes); - } - - void transformSelfName(SelfName_t* selfName, str_list& out, const ast_sel& invoke = {}) { - auto x = selfName; - auto name = selfName->name.get(); - switch (name->getId()) { - case id(): { - auto clsName = static_cast(name); - auto nameStr = _parser.toString(clsName->name); - if (LuaKeywords.find(nameStr) != LuaKeywords.end()) { - out.push_back(s("self.__class[\""sv) + nameStr + s("\"]"sv)); - if (invoke) { - if (auto invokePtr = invoke.as()) { - invokePtr->args.push_front(toAst("self.__class"sv, x)); - } else { - auto invokeArgsPtr = invoke.as(); - invokeArgsPtr->args.push_front(toAst("self.__class"sv, x)); - } - } - } else { - out.push_back(s("self.__class"sv) + s(invoke ? ":"sv : "."sv) + nameStr); - } - break; - } - case id(): - out.push_back(s("self.__class"sv)); - break; - case id(): { - auto sfName = static_cast(name); - auto nameStr = _parser.toString(sfName->name); - if (LuaKeywords.find(nameStr) != LuaKeywords.end()) { - out.push_back(s("self[\""sv) + nameStr + s("\"]"sv)); - if (invoke) { - if (auto invokePtr = invoke.as()) { - invokePtr->args.push_front(toAst("self"sv, x)); - } else { - auto invokeArgsPtr = invoke.as(); - invokeArgsPtr->args.push_front(toAst("self"sv, x)); - } - } - } else { - out.push_back(s("self"sv) + s(invoke ? ":"sv : "."sv) + nameStr); - } - break; - } - case id(): - out.push_back(s("self"sv)); - break; - default: assert(false); break; - } - } - - bool transformChainEndWithEOP(const node_container& chainList, str_list& out, ExpUsage usage, ExpList_t* assignList) { - auto x = chainList.front(); - if (ast_is(chainList.back())) { - auto parens = x->new_ptr(); - { - auto chainValue = x->new_ptr(); - for (auto item : chainList) { - chainValue->items.push_back(item); - } - chainValue->items.pop_back(); - auto value = x->new_ptr(); - value->item.set(chainValue); - auto exp = newExp(value, toAst("!="sv, x), toAst("nil"sv, x), x); - parens->expr.set(exp); - } - switch (usage) { - case ExpUsage::Assignment: { - auto callable = x->new_ptr(); - callable->item.set(parens); - auto chainValue = x->new_ptr(); - chainValue->items.push_back(callable); - auto value = x->new_ptr(); - value->item.set(chainValue); - auto exp = newExp(value, x); - auto assignment = x->new_ptr(); - assignment->expList.set(assignList); - auto assign = x->new_ptr(); - assign->values.push_back(exp); - assignment->action.set(assign); - transformAssignment(assignment, out); - break; - } - case ExpUsage::Return: - transformParens(parens, out); - out.back().insert(0, indent() + s("return "sv)); - out.back().append(nlr(x)); - break; - default: - transformParens(parens, out); - break; - } - return true; - } - return false; - } - - bool transformChainWithEOP(const node_container& chainList, str_list& out, ExpUsage usage, ExpList_t* assignList) { - auto opIt = std::find_if(chainList.begin(), chainList.end(), [](ast_node* node) { return ast_is(node); }); - if (opIt != chainList.end()) { - auto x = chainList.front(); - str_list temp; - if (usage == ExpUsage::Closure) { - temp.push_back(s("(function()"sv) + nll(x)); - pushScope(); - _enableReturn.push(true); - } - auto partOne = x->new_ptr(); - for (auto it = chainList.begin();it != opIt;++it) { - partOne->items.push_back(*it); - } - BLOCK_START - auto back = ast_cast(partOne->items.back()); - BREAK_IF(!back); - auto selfName = ast_cast(back->item); - BREAK_IF(!selfName); - if (auto sname = ast_cast(selfName->name)) { - auto colonItem = x->new_ptr(); - colonItem->name.set(sname->name); - partOne->items.pop_back(); - partOne->items.push_back(toAst("@"sv, x)); - partOne->items.push_back(colonItem); - break; - } - if (auto cname = ast_cast(selfName->name)) { - auto colonItem = x->new_ptr(); - colonItem->name.set(cname->name); - partOne->items.pop_back(); - partOne->items.push_back(toAst("@@"sv, x)); - partOne->items.push_back(colonItem); - break; - } - BLOCK_END - auto objVar = singleVariableFrom(partOne); - if (objVar.empty()) { - objVar = getUnusedName("_obj_"sv); - if (auto colonItem = ast_cast(partOne->items.back())) { - auto chainValue = x->new_ptr(); - chainValue->items.dup(partOne->items); - chainValue->items.pop_back(); - if (chainValue->items.empty()) { - if (_withVars.empty()) { - throw std::logic_error(_info.errorMessage("short dot/colon syntax must be called within a with block"sv, x)); - } - chainValue->items.push_back(toAst(_withVars.top(), x)); - } - auto newObj = singleVariableFrom(chainValue); - if (!newObj.empty()) { - objVar = newObj; - } else { - auto value = x->new_ptr(); - value->item.set(chainValue); - auto exp = newExp(value, x); - auto assign = x->new_ptr(); - assign->values.push_back(exp); - auto expListAssign = x->new_ptr(); - expListAssign->expList.set(toAst(objVar, x)); - expListAssign->action.set(assign); - transformAssignment(expListAssign, temp); - } - auto dotItem = x->new_ptr(); - auto name = colonItem->name.get(); - if (auto keyword = ast_cast(name)) { - name = keyword->name.get(); - } - dotItem->name.set(name); - partOne->items.clear(); - partOne->items.push_back(toAst(objVar, x)); - partOne->items.push_back(dotItem); - auto it = opIt; ++it; - if (it != chainList.end() && ast_is(*it)) { - - if (auto invoke = ast_cast(*it)) { - invoke->args.push_front(toAst(objVar, x)); - } else { - auto invokeArgs = static_cast(*it); - invokeArgs->args.push_front(toAst(objVar, x)); - } - } - objVar = getUnusedName("_obj_"sv); - } - auto value = x->new_ptr(); - value->item.set(partOne); - auto exp = newExp(value, x); - auto assign = x->new_ptr(); - assign->values.push_back(exp); - auto expListAssign = x->new_ptr(); - expListAssign->expList.set(toAst(objVar, x)); - expListAssign->action.set(assign); - transformAssignment(expListAssign, temp); - } - _buf << indent() << "if "sv << objVar << " ~= nil then"sv << nll(x); - temp.push_back(clearBuf()); - pushScope(); - auto partTwo = x->new_ptr(); - partTwo->items.push_back(toAst(objVar, x)); - for (auto it = ++opIt;it != chainList.end();++it) { - partTwo->items.push_back(*it); - } - switch (usage) { - case ExpUsage::Common: - transformChainValue(partTwo, temp, ExpUsage::Common); - break; - case ExpUsage::Assignment: { - auto value = x->new_ptr(); - value->item.set(partTwo); - auto exp = newExp(value, x); - auto assign = x->new_ptr(); - assign->values.push_back(exp); - auto assignment = x->new_ptr(); - assignment->expList.set(assignList); - assignment->action.set(assign); - transformAssignment(assignment, temp); - break; - } - case ExpUsage::Return: - case ExpUsage::Closure: { - auto value = x->new_ptr(); - value->item.set(partTwo); - auto exp = newExp(value, x); - auto ret = x->new_ptr(); - auto expListLow = x->new_ptr(); - expListLow->exprs.push_back(exp); - ret->valueList.set(expListLow); - transformReturn(ret, temp); - break; - } - } - popScope(); - temp.push_back(indent() + s("end"sv) + nlr(x)); - switch (usage) { - case ExpUsage::Return: - temp.push_back(indent() + s("return nil"sv) + nlr(x)); - break; - case ExpUsage::Closure: - temp.push_back(indent() + s("return nil"sv) + nlr(x)); - _enableReturn.pop(); - popScope(); - temp.push_back(indent() + s("end)()"sv)); - break; - default: - break; - } - out.push_back(join(temp)); - return true; - } - return false; - } - - bool transformChainEndWithColonItem(const node_container& chainList, str_list& out, ExpUsage usage, ExpList_t* assignList) { - if (ast_is(chainList.back())) { - auto x = chainList.front(); - str_list temp; - switch (usage) { - case ExpUsage::Assignment: - temp.push_back(indent() + s("do"sv) + nll(x)); - pushScope(); - break; - case ExpUsage::Closure: - temp.push_back(s("(function()"sv) + nll(x)); - pushScope(); - _enableReturn.push(true); - break; - default: - break; - } - auto baseChain = x->new_ptr(); - switch (chainList.front()->getId()) { - case id(): - case id(): - if (_withVars.empty()) { - throw std::logic_error(_info.errorMessage("short dot/colon syntax must be called within a with block"sv, chainList.front())); - } else { - baseChain->items.push_back(toAst(_withVars.top(), x)); - } - break; - } - auto end = --chainList.end(); - for (auto it = chainList.begin(); it != end; ++it) { - baseChain->items.push_back(*it); - } - auto colonChainItem = static_cast(chainList.back()); - auto funcName = _parser.toString(colonChainItem->name); - auto baseVar = getUnusedName("_base_"sv); - auto fnVar = getUnusedName("_fn_"sv); - { - auto value = x->new_ptr(); - value->item.set(baseChain); - auto exp = newExp(value, x); - auto assign = x->new_ptr(); - assign->values.push_back(exp); - auto assignment = x->new_ptr(); - assignment->expList.set(toAst(baseVar, x)); - assignment->action.set(assign); - transformAssignment(assignment, temp); - } - { - auto assign = x->new_ptr(); - assign->values.push_back(toAst(baseVar + "." + funcName, x)); - auto assignment = x->new_ptr(); - assignment->expList.set(toAst(fnVar, x)); - assignment->action.set(assign); - transformAssignment(assignment, temp); - } - auto funLit = toAst(fnVar + s(" and (...)-> "sv) + fnVar + s(" "sv) + baseVar + s(", ..."sv), x); - switch (usage) { - case ExpUsage::Closure: - case ExpUsage::Return: { - auto returnNode = x->new_ptr(); - auto expListLow = x->new_ptr(); - expListLow->exprs.push_back(funLit); - returnNode->valueList.set(expListLow); - transformReturn(returnNode, temp); - break; - } - case ExpUsage::Assignment: { - auto assign = x->new_ptr(); - assign->values.push_back(funLit); - auto assignment = x->new_ptr(); - assignment->expList.set(assignList); - assignment->action.set(assign); - transformAssignment(assignment, temp); - break; - } - default: - break; - } - switch (usage) { - case ExpUsage::Assignment: - popScope(); - temp.push_back(indent() + s("end"sv) + nlr(x)); - break; - case ExpUsage::Closure: - _enableReturn.pop(); - popScope(); - temp.push_back(indent() + s("end)()"sv)); - break; - default: - break; - } - out.push_back(join(temp)); - return true; - } - return false; - } - - void transformChainList(const node_container& chainList, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { - auto x = chainList.front(); - str_list temp; - switch (x->getId()) { - case id(): - case id(): - if (_withVars.empty()) { - throw std::logic_error(_info.errorMessage("short dot/colon syntax must be called within a with block"sv, x)); - } else { - temp.push_back(_withVars.top()); - } - break; - } - for (auto it = chainList.begin(); it != chainList.end(); ++it) { - auto item = *it; - switch (item->getId()) { - case id(): - transformInvoke(static_cast(item), temp); - break; - case id(): - transformDotChainItem(static_cast(item), temp); - break; - case id(): { - auto colonItem = static_cast(item); - auto current = it; - auto next = current; ++next; - auto followItem = next != chainList.end() ? *next : nullptr; - if (current != chainList.begin()) { - --current; - if (!ast_is(*current)) { - ++current; - } - } - if (ast_is(followItem)) { - ++next; - followItem = next != chainList.end() ? *next : nullptr; - --next; - } - if (!ast_is(followItem)) { - throw std::logic_error(_info.errorMessage("colon chain item must be followed by invoke arguments"sv, colonItem)); - } - if (colonItem->name.is()) { - std::string callVar; - auto block = x->new_ptr(); - { - auto chainValue = x->new_ptr(); - switch (chainList.front()->getId()) { - case id(): - case id(): - chainValue->items.push_back(toAst(_withVars.top(), x)); - break; - } - for (auto i = chainList.begin(); i != current; ++i) { - chainValue->items.push_back(*i); - } - auto value = x->new_ptr(); - value->item.set(chainValue); - auto exp = newExp(value, x); - callVar = singleVariableFrom(exp); - if (callVar.empty()) { - callVar = getUnusedName(s("_call_"sv)); - auto assignment = x->new_ptr(); - assignment->expList.set(toAst(callVar, x)); - auto assign = x->new_ptr(); - assign->values.push_back(exp); - assignment->action.set(assign); - auto stmt = x->new_ptr(); - stmt->content.set(assignment); - block->statements.push_back(stmt); - } - } - ast_ptr nexp; - { - auto name = _parser.toString(colonItem->name); - auto chainValue = x->new_ptr(); - chainValue->items.push_back(toAst(callVar, x)); - if (ast_is(*current)) { - chainValue->items.push_back(x->new_ptr()); - } - chainValue->items.push_back(toAst(s("\""sv) + name + s("\""sv), x)); - if (auto invoke = ast_cast(followItem)) { - invoke->args.push_front(toAst(callVar, x)); - } else { - auto invokeArgs = static_cast(followItem); - invokeArgs->args.push_front(toAst(callVar, x)); - } - for (auto i = next; i != chainList.end(); ++i) { - chainValue->items.push_back(*i); - } - auto value = x->new_ptr(); - value->item.set(chainValue); - nexp = newExp(value, x); - auto expList = x->new_ptr(); - expList->exprs.push_back(nexp); - auto expListAssign = x->new_ptr(); - expListAssign->expList.set(expList); - auto stmt = x->new_ptr(); - stmt->content.set(expListAssign); - block->statements.push_back(stmt); - } - switch (usage) { - case ExpUsage::Common: - case ExpUsage::Return: - transformBlock(block, out, usage); - return; - case ExpUsage::Assignment: { - transformBlock(block, out, ExpUsage::Assignment, assignList); - return; - } - default: - break; - } - if (block->statements.size() == 1) { - transformExp(nexp, out, usage, assignList); - } else { - auto body = x->new_ptr(); - body->content.set(block); - auto funLit = toAst("->"sv, x); - funLit->body.set(body); - auto simpleValue = x->new_ptr(); - simpleValue->value.set(funLit); - auto value = x->new_ptr(); - value->item.set(simpleValue); - auto exp = newExp(value, x); - auto paren = x->new_ptr(); - paren->expr.set(exp); - auto callable = x->new_ptr(); - callable->item.set(paren); - auto chainValue = x->new_ptr(); - chainValue->items.push_back(callable); - auto invoke = x->new_ptr(); - chainValue->items.push_back(invoke); - transformChainValue(chainValue, out, ExpUsage::Closure); - } - return; - } - transformColonChainItem(colonItem, temp); - break; - } - case id(): - transformSlice(static_cast(item), temp); - break; - case id(): { - auto next = it; ++next; - auto followItem = next != chainList.end() ? *next : nullptr; - ast_sel invoke; - if (ast_is(followItem)) { - invoke.set(followItem); - } - transformCallable(static_cast(item), temp, invoke); - break; - } - case id(): - transformString(static_cast(item), temp); - temp.back() = s("("sv) + temp.back() + s(")"sv); - break; - case id(): - transformExp(static_cast(item), temp, ExpUsage::Closure); - temp.back() = s(temp.back().front() == '[' ? "[ "sv : "["sv) + temp.back() + s("]"sv); - break; - case id(): transformInvokeArgs(static_cast(item), temp); break; - default: assert(false); break; - } - } - switch (usage) { - case ExpUsage::Common: - out.push_back(indent() + join(temp) + nll(chainList.front())); - break; - case ExpUsage::Return: - out.push_back(indent() + s("return "sv) + join(temp) + nll(chainList.front())); - break; - case ExpUsage::Assignment: assert(false); break; - default: - out.push_back(join(temp)); - break; - } - } - -#ifndef MOONP_NO_MACRO - std::tuple expandMacroStr(ChainValue_t* chainValue) { - const auto& chainList = chainValue->items.objects(); - auto x = ast_to(chainList.front())->item.to(); - auto macroName = x->name ? _parser.toString(x->name) : Empty; - if (!macroName.empty() && !_useModule) { - throw std::logic_error(_info.errorMessage("can not resolve macro"sv, x)); - } - pushCurrentModule(); // cur - int top = lua_gettop(L) - 1; - DEFER(lua_settop(L, top)); - if (macroName.empty()) { - lua_pop(L, 1); // empty - auto item = *(++chainList.begin()); - const node_container* args = nullptr; - if (auto invoke = ast_cast(item)) { - args = &invoke->args.objects(); - } else { - args = &ast_to(item)->args.objects(); - } - if (args->size() != 1) { - throw std::logic_error(_info.errorMessage("in-place macro must be followed by a compile time function"sv, x)); - } - auto fcodes = _parser.toString(args->back()); - Utils::trim(fcodes); - pushMoonp("loadstring"sv); // loadstring - lua_pushlstring(L, fcodes.c_str(), fcodes.size()); // loadstring codes - lua_pushliteral(L, "=(macro in-place)"); // loadstring codes chunk - pushOptions(args->back()->m_begin.m_line - 1); // loadstring codes chunk options - if (lua_pcall(L, 3, 2, 0) != 0) { // loadstring(codes,chunk,options), f err - std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage(s("failed to load macro codes\n"sv) + err, x)); - } // f err - if (lua_isnil(L, -2) != 0) { // f == nil, f err - std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage(s("failed to load macro codes, at (macro in-place): "sv) + err, x)); - } - lua_pop(L, 1); // f - pushMoonp("pcall"sv); // f pcall - lua_insert(L, -2); // pcall f - if (lua_pcall(L, 1, 2, 0) != 0) { // f(), success macroFunc - std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage(s("failed to generate macro function\n"sv) + err, x)); - } // success res - if (lua_toboolean(L, -2) == 0) { - std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage(s("failed to generate macro function\n"sv) + err, x)); - } // true macroFunc - lua_remove(L, -2); // macroFunc - pushMoonp("pcall"sv); // macroFunc pcall - lua_insert(L, -2); // pcall macroFunc - bool success = lua_pcall(L, 1, 2, 0) == 0; - if (!success) { // err - std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage(s("failed to expand macro: "sv) + err, x)); - } // success err - if (lua_toboolean(L, -2) == 0) { - std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage(s("failed to expand macro: "sv) + err, x)); - } - return {Empty, Empty, {}}; - } - lua_pushlstring(L, macroName.c_str(), macroName.size()); // cur macroName - lua_rawget(L, -2); // cur[macroName], cur macroFunc - if (lua_isfunction(L, -1) == 0) { - throw std::logic_error(_info.errorMessage("can not resolve macro"sv, x)); - } // cur macroFunc - pushMoonp("pcall"sv); // cur macroFunc pcall - lua_insert(L, -2); // cur pcall macroFunc - auto item = *(++chainList.begin()); - const node_container* args = nullptr; - if (auto invoke = ast_cast(item)) { - args = &invoke->args.objects(); - } else { - args = &ast_to(item)->args.objects(); - } - for (auto arg : *args) { - std::string str; - // check whether arg is reassembled - // do some workaround for backcall expression - if (ast_is(arg) && arg->m_begin.m_it == arg->m_end.m_it) { - auto exp = static_cast(arg); - BLOCK_START - BREAK_IF(!exp->opValues.empty()); - auto chainValue = exp->getByPath(); - BREAK_IF(!chainValue); - BREAK_IF(!isMacroChain(chainValue)); - BREAK_IF(chainValue->items.size() != 2); - str = std::get<1>(expandMacroStr(chainValue)); - BLOCK_END - if (str.empty()) { - // exp is reassembled due to backcall expressions - // in transform stage, toString(exp) won't be able - // to convert its whole text content - str = _parser.toString(exp->backcalls.front()); - } - } else if (auto lstr = ast_cast(arg)) { - str = _parser.toString(lstr->content); - } else { - bool multiLineStr = false; - BLOCK_START - auto exp = ast_cast(arg); - BREAK_IF(!exp); - auto value = singleValueFrom(exp); - BREAK_IF(!value); - auto lstr = value->getByPath(); - BREAK_IF(!lstr); - str = _parser.toString(lstr->content); - multiLineStr = true; - BLOCK_END - if (!multiLineStr) { - str = _parser.toString(arg); - } - } - Utils::trim(str); - Utils::replace(str, "\r\n"sv, "\n"sv); - lua_pushlstring(L, str.c_str(), str.size()); - } // cur pcall macroFunc args... - bool success = lua_pcall(L, static_cast(args->size()) + 1, 2, 0) == 0; - if (!success) { // cur err - std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage(s("failed to expand macro: "sv) + err, x)); - } // cur success res - if (lua_toboolean(L, -2) == 0) { - std::string err = lua_tostring(L, -2); - throw std::logic_error(_info.errorMessage(s("failed to expand macro: "sv) + err, x)); - } - lua_remove(L, -2); // cur res - if (lua_isstring(L, -1) == 0 && lua_istable(L, -1) == 0) { - throw std::logic_error(_info.errorMessage(s("macro function must return string or table"sv), x)); - } // cur res - std::string codes; - std::string type; - str_list localVars; - if (lua_istable(L, -1) != 0) { - lua_getfield(L, -1, "codes"); // cur res codes - if (lua_isstring(L, -1) != 0) { - codes = lua_tostring(L, -1); - } else { - throw std::logic_error(_info.errorMessage(s("macro table must contain field \"codes\" of string"sv), x)); - } - lua_pop(L, 1); // cur res - lua_getfield(L, -1, "type"); // cur res type - if (lua_isstring(L, -1) != 0) { - type = lua_tostring(L, -1); - } - if (type != "lua"sv && type != "text"sv) { - throw std::logic_error(_info.errorMessage(s("macro table must contain field \"type\" of value \"lua\" or \"text\""sv), x)); - } - lua_pop(L, 1); // cur res - lua_getfield(L, -1, "locals"); // cur res locals - if (lua_istable(L, -1) != 0) { - for (int i = 0; i < static_cast(lua_objlen(L, -1)); i++) { - lua_rawgeti(L, -1, i + 1); // cur res locals item - size_t len = 0; - if (lua_isstring(L, -1) == 0) { - throw std::logic_error(_info.errorMessage(s("macro table field \"locals\" must be a table of strings"sv), x)); - } - auto name = lua_tolstring(L, -1, &len); - if (_parser.match({name, len})) { - localVars.push_back(std::string(name, len)); - } else { - throw std::logic_error(_info.errorMessage(s("macro table field \"locals\" must contain names for local variables, got \""sv) + std::string(name, len) + '"', x)); - } - lua_pop(L, 1); - } - } - lua_pop(L, 1); // cur res - } else { - codes = lua_tostring(L, -1); - } - return {type, codes, std::move(localVars)}; - } - - std::tuple, std::unique_ptr, std::string, str_list> expandMacro(ChainValue_t* chainValue, ExpUsage usage, bool allowBlockMacroReturn) { - auto x = ast_to(chainValue->items.front())->item.to(); - const auto& chainList = chainValue->items.objects(); - std::string type, codes; - str_list localVars; - std::tie(type, codes, localVars) = expandMacroStr(chainValue); - bool isBlock = (usage == ExpUsage::Common) && (chainList.size() <= 2); - ParseInfo info; - if (type == "lua"sv) { - if (!isBlock) { - throw std::logic_error(_info.errorMessage("lua macro can only be placed where block macro is allowed"sv, x)); - } - auto macroChunk = s("=(macro "sv) + _parser.toString(x->name) + ')'; - int top = lua_gettop(L); - DEFER(lua_settop(L, top)); - if (luaL_loadbuffer(L, codes.c_str(), codes.size(), macroChunk.c_str()) != 0) { - std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage(err, x)); - } - return {nullptr, nullptr, std::move(codes), std::move(localVars)}; - } else if (type == "text"sv) { - if (!isBlock) { - throw std::logic_error(_info.errorMessage("text macro can only be placed where block macro is allowed"sv, x)); - } - return {nullptr, nullptr, std::move(codes), std::move(localVars)}; - } else { - if (!codes.empty()) { - if (isBlock) { - info = _parser.parse(codes); - if (!info.node) { - info.error = info.error.substr(info.error.find(':') + 2); - throw std::logic_error(_info.errorMessage(s("failed to expanded macro as block: "sv) + info.error, x)); - } - } else { - info = _parser.parse(codes); - if (!info.node && allowBlockMacroReturn) { - info = _parser.parse(codes); - if (!info.node) { - info.error = info.error.substr(info.error.find(':') + 2); - throw std::logic_error(_info.errorMessage(s("failed to expanded macro as expr or block: "sv) + info.error, x)); - } - isBlock = true; - } else if (!info.node) { - info.error = info.error.substr(info.error.find(':') + 2); - throw std::logic_error(_info.errorMessage(s("failed to expanded macro as expr: "sv) + info.error, x)); - } - } - int line = x->m_begin.m_line; - int col = x->m_begin.m_col; - info.node->traverse([&](ast_node* node) { - node->m_begin.m_line = line; - node->m_end.m_line = line; - node->m_begin.m_col = col; - node->m_end.m_col = col; - return traversal::Continue; - }); - if (!isBlock) { - ast_ptr exp; - exp.set(info.node); - if (!exp->opValues.empty() || chainList.size() > 2) { - auto paren = x->new_ptr(); - paren->expr.set(exp); - auto callable = x->new_ptr(); - callable->item.set(paren); - auto newChain = x->new_ptr(); - newChain->items.push_back(callable); - auto it = chainList.begin(); - it++; it++; - for (; it != chainList.end(); ++it) { - newChain->items.push_back(*it); - } - auto value = x->new_ptr(); - value->item.set(newChain); - exp = newExp(value, x); - } - if (usage == ExpUsage::Common) { - auto expList = x->new_ptr(); - expList->exprs.push_back(exp); - auto exps = x->new_ptr(); - exps->expList.set(expList); - auto stmt = x->new_ptr(); - stmt->content.set(exps); - auto block = x->new_ptr(); - block->statements.push_back(stmt); - info.node.set(block); - } else { - info.node.set(exp); - } - } - return {info.node, std::move(info.codes), Empty, std::move(localVars)}; - } else { - if (!isBlock) throw std::logic_error(_info.errorMessage(s("failed to expanded empty macro as expr"sv), x)); - return {x->new_ptr().get(), std::move(info.codes), Empty, std::move(localVars)}; - } - } - } -#endif // MOONP_NO_MACRO - - void transformChainValue(ChainValue_t* chainValue, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr, bool allowBlockMacroReturn = false) { - if (isMacroChain(chainValue)) { -#ifndef MOONP_NO_MACRO - ast_ptr node; - std::unique_ptr codes; - std::string luaCodes; - str_list localVars; - std::tie(node, codes, luaCodes, localVars) = expandMacro(chainValue, usage, allowBlockMacroReturn); - Utils::replace(luaCodes, "\r\n"sv, "\n"sv); - Utils::trim(luaCodes); - if (!node) { - if (!luaCodes.empty()) { - if (_config.reserveLineNumber) { - luaCodes.insert(0, nll(chainValue).substr(1)); - } - luaCodes.append(nlr(chainValue)); - } - out.push_back(luaCodes); - if (!localVars.empty()) { - for (const auto& var : localVars) { - addToScope(var); - } - } - return; - } - if (usage == ExpUsage::Common || (usage == ExpUsage::Return && node.is())) { - transformBlock(node.to(), out, usage, assignList); - } else { - auto x = chainValue; - switch (usage) { - case ExpUsage::Assignment: { - auto assign = x->new_ptr(); - assign->values.push_back(node); - auto assignment = x->new_ptr(); - assignment->expList.set(assignList); - assignment->action.set(assign); - transformAssignment(assignment, out); - break; - } - case ExpUsage::Return: { - auto expListLow = x->new_ptr(); - expListLow->exprs.push_back(node); - auto returnNode = x->new_ptr(); - returnNode->valueList.set(expListLow); - transformReturn(returnNode, out); - break; - } - default: - transformExp(node.to(), out, usage); - break; - } - } - return; -#else - (void)allowBlockMacroReturn; - throw std::logic_error(_info.errorMessage("macro feature not supported"sv, chainValue)); -#endif // MOONP_NO_MACRO - } - const auto& chainList = chainValue->items.objects(); - if (transformChainEndWithEOP(chainList, out, usage, assignList)) { - return; - } - if (transformChainWithEOP(chainList, out, usage, assignList)) { - return; - } - if (transformChainEndWithColonItem(chainList, out, usage, assignList)) { - return; - } - transformChainList(chainList, out, usage, assignList); - } - - void transformAssignableChain(AssignableChain_t* chain, str_list& out) { - transformChainList(chain->items.objects(), out, ExpUsage::Closure); - } - - void transformDotChainItem(DotChainItem_t* dotChainItem, str_list& out) { - auto name = _parser.toString(dotChainItem->name); - if (Keywords.find(name) != Keywords.end()) { - out.push_back(s("[\""sv) + name + s("\"]"sv)); - } else { - out.push_back(s("."sv) + name); - } - } - - void transformColonChainItem(ColonChainItem_t* colonChainItem, str_list& out) { - auto name = _parser.toString(colonChainItem->name); - out.push_back(s(colonChainItem->switchToDot ? "."sv : ":"sv) + name); - } - - void transformSlice(Slice_t* slice, str_list&) { - throw std::logic_error(_info.errorMessage("slice syntax not supported here"sv, slice)); - } - - void transformInvoke(Invoke_t* invoke, str_list& out) { - str_list temp; - for (auto arg : invoke->args.objects()) { - switch (arg->getId()) { - case id(): transformExp(static_cast(arg), temp, ExpUsage::Closure); break; - case id(): transformSingleString(static_cast(arg), temp); break; - case id(): transformDoubleString(static_cast(arg), temp); break; - case id(): transformLuaString(static_cast(arg), temp); break; - case id(): transformTableLit(static_cast(arg), temp); break; - default: assert(false); break; - } - } - out.push_back(s("("sv) + join(temp, ", "sv) + s(")"sv)); - } - - void transform_unary_value(unary_value_t* unary_value, str_list& out) { - str_list temp; - for (auto _op : unary_value->ops.objects()) { - std::string op = _parser.toString(_op); - temp.push_back(op + (op == "not"sv ? s(" "sv) : Empty)); - } - transformValue(unary_value->value, temp); - out.push_back(join(temp)); - } - - void transform_unary_exp(unary_exp_t* unary_exp, str_list& out) { - if (unary_exp->ops.empty() && unary_exp->expos.size() == 1) { - transformValue(static_cast(unary_exp->expos.back()), out); - return; - } - std::string unary_op; - for (auto _op : unary_exp->ops.objects()) { - std::string op = _parser.toString(_op); - unary_op.append(op + (op == "not"sv ? s(" "sv) : Empty)); - } - str_list temp; - for (auto _value : unary_exp->expos.objects()) { - auto value = static_cast(_value); - transformValue(value, temp); - } - out.push_back(unary_op + join(temp, " ^ "sv)); - } - - void transformVariable(Variable_t* name, str_list& out) { - out.push_back(_parser.toString(name)); - } - - void transformNum(Num_t* num, str_list& out) { - out.push_back(_parser.toString(num)); - } - - void transformTableLit(TableLit_t* table, str_list& out) { - transformTable(table, table->values.objects(), out); - } - - void transformCompCommon(Comprehension_t* comp, str_list& out) { - str_list temp; - auto x = comp; - auto compInner = comp->forLoop.get(); - for (auto item : compInner->items.objects()) { - switch (item->getId()) { - case id(): - transformCompForEach(static_cast(item), temp); - break; - case id(): - transformCompFor(static_cast(item), temp); - break; - case id(): - transformExp(static_cast(item), temp, ExpUsage::Closure); - temp.back() = indent() + s("if "sv) + temp.back() + s(" then"sv) + nll(item); - pushScope(); - break; - default: assert(false); break; - } - } - if (auto stmt = comp->value.as()) { - transformStatement(stmt, temp); - } else if (auto exp = comp->value.as()) { - auto expList = x->new_ptr(); - expList->exprs.push_back(exp); - auto expListAssign = x->new_ptr(); - expListAssign->expList.set(expList); - auto statement = x->new_ptr(); - statement->content.set(expListAssign); - transformStatement(statement, temp); - } - auto value = temp.back(); - temp.pop_back(); - _buf << join(temp) << value; - for (size_t i = 0; i < compInner->items.objects().size(); ++i) { - popScope(); - _buf << indent() << "end"sv << nll(comp); - } - out.push_back(clearBuf()); - } - - void transformComprehension(Comprehension_t* comp, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { - auto x = comp; - switch (usage) { - case ExpUsage::Closure: - _enableReturn.push(true); - pushScope(); - break; - case ExpUsage::Assignment: - pushScope(); - break; - default: - break; - } - str_list temp; - std::string accumVar = getUnusedName("_accum_"sv); - std::string lenVar = getUnusedName("_len_"sv); - addToScope(accumVar); - addToScope(lenVar); - auto compInner = comp->forLoop.get(); - for (auto item : compInner->items.objects()) { - switch (item->getId()) { - case id(): - transformCompForEach(static_cast(item), temp); - break; - case id(): - transformCompFor(static_cast(item), temp); - break; - case id(): - transformExp(static_cast(item), temp, ExpUsage::Closure); - temp.back() = indent() + s("if "sv) + temp.back() + s(" then"sv) + nll(item); - pushScope(); - break; - default: assert(false); break; - } - } - { - auto assignLeft = toAst(accumVar + s("["sv) + lenVar + s("]"sv), x); - auto assign = x->new_ptr(); - assign->values.push_back(comp->value); - auto assignment = x->new_ptr(); - assignment->expList.set(assignLeft); - assignment->action.set(assign); - transformAssignment(assignment, temp); - } - auto assignStr = temp.back(); - temp.pop_back(); - for (size_t i = 0; i < compInner->items.objects().size(); ++i) { - popScope(); - } - _buf << indent() << "local "sv << accumVar << " = { }"sv << nll(comp); - _buf << indent() << "local "sv << lenVar << " = 1"sv << nll(comp); - _buf << join(temp); - _buf << assignStr; - _buf << indent(int(temp.size())) << lenVar << " = "sv << lenVar << " + 1"sv << nll(comp); - for (int ind = int(temp.size()) - 1; ind > -1; --ind) { - _buf << indent(ind) << "end"sv << nll(comp); - } - switch (usage) { - case ExpUsage::Common: - break; - case ExpUsage::Closure: { - _enableReturn.pop(); - out.push_back(clearBuf()); - out.back().append(indent() + s("return "sv) + accumVar + nlr(comp)); - popScope(); - out.back().insert(0, s("(function()"sv) + nll(comp)); - out.back().append(indent() + s("end)()"sv)); - break; - } - case ExpUsage::Assignment: { - out.push_back(clearBuf()); - auto assign = x->new_ptr(); - assign->values.push_back(toAst(accumVar, x)); - auto assignment = x->new_ptr(); - assignment->expList.set(assignList); - assignment->action.set(assign); - transformAssignment(assignment, temp); - popScope(); - out.back() = indent() + s("do"sv) + nll(comp) + - out.back() + temp.back() + - indent() + s("end"sv) + nlr(comp); - break; - } - case ExpUsage::Return: - out.push_back(clearBuf()); - out.back().append(indent() + s("return "sv) + accumVar + nlr(comp)); - break; - default: - break; - } - } - - void transformForEachHead(AssignableNameList_t* nameList, ast_node* loopTarget, str_list& out) { - auto x = nameList; - str_list temp; - str_list vars; - str_list varBefore, varAfter; - std::list>> destructPairs; - for (auto _item : nameList->items.objects()) { - auto item = static_cast(_item)->item.get(); - switch (item->getId()) { - case id(): - transformVariable(static_cast(item), vars); - varAfter.push_back(vars.back()); - break; - case id(): { - auto desVar = getUnusedName("_des_"sv); - destructPairs.emplace_back(item, toAst(desVar, x)); - vars.push_back(desVar); - varAfter.push_back(desVar); - break; - } - default: assert(false); break; - } - } - switch (loopTarget->getId()) { - case id(): { - auto star_exp = static_cast(loopTarget); - auto listVar = singleVariableFrom(star_exp->value); - auto indexVar = getUnusedName("_index_"sv); - varAfter.push_back(indexVar); - auto value = singleValueFrom(star_exp->value); - if (!value) throw std::logic_error(_info.errorMessage("invalid star syntax"sv, star_exp)); - bool endWithSlice = false; - BLOCK_START - auto chainValue = value->item.as(); - BREAK_IF(!chainValue); - auto chainList = chainValue->items.objects(); - auto slice = ast_cast(chainList.back()); - BREAK_IF(!slice); - endWithSlice = true; - if (listVar.empty() && chainList.size() == 2 && - ast_is(chainList.front())) { - transformCallable(static_cast(chainList.front()), temp); - listVar = temp.back(); - temp.pop_back(); - } - chainList.pop_back(); - auto chain = x->new_ptr(); - for (auto item : chainList) { - chain->items.push_back(item); - } - std::string startValue("1"sv); - if (auto exp = slice->startValue.as()) { - transformExp(exp, temp, ExpUsage::Closure); - startValue = temp.back(); - temp.pop_back(); - } - std::string stopValue; - if (auto exp = slice->stopValue.as()) { - transformExp(exp, temp, ExpUsage::Closure); - stopValue = temp.back(); - temp.pop_back(); - } - std::string stepValue; - if (auto exp = slice->stepValue.as()) { - transformExp(exp, temp, ExpUsage::Closure); - stepValue = temp.back(); - temp.pop_back(); - } - if (listVar.empty()) { - listVar = getUnusedName("_list_"sv); - varBefore.push_back(listVar); - transformChainValue(chain, temp, ExpUsage::Closure); - _buf << indent() << "local "sv << listVar << " = "sv << temp.back() << nll(nameList); - } - std::string maxVar; - if (!stopValue.empty()) { - maxVar = getUnusedName("_max_"sv); - varBefore.push_back(maxVar); - _buf << indent() << "local "sv << maxVar << " = "sv << stopValue << nll(nameList); - } - _buf << indent() << "for "sv << indexVar << " = "sv; - _buf << startValue << ", "sv; - if (stopValue.empty()) { - _buf << "#"sv << listVar; - } else { - _buf << maxVar << " < 0 and #"sv << listVar << " + "sv << maxVar << " or "sv << maxVar; - } - if (!stepValue.empty()) { - _buf << ", "sv << stepValue; - } - _buf << " do"sv << nlr(loopTarget); - _buf << indent(1) << "local "sv << join(vars, ", "sv) << " = "sv << listVar << "["sv << indexVar << "]"sv << nll(nameList); - out.push_back(clearBuf()); - BLOCK_END - bool newListVal = false; - if (listVar.empty()) { - newListVal = true; - listVar = getUnusedName("_list_"sv); - varBefore.push_back(listVar); - } - if (!endWithSlice) { - transformExp(star_exp->value, temp, ExpUsage::Closure); - if (newListVal) _buf << indent() << "local "sv << listVar << " = "sv << temp.back() << nll(nameList); - _buf << indent() << "for "sv << indexVar << " = 1, #"sv << listVar << " do"sv << nlr(loopTarget); - _buf << indent(1) << "local "sv << join(vars) << " = "sv << listVar << "["sv << indexVar << "]"sv << nll(nameList); - out.push_back(clearBuf()); - } - break; - } - case id(): - transformExp(static_cast(loopTarget), temp, ExpUsage::Closure); - _buf << indent() << "for "sv << join(vars, ", "sv) << " in "sv << temp.back() << " do"sv << nlr(loopTarget); - out.push_back(clearBuf()); - break; - case id(): - transformExpList(static_cast(loopTarget), temp); - _buf << indent() << "for "sv << join(vars, ", "sv) << " in "sv << temp.back() << " do"sv << nlr(loopTarget); - out.push_back(clearBuf()); - break; - default: assert(false); break; - } - for (auto& var : varBefore) addToScope(var); - pushScope(); - for (auto& var : varAfter) addToScope(var); - if (!destructPairs.empty()) { - temp.clear(); - for (auto& pair : destructPairs) { - auto sValue = x->new_ptr(); - sValue->value.set(pair.first); - auto value = x->new_ptr(); - value->item.set(sValue); - auto exp = newExp(value, x); - auto expList = x->new_ptr(); - expList->exprs.push_back(exp); - auto assign = x->new_ptr(); - assign->values.push_back(pair.second); - auto assignment = x->new_ptr(); - assignment->expList.set(expList); - assignment->action.set(assign); - transformAssignment(assignment, temp); - } - out.back().append(join(temp)); - } - } - - void transformCompForEach(CompForEach_t* comp, str_list& out) { - transformForEachHead(comp->nameList, comp->loopValue, out); - } - - void transformInvokeArgs(InvokeArgs_t* invokeArgs, str_list& out) { - if (invokeArgs->args.size() > 1) { - /* merge all the key-value pairs into one table - from arguments in the end */ - auto lastArg = invokeArgs->args.back(); - _ast_list* lastTable = nullptr; - if (auto tableBlock = ast_cast(lastArg)) { - lastTable = &tableBlock->values; - } else if (auto value = singleValueFrom(lastArg)) { - if (auto simpleTable = ast_cast(value->item)) { - lastTable = &simpleTable->pairs; - } - } - if (lastTable) { - ast_ptr ref(lastArg); - invokeArgs->args.pop_back(); - while (!invokeArgs->args.empty()) { - if (Value_t* value = singleValueFrom(invokeArgs->args.back())) { - if (auto tb = value->item.as()) { - const auto& ps = tb->pairs.objects(); - for (auto it = ps.rbegin(); it != ps.rend(); ++it) { - lastTable->push_front(*it); - } - invokeArgs->args.pop_back(); - continue; - } - } - break; - } - invokeArgs->args.push_back(lastArg); - } - } - str_list temp; - for (auto arg : invokeArgs->args.objects()) { - switch (arg->getId()) { - case id(): transformExp(static_cast(arg), temp, ExpUsage::Closure); break; - case id(): transformTableBlock(static_cast(arg), temp); break; - default: assert(false); break; - } - } - out.push_back(s("("sv) + join(temp, ", "sv) + s(")"sv)); - } - - void transformForHead(For_t* forNode, str_list& out) { - str_list temp; - std::string varName = _parser.toString(forNode->varName); - transformExp(forNode->startValue, temp, ExpUsage::Closure); - transformExp(forNode->stopValue, temp, ExpUsage::Closure); - if (forNode->stepValue) { - transformExp(forNode->stepValue->value, temp, ExpUsage::Closure); - } else { - temp.emplace_back(); - } - auto it = temp.begin(); - const auto& start = *it; - const auto& stop = *(++it); - const auto& step = *(++it); - _buf << indent() << "for "sv << varName << " = "sv << start << ", "sv << stop << (step.empty() ? Empty : s(", "sv) + step) << " do"sv << nll(forNode); - pushScope(); - addToScope(varName); - out.push_back(clearBuf()); - } - - void transform_plain_body(ast_node* body, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { - switch (body->getId()) { - case id(): - transformBlock(static_cast(body), out, usage, assignList); - break; - case id(): { - auto newBlock = body->new_ptr(); - newBlock->statements.push_back(body); - transformBlock(newBlock, out, usage, assignList); - break; - } - default: assert(false); break; - } - } - - void transformLoopBody(ast_node* body, str_list& out, const std::string& appendContent, ExpUsage usage, ExpList_t* assignList = nullptr) { - str_list temp; - bool withContinue = traversal::Stop == body->traverse([&](ast_node* node) { - if (auto stmt = ast_cast(node)) { - if (stmt->content.is()) { - return _parser.toString(stmt->content) == "continue"sv ? - traversal::Stop : traversal::Return; - } else if (expListFrom(stmt)) { - return traversal::Continue; - } - return traversal::Return; - } - return traversal::Continue; - }); - if (withContinue) { - auto continueVar = getUnusedName("_continue_"sv); - addToScope(continueVar); - _buf << indent() << "local "sv << continueVar << " = false"sv << nll(body); - _buf << indent() << "repeat"sv << nll(body); - temp.push_back(clearBuf()); - _continueVars.push(continueVar); - pushScope(); - } - transform_plain_body(body, temp, usage, assignList); - if (withContinue) { - if (!appendContent.empty()) { - _buf << indent() << appendContent; - } - _buf << indent() << _continueVars.top() << " = true"sv << nll(body); - popScope(); - _buf << indent() << "until true"sv << nlr(body); - _buf << indent() << "if not "sv << _continueVars.top() << " then"sv << nlr(body); - _buf << indent(1) << "break"sv << nlr(body); - _buf << indent() << "end"sv << nlr(body); - temp.push_back(clearBuf()); - _continueVars.pop(); - } else if (!appendContent.empty()) { - temp.back().append(indent() + appendContent); - } - out.push_back(join(temp)); - } - - void transformFor(For_t* forNode, str_list& out) { - str_list temp; - transformForHead(forNode, temp); - transformLoopBody(forNode->body, temp, Empty, ExpUsage::Common); - popScope(); - out.push_back(join(temp) + indent() + s("end"sv) + nlr(forNode)); - } - - std::string transformForInner(For_t* forNode, str_list& out) { - auto x = forNode; - std::string accum = getUnusedName("_accum_"sv); - addToScope(accum); - std::string len = getUnusedName("_len_"sv); - addToScope(len); - _buf << indent() << "local "sv << accum << " = { }"sv << nll(forNode); - _buf << indent() << "local "sv << len << " = 1"sv << nll(forNode); - out.push_back(clearBuf()); - transformForHead(forNode, out); - auto expList = toAst(accum + s("["sv) + len + s("]"sv), x); - auto lenLine = len + s(" = "sv) + len + s(" + 1"sv) + nlr(forNode->body); - transformLoopBody(forNode->body, out, lenLine, ExpUsage::Assignment, expList); - popScope(); - out.push_back(indent() + s("end"sv) + nlr(forNode)); - return accum; - } - - void transformForClosure(For_t* forNode, str_list& out) { - str_list temp; - _buf << "(function()"sv << nll(forNode); - pushScope(); - _enableReturn.push(true); - auto accum = transformForInner(forNode, temp); - temp.push_back(indent() + s("return "sv) + accum + nlr(forNode)); - _enableReturn.pop(); - popScope(); - temp.push_back(indent() + s("end)()"sv)); - out.push_back(join(temp)); - } - - void transformForInPlace(For_t* forNode, str_list& out, ExpList_t* assignExpList = nullptr) { - auto x = forNode; - str_list temp; - if (assignExpList) { - _buf << indent() << "do"sv << nll(forNode); - pushScope(); - auto accum = transformForInner(forNode, temp); - auto assign = x->new_ptr(); - assign->values.push_back(toAst(accum, x)); - auto assignment = x->new_ptr(); - assignment->expList.set(assignExpList); - assignment->action.set(assign); - transformAssignment(assignment, temp); - popScope(); - temp.push_back(indent() + s("end"sv) + nlr(forNode)); - } else { - auto accum = transformForInner(forNode, temp); - auto returnNode = x->new_ptr(); - auto expListLow = toAst(accum, x); - returnNode->valueList.set(expListLow); - transformReturn(returnNode, temp); - } - out.push_back(join(temp)); - } - - void transformBinaryOperator(BinaryOperator_t* node, str_list& out) { - auto op = _parser.toString(node); - out.push_back(op == "!="sv ? s("~="sv) : op); - } - - void transformForEach(ForEach_t* forEach, str_list& out) { - str_list temp; - transformForEachHead(forEach->nameList, forEach->loopValue, temp); - transformLoopBody(forEach->body, temp, Empty, ExpUsage::Common); - popScope(); - out.push_back(temp.front() + temp.back() + indent() + s("end"sv) + nlr(forEach)); - } - - std::string transformForEachInner(ForEach_t* forEach, str_list& out) { - auto x = forEach; - std::string accum = getUnusedName("_accum_"sv); - addToScope(accum); - std::string len = getUnusedName("_len_"sv); - addToScope(len); - _buf << indent() << "local "sv << accum << " = { }"sv << nll(forEach); - _buf << indent() << "local "sv << len << " = 1"sv << nll(forEach); - out.push_back(clearBuf()); - transformForEachHead(forEach->nameList, forEach->loopValue, out); - auto expList = toAst(accum + s("["sv) + len + s("]"sv), x); - auto lenLine = len + s(" = "sv) + len + s(" + 1"sv) + nlr(forEach->body); - transformLoopBody(forEach->body, out, lenLine, ExpUsage::Assignment, expList); - popScope(); - out.push_back(indent() + s("end"sv) + nlr(forEach)); - return accum; - } - - void transformForEachClosure(ForEach_t* forEach, str_list& out) { - str_list temp; - _buf << "(function()"sv << nll(forEach); - pushScope(); - _enableReturn.push(true); - auto accum = transformForEachInner(forEach, temp); - temp.push_back(indent() + s("return "sv) + accum + nlr(forEach)); - _enableReturn.pop(); - popScope(); - temp.push_back(indent() + s("end)()"sv)); - out.push_back(join(temp)); - } - - void transformForEachInPlace(ForEach_t* forEach, str_list& out, ExpList_t* assignExpList = nullptr) { - auto x = forEach; - str_list temp; - if (assignExpList) { - _buf << indent() << "do"sv << nll(forEach); - pushScope(); - auto accum = transformForEachInner(forEach, temp); - auto assign = x->new_ptr(); - assign->values.push_back(toAst(accum, x)); - auto assignment = x->new_ptr(); - assignment->expList.set(assignExpList); - assignment->action.set(assign); - transformAssignment(assignment, temp); - popScope(); - temp.push_back(indent() + s("end"sv) + nlr(forEach)); - } else { - auto accum = transformForEachInner(forEach, temp); - auto returnNode = x->new_ptr(); - auto expListLow = toAst(accum, x); - returnNode->valueList.set(expListLow); - transformReturn(returnNode, temp); - } - out.push_back(join(temp)); - } - - void transform_variable_pair(variable_pair_t* pair, str_list& out) { - auto name = _parser.toString(pair->name); - out.push_back(name + s(" = "sv) + name); - } - - void transform_normal_pair(normal_pair_t* pair, str_list& out) { - auto key = pair->key.get(); - str_list temp; - switch (key->getId()) { - case id(): { - transformKeyName(static_cast(key), temp); - if (LuaKeywords.find(temp.back()) != LuaKeywords.end()) { - temp.back() = s("[\""sv) + temp.back() + s("\"]"); - } - break; - } - case id(): - transformExp(static_cast(key), temp, ExpUsage::Closure); - temp.back() = s(temp.back().front() == '[' ? "[ "sv : "["sv) + temp.back() + s("]"sv); - break; - case id(): - transformDoubleString(static_cast(key), temp); - temp.back() = s("["sv) + temp.back() + s("]"sv); - break; - case id(): transformSingleString(static_cast(key), temp); - temp.back() = s("["sv) + temp.back() + s("]"sv); - break; - case id(): transformLuaString(static_cast(key), temp); - temp.back() = s("[ "sv) + temp.back() + s("]"sv); - break; - default: assert(false); break; - } - auto value = pair->value.get(); - switch (value->getId()) { - case id(): transformExp(static_cast(value), temp, ExpUsage::Closure); break; - case id(): transformTableBlock(static_cast(value), temp); break; - default: assert(false); break; - } - out.push_back(temp.front() + s(" = "sv) + temp.back()); - } - - void transformKeyName(KeyName_t* keyName, str_list& out) { - auto name = keyName->name.get(); - switch (name->getId()) { - case id(): transformSelfName(static_cast(name), out); break; - case id(): out.push_back(_parser.toString(name)); break; - default: assert(false); break; - } - } - - void transformLuaString(LuaString_t* luaString, str_list& out) { - auto content = _parser.toString(luaString->content); - Utils::replace(content, "\r\n"sv, "\n"); - if (content[0] == '\n') content.erase(content.begin()); - out.push_back(_parser.toString(luaString->open) + content + _parser.toString(luaString->close)); - } - - void transformSingleString(SingleString_t* singleString, str_list& out) { - auto str = _parser.toString(singleString); - Utils::replace(str, "\r\n"sv, "\n"); - Utils::replace(str, "\n"sv, "\\n"sv); - out.push_back(str); - } - - void transformDoubleString(DoubleString_t* doubleString, str_list& out) { - str_list temp; - for (auto _seg : doubleString->segments.objects()) { - auto seg = static_cast(_seg); - auto content = seg->content.get(); - switch (content->getId()) { - case id(): { - auto str = _parser.toString(content); - Utils::replace(str, "\r\n"sv, "\n"); - Utils::replace(str, "\n"sv, "\\n"sv); - temp.push_back(s("\""sv) + str + s("\""sv)); - break; - } - case id(): { - transformExp(static_cast(content), temp, ExpUsage::Closure); - std::string tostr("tostring"sv); - temp.back() = tostr + '(' + temp.back() + s(")"sv); - if (_config.lintGlobalVariable) { - if (!isDefined(tostr)) { - if (_globals.find(tostr) == _globals.end()) { - _globals[tostr] = {content->m_begin.m_line, content->m_begin.m_col}; - } - } - } - break; - } - default: assert(false); break; - } - } - out.push_back(temp.empty() ? s("\"\""sv) : join(temp, " .. "sv)); - } - - void transformString(String_t* string, str_list& out) { - auto str = string->str.get(); - switch (str->getId()) { - case id(): transformSingleString(static_cast(str), out); break; - case id(): transformDoubleString(static_cast(str), out); break; - case id(): transformLuaString(static_cast(str), out); break; - default: assert(false); break; - } - } - - std::pair defineClassVariable(Assignable_t* assignable) { - if (auto variable = assignable->item.as()) { - auto name = _parser.toString(variable); - if (addToScope(name)) { - return {name, true}; - } else { - return {name, false}; - } - } - return {Empty, false}; - } - - void transformClassDeclClosure(ClassDecl_t* classDecl, str_list& out) { - str_list temp; - temp.push_back(s("(function()"sv) + nll(classDecl)); - pushScope(); - _enableReturn.push(true); - transformClassDecl(classDecl, temp, ExpUsage::Return); - _enableReturn.pop(); - popScope(); - temp.push_back(s("end)()"sv)); - out.push_back(join(temp)); - } - - void transformClassDecl(ClassDecl_t* classDecl, str_list& out, ExpUsage usage, ExpList_t* expList = nullptr) { - str_list temp; - auto x = classDecl; - auto body = classDecl->body.get(); - auto assignable = classDecl->name.get(); - auto extend = classDecl->extend.get(); - std::string className; - std::string assignItem; - if (assignable) { - if (!isAssignable(assignable)) { - throw std::logic_error(_info.errorMessage("left hand expression is not assignable"sv, assignable)); - } - bool newDefined = false; - std::tie(className, newDefined) = defineClassVariable(assignable); - if (newDefined) { - temp.push_back(indent() + s("local "sv) + className + nll(classDecl)); - } - if (className.empty()) { - if (auto chain = ast_cast(assignable->item)) { - if (auto dotChain = ast_cast(chain->items.back())) { - className = s("\""sv) + _parser.toString(dotChain->name) + s("\""sv); - } else if (auto index = ast_cast(chain->items.back())) { - if (auto name = index->getByPath()) { - transformString(name, temp); - className = temp.back(); - temp.pop_back(); - } - } - } - } else { - className = s("\""sv) + className + s("\""sv); - } - pushScope(); - transformAssignable(assignable, temp); - popScope(); - assignItem = temp.back(); - temp.pop_back(); - } else if (expList) { - auto name = singleVariableFrom(expList); - if (!name.empty()) { - className = s("\""sv) + name + s("\""sv); - } - } - temp.push_back(indent() + s("do"sv) + nll(classDecl)); - pushScope(); - auto classVar = getUnusedName("_class_"sv); - addToScope(classVar); - temp.push_back(indent() + s("local "sv) + classVar + nll(classDecl)); - if (body) { - str_list varDefs; - for (auto item : body->contents.objects()) { - if (auto statement = ast_cast(item)) { - ClassDecl_t* clsDecl = nullptr; - if (auto assignment = assignmentFrom(statement)) { - auto names = transformAssignDefs(assignment->expList.get()); - varDefs.insert(varDefs.end(), names.begin(), names.end()); - auto info = extractDestructureInfo(assignment, true); - if (!info.first.empty()) { - for (const auto& destruct : info.first) - for (const auto& item : destruct.items) - if (item.isVariable && addToScope(item.name)) - varDefs.push_back(item.name); - } - BLOCK_START - auto assign = assignment->action.as(); - BREAK_IF(!assign); - BREAK_IF(assign->values.objects().size() != 1); - auto exp = ast_cast(assign->values.objects().front()); - BREAK_IF(!exp); - auto value = singleValueFrom(exp); - clsDecl = value->getByPath(); - BLOCK_END - } else if (auto expList = expListFrom(statement)) { - auto value = singleValueFrom(expList); - clsDecl = value->getByPath(); - } - if (clsDecl) { - std::string clsName; - bool newDefined = false; - std::tie(clsName,newDefined) = defineClassVariable(clsDecl->name); - if (newDefined) varDefs.push_back(clsName); - } - } - } - if (!varDefs.empty()) { - temp.push_back(indent() + s("local "sv) + join(varDefs, ", "sv) + nll(body)); - } - } - std::string parent, parentVar; - if (extend) { - parentVar = getUnusedName("_parent_"sv); - addToScope(parentVar); - transformExp(extend, temp, ExpUsage::Closure); - parent = temp.back(); - temp.pop_back(); - temp.push_back(indent() + s("local "sv) + parentVar + s(" = "sv) + parent + nll(classDecl)); - } - auto baseVar = getUnusedName("_base_"sv); - addToScope(baseVar); - temp.push_back(indent() + s("local "sv) + baseVar + s(" = "sv)); - str_list builtins; - str_list commons; - str_list statements; - if (body) { - std::list members; - for (auto content : classDecl->body->contents.objects()) { - switch (content->getId()) { - case id(): { - size_t inc = transform_class_member_list(static_cast(content), members, classVar); - auto it = members.end(); - for (size_t i = 0; i < inc; ++i, --it); - for (; it != members.end(); ++it) { - auto& member = *it; - if (member.type == MemType::Property) { - statements.push_back(indent() + member.item + nll(content)); - } else { - member.item = indent(1) + member.item; - } - } - break; - } - case id(): - transformStatement(static_cast(content), statements); - break; - default: assert(false); break; - } - } - for (auto& member : members) { - switch (member.type) { - case MemType::Common: - commons.push_back((commons.empty() ? Empty : s(","sv) + nll(member.node)) + member.item); - break; - case MemType::Builtin: - builtins.push_back((builtins.empty() ? Empty : s(","sv) + nll(member.node)) + member.item); - break; - default: break; - } - } - if (!commons.empty()) { - temp.back() += s("{"sv) + nll(body); - temp.push_back(join(commons) + nll(body)); - temp.push_back(indent() + s("}"sv) + nll(body)); - } else { - temp.back() += s("{ }"sv) + nll(body); - } - } else { - temp.back() += s("{ }"sv) + nll(classDecl); - } - temp.push_back(indent() + baseVar + s(".__index = "sv) + baseVar + nll(classDecl)); - str_list tmp; - if (usage == ExpUsage::Assignment) { - auto assign = x->new_ptr(); - assign->values.push_back(toAst(classVar, x)); - auto assignment = x->new_ptr(); - assignment->expList.set(expList); - assignment->action.set(assign); - transformAssignment(assignment, tmp); - } - if (extend) { - _buf << indent() << "setmetatable("sv << baseVar << ", "sv << parentVar << ".__base)"sv << nll(classDecl); - } - _buf << indent() << classVar << " = setmetatable({"sv << nll(classDecl); - if (!builtins.empty()) { - _buf << join(builtins) << ","sv << nll(classDecl); - } else { - if (extend) { - _buf << indent(1) << "__init = function(self, ...)"sv << nll(classDecl); - _buf << indent(2) << "return _class_0.__parent.__init(self, ...)"sv << nll(classDecl); - _buf << indent(1) << "end,"sv << nll(classDecl); - } else { - _buf << indent(1) << "__init = function() end,"sv << nll(classDecl); - } - } - _buf << indent(1) << "__base = "sv << baseVar; - if (!className.empty()) { - _buf << ","sv << nll(classDecl); - _buf << indent(1) << "__name = "sv << className; - } - if (extend) { - _buf << ","sv << nll(classDecl); - _buf << indent(1) << "__parent = "sv << parentVar; - } - _buf << nll(classDecl); - _buf << indent() << "}, {"sv << nll(classDecl); - if (extend) { - _buf << indent(1) << "__index = function(cls, name)"sv << nll(classDecl); - _buf << indent(2) << "local val = rawget("sv << baseVar << ", name)"sv << nll(classDecl); - _buf << indent(2) << "if val == nil then"sv << nll(classDecl); - _buf << indent(3) << "local parent = rawget(cls, \"__parent\")"sv << nll(classDecl); - _buf << indent(3) << "if parent then"sv << nll(classDecl); - _buf << indent(4) << "return parent[name]"sv << nll(classDecl); - _buf << indent(3) << "end"sv << nll(classDecl); - _buf << indent(2) << "else"sv << nll(classDecl); - _buf << indent(3) << "return val"sv << nll(classDecl); - _buf << indent(2) << "end"sv << nll(classDecl); - _buf << indent(1) << "end,"sv << nll(classDecl); - } else { - _buf << indent(1) << "__index = "sv << baseVar << ","sv << nll(classDecl); - } - _buf << indent(1) << "__call = function(cls, ...)"sv << nll(classDecl); - pushScope(); - auto selfVar = getUnusedName("_self_"sv); - addToScope(selfVar); - _buf << indent(1) << "local "sv << selfVar << " = setmetatable({}, "sv << baseVar << ")"sv << nll(classDecl); - _buf << indent(1) << "cls.__init("sv << selfVar << ", ...)"sv << nll(classDecl); - _buf << indent(1) << "return "sv << selfVar << nll(classDecl); - popScope(); - _buf << indent(1) << "end"sv << nll(classDecl); - _buf << indent() << "})"sv << nll(classDecl); - _buf << indent() << baseVar << ".__class = "sv << classVar << nll(classDecl); - if (!statements.empty()) { - _buf << indent() << "local self = "sv << classVar << ';' << nll(classDecl); - } - _buf << join(statements); - if (extend) { - _buf << indent() << "if "sv << parentVar << ".__inherited then"sv << nll(classDecl); - _buf << indent(1) << parentVar << ".__inherited("sv << parentVar << ", "sv << classVar << ")"sv << nll(classDecl); - _buf << indent() << "end"sv << nll(classDecl); - } - if (!assignItem.empty()) { - _buf << indent() << assignItem << " = "sv << classVar << nll(classDecl); - } - switch (usage) { - case ExpUsage::Return: { - _buf << indent() << "return "sv << classVar << nlr(classDecl); - break; - } - case ExpUsage::Assignment: { - _buf << tmp.back(); - break; - } - default: break; - } - temp.push_back(clearBuf()); - popScope(); - temp.push_back(indent() + s("end"sv) + nlr(classDecl)); - out.push_back(join(temp)); - } - - size_t transform_class_member_list(class_member_list_t* class_member_list, std::list& out, const std::string& classVar) { - str_list temp; - size_t count = 0; - for (auto keyValue : class_member_list->values.objects()) { - MemType type = MemType::Common; - BLOCK_START - auto normal_pair = ast_cast(keyValue); - BREAK_IF(!normal_pair); - auto keyName = normal_pair->key.as(); - BREAK_IF(!keyName); - std::string newSuperCall; - auto selfName = keyName->name.as(); - if (selfName) { - type = MemType::Property; - auto name = ast_cast(selfName->name); - if (!name) throw std::logic_error(_info.errorMessage("invalid class poperty name"sv, selfName->name)); - newSuperCall = classVar + s(".__parent."sv) + _parser.toString(name->name); - } else { - auto x = keyName; - auto nameNode = keyName->name.as(); - if (!nameNode) break; - auto name = _parser.toString(nameNode); - if (name == "new"sv) { - type = MemType::Builtin; - keyName->name.set(toAst("__init"sv, x)); - newSuperCall = classVar + s(".__parent.__init"sv); - } else { - newSuperCall = classVar + s(".__parent.__base."sv) + name; - } - } - normal_pair->value->traverse([&](ast_node* node) { - if (node->getId() == id()) return traversal::Return; - if (auto chainValue = ast_cast(node)) { - if (auto callable = ast_cast(chainValue->items.front())) { - auto var = callable->item.get(); - if (_parser.toString(var) == "super"sv) { - auto insertSelfToArguments = [&](ast_node* item) { - auto x = item; - switch (item->getId()) { - case id(): { - auto invoke = static_cast(item); - invoke->args.push_front(toAst("self"sv, x)); - return true; - } - case id(): { - auto invoke = static_cast(item); - invoke->args.push_front(toAst("self"sv, x)); - return true; - } - default: - return false; - } - }; - const auto& chainList = chainValue->items.objects(); - if (chainList.size() >= 2) { - auto it = chainList.begin(); - auto secondItem = *(++it); - if (!insertSelfToArguments(secondItem)) { - if (auto colonChainItem = ast_cast(secondItem)) { - if (chainList.size() > 2 && insertSelfToArguments(*(++it))) { - colonChainItem->switchToDot = true; - } - } - newSuperCall = classVar + s(".__parent"sv); - } - } else { - newSuperCall = classVar + s(".__parent"sv); - } - auto newChain = toAst(newSuperCall, chainValue); - chainValue->items.pop_front(); - const auto& items = newChain->items.objects(); - for (auto it = items.rbegin(); it != items.rend(); ++it) { - chainValue->items.push_front(*it); - } - } - } - } - return traversal::Continue; - }); - BLOCK_END - pushScope(); - if (type == MemType::Property) { - decIndentOffset(); - } - switch (keyValue->getId()) { - case id(): - transform_variable_pair(static_cast(keyValue), temp); - break; - case id(): - transform_normal_pair(static_cast(keyValue), temp); - break; - default: assert(false); break; - } - if (type == MemType::Property) { - incIndentOffset(); - } - popScope(); - out.push_back({temp.back(), type, keyValue}); - temp.clear(); - ++count; - } - return count; - } - - void transformAssignable(Assignable_t* assignable, str_list& out) { - auto item = assignable->item.get(); - switch (item->getId()) { - case id(): transformAssignableChain(static_cast(item), out); break; - case id(): transformVariable(static_cast(item), out); break; - case id(): transformSelfName(static_cast(item), out); break; - default: assert(false); break; - } - } - - void transformWithClosure(With_t* with, str_list& out) { - str_list temp; - temp.push_back(s("(function()"sv) + nll(with)); - pushScope(); - _enableReturn.push(true); - transformWith(with, temp, nullptr, true); - _enableReturn.pop(); - popScope(); - temp.push_back(indent() + s("end)()"sv)); - out.push_back(join(temp)); - } - - void transformWith(With_t* with, str_list& out, ExpList_t* assignList = nullptr, bool returnValue = false) { - auto x = with; - str_list temp; - std::string withVar; - bool scoped = false; - if (with->assigns) { - checkAssignable(with->valueList); - auto vars = getAssignVars(with); - if (vars.front().empty()) { - if (with->assigns->values.objects().size() == 1) { - auto var = singleVariableFrom(with->assigns->values.objects().front()); - if (!var.empty()) { - withVar = var; - } - } - if (withVar.empty()) { - withVar = getUnusedName("_with_"sv); - auto assignment = x->new_ptr(); - assignment->expList.set(toAst(withVar, x)); - auto assign = x->new_ptr(); - assign->values.push_back(with->assigns->values.objects().front()); - assignment->action.set(assign); - if (!returnValue) { - scoped = true; - temp.push_back(indent() + s("do"sv) + nll(with)); - pushScope(); - } - transformAssignment(assignment, temp); - } - auto assignment = x->new_ptr(); - assignment->expList.set(with->valueList); - auto assign = x->new_ptr(); - assign->values.push_back(toAst(withVar, x)); - bool skipFirst = true; - for (auto value : with->assigns->values.objects()) { - if (skipFirst) { - skipFirst = false; - continue; - } - assign->values.push_back(value); - } - assignment->action.set(assign); - transformAssignment(assignment, temp); - } else { - withVar = vars.front(); - auto assignment = x->new_ptr(); - assignment->expList.set(with->valueList); - assignment->action.set(with->assigns); - if (!returnValue) { - scoped = true; - temp.push_back(indent() + s("do"sv) + nll(with)); - pushScope(); - } - transformAssignment(assignment, temp); - } - } else { - withVar = singleVariableFrom(with->valueList); - if (withVar.empty()) { - withVar = getUnusedName("_with_"sv); - auto assignment = x->new_ptr(); - assignment->expList.set(toAst(withVar, x)); - auto assign = x->new_ptr(); - assign->values.dup(with->valueList->exprs); - assignment->action.set(assign); - if (!returnValue) { - scoped = true; - temp.push_back(indent() + s("do"sv) + nll(with)); - pushScope(); - } - transformAssignment(assignment, temp); - } - } - if (!with->eop && !scoped && !returnValue) { - pushScope(); - scoped = traversal::Stop == with->body->traverse([&](ast_node* node) { - if (auto statement = ast_cast(node)) { - ClassDecl_t* clsDecl = nullptr; - if (auto assignment = assignmentFrom(statement)) { - auto names = getAssignDefs(assignment->expList.get()); - if (!names.empty()) { - return traversal::Stop; - } - auto info = extractDestructureInfo(assignment, true); - if (!info.first.empty()) { - for (const auto& destruct : info.first) - for (const auto& item : destruct.items) - if (item.isVariable && !isDefined(item.name)) - return traversal::Stop; - } - BLOCK_START - auto assign = assignment->action.as(); - BREAK_IF(!assign); - BREAK_IF(assign->values.objects().size() != 1); - auto exp = ast_cast(assign->values.objects().front()); - BREAK_IF(!exp); - if (auto value = singleValueFrom(exp)) { - clsDecl = value->getByPath(); - } - BLOCK_END - } else if (auto expList = expListFrom(statement)) { - auto value = singleValueFrom(expList); - clsDecl = value->getByPath(); - } - if (clsDecl) { - auto variable = clsDecl->name.as(); - if (!isDefined(_parser.toString(variable))) return traversal::Stop; - } - return traversal::Return; - } - return traversal::Continue; - }); - popScope(); - if (scoped) { - temp.push_back(indent() + s("do"sv) + nll(with)); - pushScope(); - } - } - _withVars.push(withVar); - if (with->eop) { - auto ifNode = x->new_ptr(); - ifNode->nodes.push_back(toAst(withVar + s("~=nil"sv), x)); - ifNode->nodes.push_back(with->body); - transformIf(ifNode, temp, ExpUsage::Common); - } else { - transform_plain_body(with->body, temp, ExpUsage::Common); - } - _withVars.pop(); - if (assignList) { - auto assignment = x->new_ptr(); - assignment->expList.set(assignList); - auto assign = x->new_ptr(); - assign->values.push_back(toAst(withVar, x)); - assignment->action.set(assign); - transformAssignment(assignment, temp); - } - if (returnValue) { - auto last = lastStatementFrom(with->body); - if (last && !last->content.is()) { - temp.push_back(indent() + s("return "sv) + withVar + nll(with)); - } - } - if (scoped) { - popScope(); - temp.push_back(indent() + s("end"sv) + nll(with)); - } - out.push_back(join(temp)); - } - - void transform_const_value(const_value_t* const_value, str_list& out) { - out.push_back(_parser.toString(const_value)); - } - - void transformGlobal(Global_t* global, str_list& out) { - auto x = global; - auto item = global->item.get(); - switch (item->getId()) { - case id(): { - auto classDecl = static_cast(item); - if (classDecl->name && classDecl->name->item->getId() == id()) { - markVarGlobal(GlobalMode::Any, true); - addGlobalVar(_parser.toString(classDecl->name->item)); - } - transformClassDecl(classDecl, out, ExpUsage::Common); - break; - } - case id(): - if (_parser.toString(item) == "*"sv) { - markVarGlobal(GlobalMode::Any, false); - } else { - markVarGlobal(GlobalMode::Capital, false); - } - break; - case id(): { - markVarGlobal(GlobalMode::Any, true); - auto values = global->item.to(); - if (values->valueList) { - auto expList = x->new_ptr(); - for (auto name : values->nameList->names.objects()) { - addGlobalVar(_parser.toString(name)); - auto callable = x->new_ptr(); - callable->item.set(name); - auto chainValue = x->new_ptr(); - chainValue->items.push_back(callable); - auto value = x->new_ptr(); - value->item.set(chainValue); - auto exp = newExp(value, x); - expList->exprs.push_back(exp); - } - auto assignment = x->new_ptr(); - assignment->expList.set(expList); - auto assign = x->new_ptr(); - if (auto expListLow = values->valueList.as()) { - assign->values.dup(expListLow->exprs); - } else { - auto tableBlock = values->valueList.to(); - assign->values.push_back(tableBlock); - } - assignment->action.set(assign); - transformAssignment(assignment, out); - } else { - for (auto name : values->nameList->names.objects()) { - addGlobalVar(_parser.toString(name)); - } - } - break; - } - default: assert(false); break; - } - } - - void transformExport(Export_t* exportNode, str_list& out) { - auto x = exportNode; - if (_scopes.size() > 1) { - throw std::logic_error(_info.errorMessage("can not do module export outside the root block"sv, exportNode)); - } - if (exportNode->assign) { - auto expList = exportNode->target.to(); - if (expList->exprs.size() != exportNode->assign->values.size()) { - throw std::logic_error(_info.errorMessage("left and right expressions must be matched in export statement"sv, x)); - } - for (auto _exp : expList->exprs.objects()) { - auto exp = static_cast(_exp); - if (!variableFrom(exp) && - !exp->getByPath() && - !exp->getByPath()) { - throw std::logic_error(_info.errorMessage("left hand expressions must be variables in export statement"sv, x)); - } - } - auto assignment = x->new_ptr(); - assignment->expList.set(expList); - assignment->action.set(exportNode->assign); - transformAssignment(assignment, out); - str_list names = transformAssignDefs(expList, false); - auto info = extractDestructureInfo(assignment, true); - if (!info.first.empty()) { - for (const auto& destruct : info.first) - for (const auto& item : destruct.items) - if (item.isVariable) - names.push_back(item.name); - } - if (_info.exportDefault) { - out.back().append(indent() + _info.moduleName + s(" = "sv) + names.back() + nlr(exportNode)); - } else { - str_list lefts, rights; - for (const auto& name : names) { - lefts.push_back(_info.moduleName + s("[\""sv) + name + s("\"]"sv)); - rights.push_back(name); - } - out.back().append(indent() + join(lefts,", "sv) + s(" = "sv) + join(rights, ", "sv) + nlr(exportNode)); - } - } else { - if (auto macro = exportNode->target.as()) { - transformMacro(macro, out, true); - } else if (_info.exportDefault) { - auto exp = exportNode->target.to(); - auto assignment = x->new_ptr(); - assignment->expList.set(toAst(_info.moduleName, x)); - auto assign = x->new_ptr(); - assign->values.push_back(exp); - assignment->action.set(assign); - transformAssignment(assignment, out); - } else { - str_list temp; - auto expList = exportNode->target.to(); - auto assignment = x->new_ptr(); - auto assignList = toAst(_info.moduleName + s("[#"sv) + _info.moduleName + s("+1]"sv), x); - assignment->expList.set(assignList); - for (auto exp : expList->exprs.objects()) { - if (auto classDecl = exp->getByPath()) { - if (classDecl->name && classDecl->name->item->getId() == id()) { - transformClassDecl(classDecl, temp, ExpUsage::Common); - auto name = _parser.toString(classDecl->name->item); - assignment->expList.set(toAst(_info.moduleName + s("[\""sv) + name + s("\"]"sv), x)); - auto assign = x->new_ptr(); - assign->values.push_back(toAst(name, x)); - assignment->action.set(assign); - transformAssignment(assignment, temp); - assignment->expList.set(assignList); - continue; - } - } - auto assign = x->new_ptr(); - assign->values.push_back(exp); - assignment->action.set(assign); - transformAssignment(assignment, temp); - } - out.push_back(join(temp)); - } - } - } - - void transformTable(ast_node* table, const node_container& pairs, str_list& out) { - if (pairs.empty()) { - out.push_back(s("{ }"sv)); - return; - } - str_list temp; - incIndentOffset(); - for (auto pair : pairs) { - switch (pair->getId()) { - case id(): transformExp(static_cast(pair), temp, ExpUsage::Closure); break; - case id(): transform_variable_pair(static_cast(pair), temp); break; - case id(): transform_normal_pair(static_cast(pair), temp); break; - case id(): transformTableBlockIndent(static_cast(pair), temp); break; - case id(): transformTableBlock(static_cast(pair), temp); break; - default: assert(false); break; - } - temp.back() = indent() + temp.back() + (pair == pairs.back() ? Empty : s(","sv)) + nll(pair); - } - out.push_back(s("{"sv) + nll(table) + join(temp)); - decIndentOffset(); - out.back() += (indent() + s("}"sv)); - } - - void transform_simple_table(simple_table_t* table, str_list& out) { - transformTable(table, table->pairs.objects(), out); - } - - void transformTblComprehension(TblComprehension_t* comp, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { - switch (usage) { - case ExpUsage::Closure: - pushScope(); - _enableReturn.push(true); - break; - case ExpUsage::Assignment: - pushScope(); - break; - default: - break; - } - auto x = comp; - str_list kv; - std::string tbl = getUnusedName("_tbl_"sv); - addToScope(tbl); - str_list temp; - auto compInner = comp->forLoop.get(); - for (auto item : compInner->items.objects()) { - switch (item->getId()) { - case id(): - transformCompForEach(static_cast(item), temp); - break; - case id(): - transformCompFor(static_cast(item), temp); - break; - case id(): - transformExp(static_cast(item), temp, ExpUsage::Closure); - temp.back() = indent() + s("if "sv) + temp.back() + s(" then"sv) + nll(item); - pushScope(); - break; - default: assert(false); break; - } - } - transformExp(comp->key, kv, ExpUsage::Closure); - if (comp->value) { - transformExp(comp->value->value, kv, ExpUsage::Closure); - } - for (size_t i = 0; i < compInner->items.objects().size(); ++i) { - popScope(); - } - _buf << indent() << "local "sv << tbl << " = { }"sv << nll(comp); - _buf << join(temp); - pushScope(); - if (!comp->value) { - auto keyVar = getUnusedName("_key_"sv); - auto valVar = getUnusedName("_val_"sv); - _buf << indent(int(temp.size()) - 1) << "local "sv << keyVar << ", "sv << valVar << " = "sv << kv.front() << nll(comp); - kv.front() = keyVar; - kv.push_back(valVar); - } - _buf << indent(int(temp.size()) - 1) << tbl << "["sv << kv.front() << "] = "sv << kv.back() << nll(comp); - for (int ind = int(temp.size()) - 2; ind > -1 ; --ind) { - _buf << indent(ind) << "end"sv << nll(comp); - } - popScope(); - _buf << indent() << "end"sv << nll(comp); - switch (usage) { - case ExpUsage::Closure: - out.push_back(clearBuf() + indent() + s("return "sv) + tbl + nlr(comp)); - popScope(); - _enableReturn.pop(); - out.back().insert(0, s("(function()"sv) + nll(comp)); - out.back().append(indent() + s("end)()"sv)); - break; - case ExpUsage::Assignment: { - out.push_back(clearBuf()); - auto assign = x->new_ptr(); - assign->values.push_back(toAst(tbl, x)); - auto assignment = x->new_ptr(); - assignment->expList.set(assignList); - assignment->action.set(assign); - transformAssignment(assignment, temp); - out.back().append(temp.back()); - popScope(); - out.back().insert(0, indent() + s("do"sv) + nll(comp)); - out.back().append(indent() + s("end"sv) + nlr(comp)); - break; - } - case ExpUsage::Return: - out.push_back(clearBuf() + indent() + s("return "sv) + tbl + nlr(comp)); - break; - default: - break; - } - } - - void transformCompFor(CompFor_t* comp, str_list& out) { - str_list temp; - std::string varName = _parser.toString(comp->varName); - transformExp(comp->startValue, temp, ExpUsage::Closure); - transformExp(comp->stopValue, temp, ExpUsage::Closure); - if (comp->stepValue) { - transformExp(comp->stepValue->value, temp, ExpUsage::Closure); - } else { - temp.emplace_back(); - } - auto it = temp.begin(); - const auto& start = *it; - const auto& stop = *(++it); - const auto& step = *(++it); - _buf << indent() << "for "sv << varName << " = "sv << start << ", "sv << stop << (step.empty() ? Empty : s(", "sv) + step) << " do"sv << nll(comp); - out.push_back(clearBuf()); - pushScope(); - addToScope(varName); - } - - void transformTableBlockIndent(TableBlockIndent_t* table, str_list& out) { - transformTable(table, table->values.objects(), out); - } - - void transformTableBlock(TableBlock_t* table, str_list& out) { - transformTable(table, table->values.objects(), out); - } - - void transformDo(Do_t* doNode, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { - str_list temp; - if (usage == ExpUsage::Closure) { - temp.push_back(s("(function()"sv) + nll(doNode)); - _enableReturn.push(true); - } else { - temp.push_back(indent() + s("do"sv) + nll(doNode)); - } - pushScope(); - transformBody(doNode->body, temp, usage, assignList); - popScope(); - if (usage == ExpUsage::Closure) { - _enableReturn.pop(); - temp.push_back(indent() + s("end)()"sv)); - } else { - temp.push_back(indent() + s("end"sv) + nlr(doNode)); - } - out.push_back(join(temp)); - } - - void transformImportFrom(ImportFrom_t* import, str_list& out) { - str_list temp; - auto x = import; - auto objVar = singleVariableFrom(import->exp); - ast_ptr objAssign; - if (objVar.empty()) { - objVar = getUnusedName("_obj_"sv); - auto expList = toAst(objVar, x); - auto assign = x->new_ptr(); - assign->values.push_back(import->exp); - auto assignment = x->new_ptr(); - assignment->expList.set(expList); - assignment->action.set(assign); - objAssign.set(assignment); - } - auto expList = x->new_ptr(); - auto assign = x->new_ptr(); - for (auto name : import->names.objects()) { - switch (name->getId()) { - case id(): { - auto var = ast_to(name); - { - auto callable = toAst(objVar, x); - auto dotChainItem = x->new_ptr(); - dotChainItem->name.set(var->name); - auto chainValue = x->new_ptr(); - chainValue->items.push_back(callable); - chainValue->items.push_back(dotChainItem); - auto value = x->new_ptr(); - value->item.set(chainValue); - auto exp = newExp(value, x); - assign->values.push_back(exp); - } - auto callable = x->new_ptr(); - callable->item.set(var); - auto chainValue = x->new_ptr(); - chainValue->items.push_back(callable); - auto value = x->new_ptr(); - value->item.set(chainValue); - auto exp = newExp(value, x); - expList->exprs.push_back(exp); - break; - } - case id(): { - auto var = static_cast(name)->name.get(); - { - auto nameNode = var->name.get(); - auto callable = toAst(objVar, x); - auto colonChain = x->new_ptr(); - colonChain->name.set(nameNode); - auto chainValue = x->new_ptr(); - chainValue->items.push_back(callable); - chainValue->items.push_back(colonChain); - auto value = x->new_ptr(); - value->item.set(chainValue); - auto exp = newExp(value, x); - assign->values.push_back(exp); - } - auto callable = x->new_ptr(); - callable->item.set(var); - auto chainValue = x->new_ptr(); - chainValue->items.push_back(callable); - auto value = x->new_ptr(); - value->item.set(chainValue); - auto exp = newExp(value, x); - expList->exprs.push_back(exp); - break; - } - default: assert(false); break; - } - } - if (objAssign) { - auto preDef = getPredefine(transformAssignDefs(expList)); - if (!preDef.empty()) { - temp.push_back(preDef + nll(import)); - } - temp.push_back(indent() + s("do"sv) + nll(import)); - pushScope(); - transformAssignment(objAssign, temp); - } - auto assignment = x->new_ptr(); - assignment->expList.set(expList); - assignment->action.set(assign); - transformAssignment(assignment, temp); - if (objAssign) { - popScope(); - temp.push_back(indent() + s("end"sv) + nlr(import)); - } - out.push_back(join(temp)); - } - - std::string moduleNameFrom(ImportLiteral_t* literal) { - auto name = _parser.toString(literal->inners.back()); - Utils::replace(name, "-"sv, "_"sv); - Utils::replace(name, " "sv, "_"sv); - return name; - } - - void transformImportAs(ImportAs_t* import, str_list& out) { - auto x = import; - if (!import->target) { - auto name = moduleNameFrom(import->literal); - import->target.set(toAst(name, x)); - } - if (auto tabLit = import->target.as()) { - auto newTab = x->new_ptr(); -#ifndef MOONP_NO_MACRO - bool importAllMacro = false; - std::list> macroPairs; - for (auto item : tabLit->items.objects()) { - switch (item->getId()) { - case id(): { - auto macroName = static_cast(item); - auto name = _parser.toString(macroName->name); - macroPairs.emplace_back(name, name); - break; - } - case id(): { - auto pair = static_cast(item); - macroPairs.emplace_back(_parser.toString(pair->key->name), _parser.toString(pair->value->name)); - break; - } - case id(): - if (importAllMacro) throw std::logic_error(_info.errorMessage(s("import all macro symbol duplicated"sv), item)); - importAllMacro = true; - break; - case id(): - case id(): - newTab->items.push_back(item); - break; - default: assert(false); break; - } - } - if (importAllMacro || !macroPairs.empty()) { - auto moduleName = _parser.toString(import->literal); - Utils::replace(moduleName, "'"sv, ""sv); - Utils::replace(moduleName, "\""sv, ""sv); - Utils::trim(moduleName); - pushCurrentModule(); // cur - int top = lua_gettop(L) - 1; // Lua state may be setup by pushCurrentModule() - DEFER(lua_settop(L, top)); - pushMoonp("find_modulepath"sv); // cur find_modulepath - lua_pushlstring(L, moduleName.c_str(), moduleName.size()); // cur find_modulepath moduleName - if (lua_pcall(L, 1, 1, 0) != 0) { - std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage(s("failed to resolve module path\n"sv) + err, x)); - } - if (lua_isnil(L, -1) != 0) { - throw std::logic_error(_info.errorMessage(s("failed to find module '"sv) + moduleName + '\'', x)); - } - std::string moduleFullName = lua_tostring(L, -1); - lua_pop(L, 1); // cur - if (!isModuleLoaded(moduleFullName)) { - pushMoonp("read_file"sv); // cur read_file - lua_pushlstring(L, moduleFullName.c_str(), moduleFullName.size()); // cur load_text moduleFullName - if (lua_pcall(L, 1, 1, 0) != 0) { - std::string err = lua_tostring(L, -1); - throw std::logic_error(_info.errorMessage(s("failed to read module file\n"sv) + err, x)); - } // cur text - if (lua_isnil(L, -1) != 0) { - throw std::logic_error(_info.errorMessage("failed to get module text"sv, x)); - } // cur text - std::string text = lua_tostring(L, -1); - auto compiler = MoonCompilerImpl(L, _luaOpen, false, moduleFullName); - MoonConfig config; - config.lineOffset = 0; - config.lintGlobalVariable = false; - config.reserveLineNumber = false; - config.implicitReturnRoot = _config.implicitReturnRoot; - auto result = compiler.compile(text, config); - if (result.codes.empty() && !result.error.empty()) { - throw std::logic_error(_info.errorMessage(s("failed to compile module '"sv) + moduleName + s("\': "sv) + result.error, x)); - } - lua_pop(L, 1); // cur - } - pushModuleTable(moduleFullName); // cur mod - if (importAllMacro) { - lua_pushnil(L); // cur mod startKey - while (lua_next(L, -2) != 0) { // cur mod key value - lua_pushvalue(L, -2); // cur mod key value key - lua_insert(L, -2); // cur mod key key value - lua_rawset(L, -5); // cur[key] = value, cur mod key - } - } - for (const auto& pair : macroPairs) { - lua_getfield(L, -1, pair.first.c_str()); // mod[first], cur mod val - lua_setfield(L, -3, pair.second.c_str()); // cur[second] = val, cur mod - } - } -#else // MOONP_NO_MACRO - for (auto item : tabLit->items.objects()) { - switch (item->getId()) { - case id(): - case id(): - case id(): { - throw std::logic_error(_info.errorMessage("macro feature not supported"sv, item)); - break; - } - case id(): - case id(): - newTab->items.push_back(item); - break; - default: assert(false); break; - } - } -#endif // MOONP_NO_MACRO - if (newTab->items.empty()) { - out.push_back(Empty); - return; - } else { - import->target.set(newTab); - } - } - auto target = import->target.get(); - auto value = x->new_ptr(); - if (auto var = ast_cast(target)) { - auto callable = x->new_ptr(); - callable->item.set(var); - auto chainValue = x->new_ptr(); - chainValue->items.push_back(callable); - value->item.set(chainValue); - } else { - auto tabLit = ast_to(target); - auto simpleValue = x->new_ptr(); - auto tableLit = x->new_ptr(); - tableLit->values.dup(tabLit->items); - simpleValue->value.set(tableLit); - value->item.set(simpleValue); - } - auto exp = newExp(value, x); - auto assignList = x->new_ptr(); - assignList->exprs.push_back(exp); - auto assign = x->new_ptr(); - assign->values.push_back(toAst(s("require "sv) + _parser.toString(import->literal), x)); - auto assignment = x->new_ptr(); - assignment->expList.set(assignList); - assignment->action.set(assign); - transformAssignment(assignment, out); - } - - void transformImport(Import_t* import, str_list& out) { - auto content = import->content.get(); - switch (content->getId()) { - case id(): - transformImportAs(static_cast(content), out); - break; - case id(): - transformImportFrom(static_cast(content), out); - break; - default: assert(false); break; - } - } - - void transformWhileInPlace(While_t* whileNode, str_list& out, ExpList_t* expList = nullptr) { - auto x = whileNode; - str_list temp; - if (expList) { - temp.push_back(indent() + s("do"sv) + nll(whileNode)); - } - pushScope(); - auto accumVar = getUnusedName("_accum_"sv); - addToScope(accumVar); - auto lenVar = getUnusedName("_len_"sv); - addToScope(lenVar); - temp.push_back(indent() + s("local "sv) + accumVar + s(" = { }"sv) + nll(whileNode)); - temp.push_back(indent() + s("local "sv) + lenVar + s(" = 1"sv) + nll(whileNode)); - transformExp(whileNode->condition, temp, ExpUsage::Closure); - temp.back() = indent() + s("while "sv) + temp.back() + s(" do"sv) + nll(whileNode); - pushScope(); - auto assignLeft = toAst(accumVar + s("["sv) + lenVar + s("]"sv), x); - auto lenLine = lenVar + s(" = "sv) + lenVar + s(" + 1"sv) + nlr(whileNode); - transformLoopBody(whileNode->body, temp, lenLine, ExpUsage::Assignment, assignLeft); - popScope(); - temp.push_back(indent() + s("end"sv) + nlr(whileNode)); - if (expList) { - auto assign = x->new_ptr(); - assign->values.push_back(toAst(accumVar, x)); - auto assignment = x->new_ptr(); - assignment->expList.set(expList); - assignment->action.set(assign); - transformAssignment(assignment, temp); - } else { - temp.push_back(indent() + s("return "sv) + accumVar + nlr(whileNode)); - } - popScope(); - if (expList) { - temp.push_back(indent() + s("end"sv) + nlr(whileNode)); - } - out.push_back(join(temp)); - } - - void transformWhileClosure(While_t* whileNode, str_list& out) { - auto x = whileNode; - str_list temp; - temp.push_back(s("(function() "sv) + nll(whileNode)); - pushScope(); - _enableReturn.push(true); - auto accumVar = getUnusedName("_accum_"sv); - addToScope(accumVar); - auto lenVar = getUnusedName("_len_"sv); - addToScope(lenVar); - temp.push_back(indent() + s("local "sv) + accumVar + s(" = { }"sv) + nll(whileNode)); - temp.push_back(indent() + s("local "sv) + lenVar + s(" = 1"sv) + nll(whileNode)); - transformExp(whileNode->condition, temp, ExpUsage::Closure); - temp.back() = indent() + s("while "sv) + temp.back() + s(" do"sv) + nll(whileNode); - pushScope(); - auto assignLeft = toAst(accumVar + s("["sv) + lenVar + s("]"sv), x); - auto lenLine = lenVar + s(" = "sv) + lenVar + s(" + 1"sv) + nlr(whileNode); - transformLoopBody(whileNode->body, temp, lenLine, ExpUsage::Assignment, assignLeft); - popScope(); - temp.push_back(indent() + s("end"sv) + nlr(whileNode)); - temp.push_back(indent() + s("return "sv) + accumVar + nlr(whileNode)); - _enableReturn.pop(); - popScope(); - temp.push_back(indent() + s("end)()"sv)); - out.push_back(join(temp)); - } - - void transformWhile(While_t* whileNode, str_list& out) { - str_list temp; - pushScope(); - transformExp(whileNode->condition, temp, ExpUsage::Closure); - transformLoopBody(whileNode->body, temp, Empty, ExpUsage::Common); - popScope(); - _buf << indent() << "while "sv << temp.front() << " do"sv << nll(whileNode); - _buf << temp.back(); - _buf << indent() << "end"sv << nlr(whileNode); - out.push_back(clearBuf()); - } - - void transformRepeat(Repeat_t* repeat, str_list& out) { - str_list temp; - pushScope(); - transformLoopBody(repeat->body->content, temp, Empty, ExpUsage::Common); - transformExp(repeat->condition, temp, ExpUsage::Closure); - popScope(); - _buf << indent() << "repeat"sv << nll(repeat); - _buf << temp.front(); - _buf << indent() << "until "sv << temp.back() << nlr(repeat); - out.push_back(clearBuf()); - } - - void transformSwitch(Switch_t* switchNode, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { - str_list temp; - if (usage == ExpUsage::Closure) { - temp.push_back(s("(function()"sv) + nll(switchNode)); - pushScope(); - _enableReturn.push(true); - } - auto objVar = singleVariableFrom(switchNode->target); - if (objVar.empty()) { - objVar = getUnusedName("_exp_"sv); - addToScope(objVar); - transformExp(switchNode->target, temp, ExpUsage::Closure); - _buf << indent() << "local "sv << objVar << " = "sv << temp.back() << nll(switchNode); - temp.back() = clearBuf(); - } - const auto& branches = switchNode->branches.objects(); - for (auto branch_ : branches) { - auto branch = static_cast(branch_); - temp.push_back(indent() + s(branches.front() == branch ? "if"sv : "elseif"sv)); - str_list tmp; - const auto& exprs = branch->valueList->exprs.objects(); - for (auto exp_ : exprs) { - auto exp = static_cast(exp_); - transformExp(exp, tmp, ExpUsage::Closure); - if (!singleValueFrom(exp)) { - tmp.back() = s("("sv) + tmp.back() + s(")"sv); - } - temp.back().append(s(" "sv) + tmp.back() + s(" == "sv) + objVar + - s(exp == exprs.back() ? ""sv : " or"sv)); - } - temp.back().append(s(" then"sv) + nll(branch)); - pushScope(); - transform_plain_body(branch->body, temp, usage, assignList); - popScope(); - } - if (switchNode->lastBranch) { - temp.push_back(indent() + s("else"sv) + nll(switchNode->lastBranch)); - pushScope(); - transform_plain_body(switchNode->lastBranch, temp, usage, assignList); - popScope(); - } - temp.push_back(indent() + s("end"sv) + nlr(switchNode)); - if (usage == ExpUsage::Closure) { - _enableReturn.pop(); - popScope(); - temp.push_back(indent() + s("end)()"sv)); - } - out.push_back(join(temp)); - } - - void transformLocalDef(Local_t* local, str_list& out) { - if (!local->forceDecls.empty() || !local->decls.empty()) { - str_list defs; - for (const auto& decl : local->forceDecls) { - forceAddToScope(decl); - defs.push_back(decl); - } - for (const auto& decl : local->decls) { - if (addToScope(decl)) { - defs.push_back(decl); - } - } - auto preDefine = getPredefine(defs); - if (!preDefine.empty()) { - out.push_back(preDefine + nll(local)); - } - } - } - - void transformLocal(Local_t* local, str_list& out) { - str_list temp; - if (!local->defined) { - local->defined = true; - transformLocalDef(local, temp); - } - if (auto values = local->item.as()) { - if (values->valueList) { - auto x = local; - auto expList = x->new_ptr(); - for (auto name : values->nameList->names.objects()) { - auto callable = x->new_ptr(); - callable->item.set(name); - auto chainValue = x->new_ptr(); - chainValue->items.push_back(callable); - auto value = x->new_ptr(); - value->item.set(chainValue); - auto exp = newExp(value, x); - expList->exprs.push_back(exp); - } - auto assignment = x->new_ptr(); - assignment->expList.set(expList); - auto assign = x->new_ptr(); - if (auto expListLow = values->valueList.as()) { - assign->values.dup(expListLow->exprs); - } else { - auto tableBlock = values->valueList.to(); - assign->values.push_back(tableBlock); - } - assignment->action.set(assign); - transformAssignment(assignment, temp); - } - } - out.push_back(join(temp)); - } - - void transformLocalAttrib(LocalAttrib_t* localAttrib, str_list& out) { - auto x = localAttrib; - auto attrib = _parser.toString(localAttrib->attrib); - if (attrib != "close"sv && attrib != "const"sv) { - throw std::logic_error(_info.errorMessage(s("unknown attribute '"sv) + attrib + '\'', localAttrib->attrib)); - } - auto expList = x->new_ptr(); - str_list tmpVars; - str_list vars; - pushScope(); - for (auto name : localAttrib->nameList->names.objects()) { - auto callable = x->new_ptr(); - callable->item.set(name); - auto chainValue = x->new_ptr(); - chainValue->items.push_back(callable); - auto value = x->new_ptr(); - value->item.set(chainValue); - auto exp = newExp(value, x); - expList->exprs.push_back(exp); - tmpVars.push_back(getUnusedName("_var_"sv)); - addToScope(tmpVars.back()); - vars.push_back(_parser.toString(name)); - } - popScope(); - auto tmpVarStr = join(tmpVars, ", "sv); - auto tmpVarList = toAst(tmpVarStr, x); - auto assignment = x->new_ptr(); - assignment->expList.set(tmpVarList); - assignment->action.set(localAttrib->assign); - str_list temp; - transformAssignment(assignment, temp); - attrib = s(" <"sv) + attrib + '>'; - for (auto& var : vars) { - forceAddToScope(var); - var.append(attrib); - } - temp.push_back(indent() + s("local "sv) + join(vars, ", "sv) + s(" = "sv) + tmpVarStr + nll(x)); - out.push_back(join(temp)); - } - - void transformBreakLoop(BreakLoop_t* breakLoop, str_list& out) { - auto keyword = _parser.toString(breakLoop); - if (keyword == "break"sv) { - out.push_back(indent() + keyword + nll(breakLoop)); - return; - } - if (_continueVars.empty()) throw std::logic_error(_info.errorMessage("continue is not inside a loop"sv, breakLoop)); - _buf << indent() << _continueVars.top() << " = true"sv << nll(breakLoop); - _buf << indent() << "break"sv << nll(breakLoop); - out.push_back(clearBuf()); - } - - void transformLabel(Label_t* label, str_list& out) { - out.push_back(indent() + s("::"sv) + _parser.toString(label->label) + s("::"sv) + nll(label)); - } - - void transformGoto(Goto_t* gotoNode, str_list& out) { - out.push_back(indent() + s("goto "sv) + _parser.toString(gotoNode->label) + nll(gotoNode)); - } -}; - -const std::string MoonCompilerImpl::Empty; - -MoonCompiler::MoonCompiler(void* sharedState, - const std::function& luaOpen, - bool sameModule): -#ifndef MOONP_NO_MACRO -_compiler(std::make_unique(static_cast(sharedState), luaOpen, sameModule)) {} -#else -_compiler(std::make_unique()) { - (void)sharedState; - (void)luaOpen; - (void)sameModule; -} -#endif // MOONP_NO_MACRO - -MoonCompiler::~MoonCompiler() {} - -CompileInfo MoonCompiler::compile(std::string_view codes, const MoonConfig& config) { - return _compiler->compile(codes, config); -} - -} // namespace MoonP diff --git a/src/MoonP/moon_compiler.h b/src/MoonP/moon_compiler.h deleted file mode 100644 index 5855b2d..0000000 --- a/src/MoonP/moon_compiler.h +++ /dev/null @@ -1,63 +0,0 @@ -/* Copyright (c) 2021 Jin Li, http://www.luvfight.me - -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. */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace MoonP { - -extern const std::string_view version; -extern const std::string_view extension; - -using Options = std::unordered_map; - -struct MoonConfig { - bool lintGlobalVariable = false; - bool implicitReturnRoot = true; - bool reserveLineNumber = true; - bool useSpaceOverTab = false; - int lineOffset = 0; - Options options; -}; - -struct GlobalVar { - std::string name; - int line; - int col; -}; - -using GlobalVars = std::list; - -struct CompileInfo { - std::string codes; - std::string error; - std::unique_ptr globals; - std::unique_ptr options; -}; - -class MoonCompilerImpl; - -class MoonCompiler { -public: - MoonCompiler(void* luaState = nullptr, - const std::function& luaOpen = nullptr, - bool sameModule = false); - virtual ~MoonCompiler(); - CompileInfo compile(std::string_view codes, const MoonConfig& config = {}); -private: - std::unique_ptr _compiler; -}; - -} // namespace MoonP diff --git a/src/MoonP/moon_parser.cpp b/src/MoonP/moon_parser.cpp deleted file mode 100644 index a581e15..0000000 --- a/src/MoonP/moon_parser.cpp +++ /dev/null @@ -1,719 +0,0 @@ -/* Copyright (c) 2021 Jin Li, http://www.luvfight.me - -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. */ - -#include "MoonP/moon_parser.h" - -namespace pl = parserlib; - -namespace MoonP { -using namespace std::string_view_literals; - -std::unordered_set LuaKeywords = { - "and", "break", "do", "else", "elseif", - "end", "false", "for", "function", "goto", - "if", "in", "local", "nil", "not", - "or", "repeat", "return", "then", "true", - "until", "while" -}; - -std::unordered_set Keywords = { - "and", "break", "do", "else", "elseif", - "end", "false", "for", "function", "goto", - "if", "in", "local", "nil", "not", - "or", "repeat", "return", "then", "true", - "until", "while", // Lua keywords - "as", "class", "continue", "export", "extends", - "from", "global", "import", "macro", "switch", - "unless", "using", "when", "with" // Moon keywords -}; - -MoonParser::MoonParser() { - plain_space = *set(" \t"); - Break = nl(-expr('\r') >> '\n'); - Any = Break | any(); - Stop = Break | eof(); - Indent = plain_space; - Comment = "--" >> *(not_(set("\r\n")) >> Any) >> and_(Stop); - multi_line_open = expr("--[["); - multi_line_close = expr("]]"); - multi_line_content = *(not_(multi_line_close) >> Any); - MultiLineComment = multi_line_open >> multi_line_content >> multi_line_close; - EscapeNewLine = expr('\\') >> *(set(" \t") | MultiLineComment) >> -Comment >> Break; - space_one = set(" \t") | and_(set("-\\")) >> (MultiLineComment | EscapeNewLine); - Space = *space_one >> -Comment; - SpaceBreak = Space >> Break; - White = Space >> *(Break >> Space); - EmptyLine = SpaceBreak; - AlphaNum = range('a', 'z') | range('A', 'Z') | range('0', '9') | '_'; - Name = (range('a', 'z') | range('A', 'Z') | '_') >> *AlphaNum; - Num = ( - "0x" >> - +(range('0', '9') | range('a', 'f') | range('A', 'F')) >> - -(-set("uU") >> set("lL") >> set("lL")) - ) | ( - +range('0', '9') >> -set("uU") >> set("lL") >> set("lL") - ) | ( - ( - +range('0', '9') >> -('.' >> +range('0', '9')) - ) | ( - '.' >> +range('0', '9') - ) - ) >> -(set("eE") >> -expr('-') >> +range('0', '9')); - - Cut = false_(); - Seperator = true_(); - - #define sym(str) (Space >> str) - #define symx(str) expr(str) - #define ensure(patt, finally) ((patt) >> (finally) | (finally) >> Cut) - #define key(str) (Space >> str >> not_(AlphaNum)) - #define disable_do(patt) (DisableDo >> ((patt) >> EnableDo | EnableDo >> Cut)) - #define disable_chain(patt) (DisableChain >> ((patt) >> EnableChain | EnableChain >> Cut)) - #define disable_do_chain(patt) (DisableDoChain >> ((patt) >> EnableDoChain | EnableDoChain >> Cut)) - #define plain_body_with(str) (-key(str) >> InBlock | key(str) >> Statement) - #define plain_body (InBlock | Statement) - - Variable = pl::user(Name, [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - for (auto it = item.begin; it != item.end; ++it) st->buffer += static_cast(*it); - auto isValid = Keywords.find(st->buffer) == Keywords.end(); - if (isValid) { - if (st->buffer == st->moduleName) { - st->moduleFix++; - st->moduleName = std::string("_module_"sv) + std::to_string(st->moduleFix); - } - } - st->buffer.clear(); - return isValid; - }); - - LabelName = pl::user(Name, [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - for (auto it = item.begin; it != item.end; ++it) st->buffer += static_cast(*it); - auto isValid = LuaKeywords.find(st->buffer) == LuaKeywords.end(); - st->buffer.clear(); - return isValid; - }); - - LuaKeyword = pl::user(Name, [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - for (auto it = item.begin; it != item.end; ++it) st->buffer += static_cast(*it); - auto it = LuaKeywords.find(st->buffer); - st->buffer.clear(); - return it != LuaKeywords.end(); - }); - - self = expr('@'); - self_name = '@' >> Name; - self_class = expr("@@"); - self_class_name = "@@" >> Name; - - SelfName = self_class_name | self_class | self_name | self; - KeyName = Space >> (SelfName | Name); - VarArg = expr("..."); - - check_indent = pl::user(Indent, [](const item_t& item) { - int indent = 0; - for (input_it i = item.begin; i != item.end; ++i) { - switch (*i) { - case ' ': indent++; break; - case '\t': indent += 4; break; - } - } - State* st = reinterpret_cast(item.user_data); - return st->indents.top() == indent; - }); - CheckIndent = and_(check_indent); - - advance = pl::user(Indent, [](const item_t& item) { - int indent = 0; - for (input_it i = item.begin; i != item.end; ++i) { - switch (*i) { - case ' ': indent++; break; - case '\t': indent += 4; break; - } - } - State* st = reinterpret_cast(item.user_data); - int top = st->indents.top(); - if (top != -1 && indent > top) { - st->indents.push(indent); - return true; - } - return false; - }); - Advance = and_(advance); - - push_indent = pl::user(Indent, [](const item_t& item) { - int indent = 0; - for (input_it i = item.begin; i != item.end; ++i) { - switch (*i) { - case ' ': indent++; break; - case '\t': indent += 4; break; - } - } - State* st = reinterpret_cast(item.user_data); - st->indents.push(indent); - return true; - }); - PushIndent = and_(push_indent); - - PreventIndent = pl::user(true_(), [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - st->indents.push(-1); - return true; - }); - - PopIndent = pl::user(true_(), [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - st->indents.pop(); - return true; - }); - - InBlock = +SpaceBreak >> Advance >> ensure(Block, PopIndent); - - local_flag = expr('*') | expr('^'); - local_values = NameList >> -(sym('=') >> (TableBlock | ExpListLow)); - Local = key("local") >> (Space >> local_flag | local_values); - - LocalAttrib = and_(key(pl::user(Name, [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - for (auto it = item.begin; it != item.end; ++it) st->buffer += static_cast(*it); - auto it = Keywords.find(st->buffer); - st->buffer.clear(); - return it == Keywords.end(); - })) >> NameList >> sym('=') >> not_('=')) >> Space >> Name >> NameList >> Assign; - - colon_import_name = sym('\\') >> Space >> Variable; - ImportName = colon_import_name | Space >> Variable; - ImportNameList = Seperator >> *SpaceBreak >> ImportName >> *((+SpaceBreak | sym(',') >> *SpaceBreak) >> ImportName); - ImportFrom = ImportNameList >> *SpaceBreak >> key("from") >> Exp; - - import_literal_inner = (range('a', 'z') | range('A', 'Z') | set("_-")) >> *(AlphaNum | '-'); - import_literal_chain = Seperator >> import_literal_inner >> *(expr('.') >> import_literal_inner); - ImportLiteral = sym('\'') >> import_literal_chain >> symx('\'') | sym('"') >> import_literal_chain >> symx('"'); - - macro_name_pair = Space >> MacroName >> Space >> symx(':') >> Space >> MacroName; - import_all_macro = expr('$'); - ImportTabItem = variable_pair | normal_pair | sym(':') >> MacroName | macro_name_pair | Space >> import_all_macro; - ImportTabList = ImportTabItem >> *(sym(',') >> ImportTabItem); - ImportTabLine = ( - PushIndent >> (ImportTabList >> PopIndent | PopIndent) - ) | Space; - import_tab_lines = SpaceBreak >> ImportTabLine >> *(-sym(',') >> SpaceBreak >> ImportTabLine) >> -sym(','); - ImportTabLit = - sym('{') >> Seperator >> - -ImportTabList >> - -sym(',') >> - -import_tab_lines >> - White >> sym('}'); - - ImportAs = ImportLiteral >> -(key("as") >> (Space >> Variable | ImportTabLit)); - - Import = key("import") >> (ImportAs | ImportFrom); - - Label = Space >> expr("::") >> LabelName >> expr("::"); - - Goto = key("goto") >> Space >> LabelName; - - BreakLoop = (expr("break") | expr("continue")) >> not_(AlphaNum); - - Return = key("return") >> -ExpListLow; - - WithExp = ExpList >> -Assign; - - With = key("with") >> -existential_op >> disable_do_chain(WithExp) >> plain_body_with("do"); - SwitchCase = key("when") >> disable_chain(ExpList) >> plain_body_with("then"); - SwitchElse = key("else") >> plain_body; - - SwitchBlock = *EmptyLine >> - Advance >> Seperator >> - SwitchCase >> - *(+SpaceBreak >> SwitchCase) >> - -(+SpaceBreak >> SwitchElse) >> - PopIndent; - - Switch = key("switch") >> disable_do(Exp) >> -key("do") - >> -Space >> Break >> SwitchBlock; - - IfCond = disable_chain(Exp >> -Assign); - IfElseIf = -(Break >> *EmptyLine >> CheckIndent) >> key("elseif") >> IfCond >> plain_body_with("then"); - IfElse = -(Break >> *EmptyLine >> CheckIndent) >> key("else") >> plain_body; - If = key("if") >> Seperator >> IfCond >> plain_body_with("then") >> *IfElseIf >> -IfElse; - Unless = key("unless") >> Seperator >> IfCond >> plain_body_with("then") >> *IfElseIf >> -IfElse; - - While = key("while") >> disable_do_chain(Exp) >> plain_body_with("do"); - Repeat = key("repeat") >> Body >> Break >> *EmptyLine >> CheckIndent >> key("until") >> Exp; - - for_step_value = sym(',') >> Exp; - for_args = Space >> Variable >> sym('=') >> Exp >> sym(',') >> Exp >> -for_step_value; - - For = key("for") >> disable_do_chain(for_args) >> plain_body_with("do"); - - for_in = star_exp | ExpList; - - ForEach = key("for") >> AssignableNameList >> key("in") >> - disable_do_chain(for_in) >> plain_body_with("do"); - - Do = pl::user(key("do"), [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - return st->doStack.empty() || st->doStack.top(); - }) >> Body; - - DisableDo = pl::user(true_(), [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - st->doStack.push(false); - return true; - }); - - EnableDo = pl::user(true_(), [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - st->doStack.pop(); - return true; - }); - - DisableDoChain = pl::user(true_(), [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - st->doStack.push(false); - st->chainBlockStack.push(false); - return true; - }); - - EnableDoChain = pl::user(true_(), [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - st->doStack.pop(); - st->chainBlockStack.pop(); - return true; - }); - - Comprehension = sym('[') >> Exp >> CompInner >> sym(']'); - comp_value = sym(',') >> Exp; - TblComprehension = sym('{') >> Exp >> -comp_value >> CompInner >> sym('}'); - - CompInner = Seperator >> (CompForEach | CompFor) >> *CompClause; - star_exp = sym('*') >> Exp; - CompForEach = key("for") >> AssignableNameList >> key("in") >> (star_exp | Exp); - CompFor = key("for") >> Space >> Variable >> sym('=') >> Exp >> sym(',') >> Exp >> -for_step_value; - CompClause = CompFor | CompForEach | key("when") >> Exp; - - Assign = sym('=') >> Seperator >> (With | If | Switch | TableBlock | Exp >> *(Space >> set(",;") >> Exp)); - - update_op = - expr("..") | - expr("+") | - expr("-") | - expr("*") | - expr("/") | - expr("%") | - expr("or") | - expr("and") | - expr("&") | - expr("|") | - expr(">>") | - expr("<<"); - - Update = Space >> update_op >> expr("=") >> Exp; - - Assignable = AssignableChain | Space >> Variable | Space >> SelfName; - - unary_value = unary_operator >> *(Space >> unary_operator) >> Value; - - ExponentialOperator = expr('^'); - expo_value = Space >> ExponentialOperator >> *SpaceBreak >> Value; - expo_exp = Value >> *expo_value; - - unary_operator = - expr('-') >> not_(set(">=") | space_one) | - expr('#') | - expr('~') >> not_(expr('=') | space_one) | - expr("not") >> not_(AlphaNum); - unary_exp = *(Space >> unary_operator) >> expo_exp; - - BackcallOperator = expr("|>"); - backcall_value = Space >> BackcallOperator >> *SpaceBreak >> unary_exp; - backcall_exp = unary_exp >> *backcall_value; - - BinaryOperator = - (expr("or") >> not_(AlphaNum)) | - (expr("and") >> not_(AlphaNum)) | - expr("<=") | - expr(">=") | - expr("~=") | - expr("!=") | - expr("==") | - expr("..") | - expr("<<") | - expr(">>") | - expr("//") | - set("+-*/%><|&~"); - exp_op_value = Space >> BinaryOperator >> *SpaceBreak >> backcall_exp; - Exp = Seperator >> backcall_exp >> *exp_op_value; - - DisableChain = pl::user(true_(), [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - st->chainBlockStack.push(false); - return true; - }); - - EnableChain = pl::user(true_(), [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - st->chainBlockStack.pop(); - return true; - }); - - chain_line = CheckIndent >> (chain_item | Space >> (chain_dot_chain | ColonChain)) >> -InvokeArgs; - chain_block = pl::user(true_(), [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - return st->chainBlockStack.empty() || st->chainBlockStack.top(); - }) >> +SpaceBreak >> Advance >> ensure( - chain_line >> *(+SpaceBreak >> chain_line), PopIndent); - ChainValue = Seperator >> (Chain | Callable) >> -existential_op >> -(InvokeArgs | chain_block); - - simple_table = Seperator >> KeyValue >> *(sym(',') >> KeyValue); - Value = SimpleValue | simple_table | ChainValue | String; - - single_string_inner = expr("\\'") | "\\\\" | not_(expr('\'')) >> Any; - SingleString = symx('\'') >> *single_string_inner >> symx('\''); - interp = symx("#{") >> Exp >> sym('}'); - double_string_plain = expr("\\\"") | "\\\\" | not_(expr('"')) >> Any; - double_string_inner = +(not_(interp) >> double_string_plain); - double_string_content = double_string_inner | interp; - DoubleString = symx('"') >> Seperator >> *double_string_content >> symx('"'); - String = Space >> (DoubleString | SingleString | LuaString); - - lua_string_open = '[' >> *expr('=') >> '['; - lua_string_close = ']' >> *expr('=') >> ']'; - - LuaStringOpen = pl::user(lua_string_open, [](const item_t& item) { - size_t count = std::distance(item.begin, item.end); - State* st = reinterpret_cast(item.user_data); - st->stringOpen = count; - return true; - }); - - LuaStringClose = pl::user(lua_string_close, [](const item_t& item) { - size_t count = std::distance(item.begin, item.end); - State* st = reinterpret_cast(item.user_data); - return st->stringOpen == count; - }); - - LuaStringContent = *(not_(LuaStringClose) >> Any); - - LuaString = LuaStringOpen >> -Break >> LuaStringContent >> LuaStringClose; - - Parens = symx('(') >> *SpaceBreak >> Exp >> *SpaceBreak >> sym(')'); - Callable = Space >> (Variable | SelfName | MacroName | VarArg | Parens); - FnArgsExpList = Exp >> *((Break | sym(',')) >> White >> Exp); - - FnArgs = (symx('(') >> *SpaceBreak >> -FnArgsExpList >> *SpaceBreak >> sym(')')) | - (sym('!') >> not_(expr('='))); - - existential_op = expr('?'); - chain_call = (Callable | String) >> -existential_op >> ChainItems; - chain_item = and_(set(".\\")) >> ChainItems; - chain_dot_chain = DotChainItem >> -existential_op >> -ChainItems; - - Chain = chain_call | chain_item | - Space >> (chain_dot_chain | ColonChain); - - AssignableChain = Seperator >> Chain; - - chain_with_colon = +ChainItem >> -ColonChain; - ChainItems = chain_with_colon | ColonChain; - - Index = symx('[') >> Exp >> sym(']'); - ChainItem = Invoke >> -existential_op | DotChainItem >> -existential_op | Slice | Index >> -existential_op; - DotChainItem = symx('.') >> Name; - ColonChainItem = symx('\\') >> (LuaKeyword | Name); - invoke_chain = Invoke >> -existential_op >> -ChainItems; - ColonChain = ColonChainItem >> -existential_op >> -invoke_chain; - - default_value = true_(); - Slice = - symx('[') >> - (Exp | default_value) >> - sym(',') >> - (Exp | default_value) >> - (sym(',') >> Exp | default_value) >> - sym(']'); - - Invoke = Seperator >> ( - FnArgs | - SingleString | - DoubleString | - and_(expr('[')) >> LuaString | - and_(expr('{')) >> TableLit); - - TableValue = KeyValue | Exp; - - table_lit_lines = SpaceBreak >> TableLitLine >> *(-sym(',') >> SpaceBreak >> TableLitLine) >> -sym(','); - - TableLit = - sym('{') >> Seperator >> - -TableValueList >> - -sym(',') >> - -table_lit_lines >> - White >> sym('}'); - - TableValueList = TableValue >> *(sym(',') >> TableValue); - - TableLitLine = ( - PushIndent >> (TableValueList >> PopIndent | PopIndent) - ) | ( - Space - ); - - TableBlockInner = Seperator >> KeyValueLine >> *(+SpaceBreak >> KeyValueLine); - TableBlock = +SpaceBreak >> Advance >> ensure(TableBlockInner, PopIndent); - TableBlockIndent = sym('*') >> Seperator >> KeyValueList >> -sym(',') >> - -(+SpaceBreak >> Advance >> ensure(KeyValueList >> -sym(',') >> *(+SpaceBreak >> KeyValueLine), PopIndent)); - - class_member_list = Seperator >> KeyValue >> *(sym(',') >> KeyValue); - ClassLine = CheckIndent >> (class_member_list | Statement) >> -sym(','); - ClassBlock = +SpaceBreak >> Advance >> Seperator >> ClassLine >> *(+SpaceBreak >> ClassLine) >> PopIndent; - - ClassDecl = - key("class") >> not_(expr(':')) >> - -Assignable >> - -(key("extends") >> PreventIndent >> ensure(Exp, PopIndent)) >> - -ClassBlock; - - global_values = NameList >> -(sym('=') >> (TableBlock | ExpListLow)); - global_op = expr('*') | expr('^'); - Global = key("global") >> (ClassDecl | (Space >> global_op) | global_values); - - export_default = key("default"); - - Export = pl::user(key("export"), [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - st->exportCount++; - return true; - }) >> ((pl::user(export_default, [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - bool isValid = !st->exportDefault && st->exportCount == 1; - st->exportDefault = true; - return isValid; - }) >> Exp) - | (pl::user(true_(), [](const item_t& item) { - State* st = reinterpret_cast(item.user_data); - if (st->exportDefault && st->exportCount > 1) { - return false; - } else { - return true; - } - }) >> ExpList >> -Assign) - | Macro) >> not_(Space >> statement_appendix); - - variable_pair = sym(':') >> Variable; - - normal_pair = ( - KeyName | - sym('[') >> Exp >> sym(']') | - Space >> DoubleString | - Space >> SingleString | - Space >> LuaString - ) >> - symx(':') >> - (Exp | TableBlock | +(SpaceBreak) >> Exp); - - KeyValue = variable_pair | normal_pair; - - KeyValueList = KeyValue >> *(sym(',') >> KeyValue); - KeyValueLine = CheckIndent >> (KeyValueList >> -sym(',') | TableBlockIndent | Space >> expr('*') >> (Exp | TableBlock)); - - FnArgDef = (Variable | SelfName) >> -(sym('=') >> Space >> Exp); - - FnArgDefList = Space >> Seperator >> ( - ( - FnArgDef >> - *((sym(',') | Break) >> White >> FnArgDef) >> - -((sym(',') | Break) >> White >> VarArg) - ) | ( - VarArg - ) - ); - - outer_var_shadow = key("using") >> (NameList | Space >> expr("nil")); - - FnArgsDef = sym('(') >> White >> -FnArgDefList >> -outer_var_shadow >> White >> sym(')'); - fn_arrow = expr("->") | expr("=>"); - FunLit = -FnArgsDef >> Space >> fn_arrow >> -Body; - - MacroName = expr('$') >> -Name; - macro_args_def = sym('(') >> White >> -FnArgDefList >> White >> sym(')'); - MacroLit = -macro_args_def >> Space >> expr("->") >> Body; - Macro = key("macro") >> Space >> Name >> sym('=') >> MacroLit; - - NameList = Seperator >> Space >> Variable >> *(sym(',') >> Space >> Variable); - NameOrDestructure = Space >> Variable | TableLit; - AssignableNameList = Seperator >> NameOrDestructure >> *(sym(',') >> NameOrDestructure); - - fn_arrow_back = expr('<') >> set("-="); - Backcall = -FnArgsDef >> Space >> fn_arrow_back >> Space >> ChainValue; - - BackcallBody = Seperator >> Space >> BackcallOperator >> unary_exp >> *(+SpaceBreak >> CheckIndent >> Space >> BackcallOperator >> unary_exp); - - ExpList = Seperator >> Exp >> *(sym(',') >> Exp); - ExpListLow = Seperator >> Exp >> *(Space >> set(",;") >> Exp); - - ArgLine = CheckIndent >> Exp >> *(sym(',') >> Exp); - ArgBlock = ArgLine >> *(sym(',') >> SpaceBreak >> ArgLine) >> PopIndent; - - invoke_args_with_table = - sym(',') >> - ( - TableBlock | - SpaceBreak >> Advance >> ArgBlock >> -TableBlock - ); - - InvokeArgs = - not_(set("-~")) >> Seperator >> - ( - Exp >> *(sym(',') >> Exp) >> -(invoke_args_with_table | TableBlock) | - TableBlock - ); - - const_value = (expr("nil") | expr("true") | expr("false")) >> not_(AlphaNum); - - SimpleValue = - (Space >> const_value) | - If | Unless | Switch | With | ClassDecl | ForEach | For | While | Do | - (Space >> unary_value) | - TblComprehension | TableLit | Comprehension | FunLit | - (Space >> Num); - - ExpListAssign = ExpList >> -(Update | Assign); - - if_line = key("if") >> Exp >> -Assign; - unless_line = key("unless") >> Exp; - - statement_appendix = (if_line | unless_line | CompInner) >> Space; - statement_sep = and_(*SpaceBreak >> CheckIndent >> Space >> (set("($'\"") | expr("[[") | expr("[="))); - Statement = ( - Import | While | Repeat | For | ForEach | - Return | Local | Global | Export | Macro | - Space >> BreakLoop | Label | Goto | Backcall | - LocalAttrib | BackcallBody | ExpListAssign - ) >> Space >> - -statement_appendix >> -statement_sep; - - Body = InBlock | Statement; - - empty_line_stop = Space >> and_(Stop); - Line = and_(check_indent >> Space >> not_(BackcallOperator)) >> Statement | Advance >> ensure(and_(Space >> BackcallOperator) >> Statement, PopIndent) | empty_line_stop; - Block = Seperator >> Line >> *(+Break >> Line); - - Shebang = expr("#!") >> *(not_(Stop) >> Any); - File = White >> -Shebang >> Block >> eof(); -} - -ParseInfo MoonParser::parse(std::string_view codes, rule& r) { - ParseInfo res; - try { - res.codes = std::make_unique(); - *(res.codes) = _converter.from_bytes(&codes.front(), &codes.back() + 1); - } catch (const std::range_error&) { - res.error = "Invalid text encoding."sv; - return res; - } - error_list errors; - try { - State state; - res.node.set(pl::parse(*(res.codes), r, errors, &state)); - if (state.exportCount > 0) { - res.moduleName = std::move(state.moduleName); - res.exportDefault = state.exportDefault; - } - } catch (const std::logic_error& err) { - res.error = err.what(); - return res; - } - if (!errors.empty()) { - std::ostringstream buf; - for (error_list::iterator it = errors.begin(); it != errors.end(); ++it) { - const error& err = *it; - switch (err.m_type) { - case ERROR_TYPE::ERROR_SYNTAX_ERROR: - buf << res.errorMessage("syntax error"sv, &err); - break; - case ERROR_TYPE::ERROR_INVALID_EOF: - buf << res.errorMessage("invalid EOF"sv, &err); - break; - } - } - res.error = buf.str(); - } - return res; -} - -std::string MoonParser::toString(ast_node* node) { - return _converter.to_bytes(std::wstring(node->m_begin.m_it, node->m_end.m_it)); -} - -std::string MoonParser::toString(input::iterator begin, input::iterator end) { - return _converter.to_bytes(std::wstring(begin, end)); -} - -input MoonParser::encode(std::string_view codes) { - return _converter.from_bytes(&codes.front(), &codes.back() + 1); -} - -std::string MoonParser::decode(const input& codes) { - return _converter.to_bytes(codes); -} - -namespace Utils { - void replace(std::string& str, std::string_view from, std::string_view to) { - size_t start_pos = 0; - while((start_pos = str.find(from, start_pos)) != std::string::npos) { - str.replace(start_pos, from.size(), to); - start_pos += to.size(); - } - } - - void trim(std::string& str) { - if (str.empty()) return; - str.erase(0, str.find_first_not_of(" \t\n")); - str.erase(str.find_last_not_of(" \t\n") + 1); - } -} - -std::string ParseInfo::errorMessage(std::string_view msg, const input_range* loc) const { - const int ASCII = 255; - int length = loc->m_begin.m_line; - auto begin = codes->begin(); - auto end = codes->end(); - int count = 0; - for (auto it = codes->begin(); it != codes->end(); ++it) { - if (*it == '\n') { - if (count + 1 == length) { - end = it; - break; - } else { - begin = it + 1; - } - count++; - } - } - int oldCol = loc->m_begin.m_col; - int col = std::max(0, oldCol - 1); - auto it = begin; - for (int i = 0; i < oldCol && it != end; ++i) { - if (*it > ASCII) { - ++col; - } - ++it; - } - auto line = Converter{}.to_bytes(std::wstring(begin, end)); - Utils::replace(line, "\t"sv, " "sv); - std::ostringstream buf; - buf << loc->m_begin.m_line << ": "sv << msg << - '\n' << line << '\n' << std::string(col, ' ') << "^"sv; - return buf.str(); -} - -} // namespace MoonP diff --git a/src/MoonP/moon_parser.h b/src/MoonP/moon_parser.h deleted file mode 100644 index b165070..0000000 --- a/src/MoonP/moon_parser.h +++ /dev/null @@ -1,317 +0,0 @@ -/* Copyright (c) 2021 Jin Li, http://www.luvfight.me - -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. */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "MoonP/ast.hpp" -#include "MoonP/moon_ast.h" - -namespace MoonP { -using namespace parserlib; - -struct ParseInfo { - ast_ptr node; - std::string error; - std::unique_ptr codes; - bool exportDefault = false; - std::string moduleName; - std::string errorMessage(std::string_view msg, const input_range* loc) const; -}; - -template -struct identity { typedef T type; }; - -#define AST_RULE(type) \ - rule type; \ - ast type##_impl = type; \ - inline rule& getRule(identity) { return type; } - -extern std::unordered_set LuaKeywords; -extern std::unordered_set Keywords; - -class MoonParser { -public: - MoonParser(); - - template - ParseInfo parse(std::string_view codes) { - return parse(codes, getRule()); - } - - template - bool match(std::string_view codes) { - auto rEnd = rule(getRule() >> eof()); - return parse(codes, rEnd).node; - } - - std::string toString(ast_node* node); - std::string toString(input::iterator begin, input::iterator end); - - input encode(std::string_view input); - std::string decode(const input& input); - -protected: - ParseInfo parse(std::string_view codes, rule& r); - - struct State { - State() { - indents.push(0); - } - bool exportDefault = false; - int exportCount = 0; - int moduleFix = 0; - size_t stringOpen = 0; - std::string moduleName = "_module_0"; - std::string buffer; - std::stack indents; - std::stack doStack; - std::stack chainBlockStack; - }; - - template - inline rule& getRule() { - return getRule(identity()); - } - -private: - Converter _converter; - - template - inline rule& getRule(identity) { - assert(false); - return Cut; - } - - rule plain_space; - rule Break; - rule Any; - rule White; - rule Stop; - rule Comment; - rule multi_line_open; - rule multi_line_close; - rule multi_line_content; - rule MultiLineComment; - rule Indent; - rule EscapeNewLine; - rule space_one; - rule Space; - rule SpaceBreak; - rule EmptyLine; - rule AlphaNum; - rule Cut; - rule check_indent; - rule CheckIndent; - rule advance; - rule Advance; - rule push_indent; - rule PushIndent; - rule PreventIndent; - rule PopIndent; - rule InBlock; - rule ImportName; - rule ImportNameList; - rule import_literal_chain; - rule ImportTabItem; - rule ImportTabList; - rule ImportTabLine; - rule import_tab_lines; - rule WithExp; - rule DisableDo; - rule EnableDo; - rule DisableChain; - rule EnableChain; - rule DisableDoChain; - rule EnableDoChain; - rule SwitchElse; - rule SwitchBlock; - rule IfElseIf; - rule IfElse; - rule for_args; - rule for_in; - rule CompClause; - rule Chain; - rule KeyValue; - rule single_string_inner; - rule interp; - rule double_string_plain; - rule lua_string_open; - rule lua_string_close; - rule FnArgsExpList; - rule FnArgs; - rule macro_args_def; - rule chain_call; - rule chain_item; - rule ChainItems; - rule chain_dot_chain; - rule ColonChain; - rule chain_with_colon; - rule ChainItem; - rule chain_line; - rule chain_block; - rule Index; - rule invoke_chain; - rule TableValue; - rule table_lit_lines; - rule TableLitLine; - rule TableValueList; - rule TableBlockInner; - rule ClassLine; - rule KeyValueLine; - rule KeyValueList; - rule ArgLine; - rule ArgBlock; - rule invoke_args_with_table; - rule BackcallOperator; - rule ExponentialOperator; - rule backcall_value; - rule backcall_exp; - rule expo_value; - rule expo_exp; - rule empty_line_stop; - rule Line; - rule Shebang; - - AST_RULE(Num) - AST_RULE(Name) - AST_RULE(Variable) - AST_RULE(LabelName) - AST_RULE(LuaKeyword) - AST_RULE(self) - AST_RULE(self_name) - AST_RULE(self_class) - AST_RULE(self_class_name) - AST_RULE(SelfName) - AST_RULE(KeyName) - AST_RULE(VarArg) - AST_RULE(Seperator) - AST_RULE(NameList) - AST_RULE(local_flag) - AST_RULE(local_values) - AST_RULE(Local) - AST_RULE(LocalAttrib); - AST_RULE(colon_import_name) - AST_RULE(import_literal_inner) - AST_RULE(ImportLiteral) - AST_RULE(ImportFrom) - AST_RULE(macro_name_pair) - AST_RULE(import_all_macro) - AST_RULE(ImportTabLit) - AST_RULE(ImportAs) - AST_RULE(Import) - AST_RULE(Label) - AST_RULE(Goto) - AST_RULE(fn_arrow_back) - AST_RULE(Backcall) - AST_RULE(BackcallBody) - AST_RULE(ExpListLow) - AST_RULE(ExpList) - AST_RULE(Return) - AST_RULE(With) - AST_RULE(SwitchCase) - AST_RULE(Switch) - AST_RULE(IfCond) - AST_RULE(If) - AST_RULE(Unless) - AST_RULE(While) - AST_RULE(Repeat) - AST_RULE(for_step_value) - AST_RULE(For) - AST_RULE(ForEach) - AST_RULE(Do) - AST_RULE(Comprehension) - AST_RULE(comp_value) - AST_RULE(TblComprehension) - AST_RULE(star_exp) - AST_RULE(CompForEach) - AST_RULE(CompFor) - AST_RULE(CompInner) - AST_RULE(Assign) - AST_RULE(update_op) - AST_RULE(Update) - AST_RULE(BinaryOperator) - AST_RULE(unary_operator) - AST_RULE(Assignable) - AST_RULE(AssignableChain) - AST_RULE(exp_op_value) - AST_RULE(Exp) - AST_RULE(Callable) - AST_RULE(ChainValue) - AST_RULE(simple_table) - AST_RULE(SimpleValue) - AST_RULE(Value) - AST_RULE(LuaStringOpen); - AST_RULE(LuaStringContent); - AST_RULE(LuaStringClose); - AST_RULE(LuaString) - AST_RULE(SingleString) - AST_RULE(double_string_inner) - AST_RULE(double_string_content) - AST_RULE(DoubleString) - AST_RULE(String) - AST_RULE(Parens) - AST_RULE(DotChainItem) - AST_RULE(ColonChainItem) - AST_RULE(default_value) - AST_RULE(Slice) - AST_RULE(Invoke) - AST_RULE(existential_op) - AST_RULE(TableLit) - AST_RULE(TableBlock) - AST_RULE(TableBlockIndent) - AST_RULE(class_member_list) - AST_RULE(ClassBlock) - AST_RULE(ClassDecl) - AST_RULE(global_values) - AST_RULE(global_op) - AST_RULE(Global) - AST_RULE(export_default) - AST_RULE(Export) - AST_RULE(variable_pair) - AST_RULE(normal_pair) - AST_RULE(FnArgDef) - AST_RULE(FnArgDefList) - AST_RULE(outer_var_shadow) - AST_RULE(FnArgsDef) - AST_RULE(fn_arrow) - AST_RULE(FunLit) - AST_RULE(MacroName) - AST_RULE(MacroLit) - AST_RULE(Macro) - AST_RULE(NameOrDestructure) - AST_RULE(AssignableNameList) - AST_RULE(InvokeArgs) - AST_RULE(const_value) - AST_RULE(unary_value) - AST_RULE(unary_exp) - AST_RULE(ExpListAssign) - AST_RULE(if_line) - AST_RULE(unless_line) - AST_RULE(BreakLoop) - AST_RULE(statement_appendix) - AST_RULE(statement_sep) - AST_RULE(Statement) - AST_RULE(Body) - AST_RULE(Block) - AST_RULE(File) -}; - -namespace Utils { - void replace(std::string& str, std::string_view from, std::string_view to); - void trim(std::string& str); -}; - -} // namespace MoonP diff --git a/src/MoonP/moonplus.cpp b/src/MoonP/moonplus.cpp deleted file mode 100644 index 9b9689c..0000000 --- a/src/MoonP/moonplus.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* Copyright (c) 2021 Jin Li, http://www.luvfight.me - -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. */ -#include "MoonP/moon_compiler.h" - -extern "C" { - -#include "lua.h" -#include "lauxlib.h" - -static const char moonplusCodes[] = -#include "MoonP/moonplus.h" - -static void init_moonplus(lua_State* L) { - if (luaL_loadbuffer(L, moonplusCodes, sizeof(moonplusCodes) / sizeof(moonplusCodes[0]) - 1, "=(moonplus)") != 0) { - std::string err = std::string("failed to load moonplus module.\n") + lua_tostring(L, -1); - luaL_error(L, err.c_str()); - } else if (lua_pcall(L, 0, 0, 0) != 0) { - std::string err = std::string("failed to init moonplus module.\n") + lua_tostring(L, -1); - luaL_error(L, err.c_str()); - } -} - -static const char stpCodes[] = -#include "MoonP/stacktraceplus.h" - -static int init_stacktraceplus(lua_State* L) { - if (luaL_loadbuffer(L, stpCodes, sizeof(stpCodes) / sizeof(stpCodes[0]) - 1, "=(stacktraceplus)") != 0) { - std::string err = std::string("failed to load stacktraceplus module.\n") + lua_tostring(L, -1); - luaL_error(L, err.c_str()); - } else if (lua_pcall(L, 0, 1, 0) != 0) { - std::string err = std::string("failed to init stacktraceplus module.\n") + lua_tostring(L, -1); - luaL_error(L, err.c_str()); - } - return 1; -} - -static int moontolua(lua_State* L) { - size_t size = 0; - const char* input = luaL_checklstring(L, 1, &size); - MoonP::MoonConfig config; - bool sameModule = false; - if (lua_gettop(L) == 2) { - luaL_checktype(L, 2, LUA_TTABLE); - lua_pushliteral(L, "lint_global"); - lua_gettable(L, -2); - if (lua_isboolean(L, -1) != 0) { - config.lintGlobalVariable = lua_toboolean(L, -1) != 0; - } - lua_pop(L, 1); - lua_pushliteral(L, "implicit_return_root"); - lua_gettable(L, -2); - if (lua_isboolean(L, -1) != 0) { - config.implicitReturnRoot = lua_toboolean(L, -1) != 0; - } - lua_pop(L, 1); - lua_pushliteral(L, "reserve_line_number"); - lua_gettable(L, -2); - if (lua_isboolean(L, -1) != 0) { - config.reserveLineNumber = lua_toboolean(L, -1) != 0; - } - lua_pop(L, 1); - lua_pushliteral(L, "space_over_tab"); - lua_gettable(L, -2); - if (lua_isboolean(L, -1) != 0) { - config.useSpaceOverTab = lua_toboolean(L, -1) != 0; - } - lua_pop(L, 1); - lua_pushliteral(L, "same_module"); - lua_gettable(L, -2); - if (lua_isboolean(L, -1) != 0) { - sameModule = lua_toboolean(L, -1) != 0; - } - lua_pop(L, 1); - lua_pushliteral(L, "line_offset"); - lua_gettable(L, -2); - if (lua_isnumber(L, -1) != 0) { - config.lineOffset = static_cast(lua_tonumber(L, -1)); - } - lua_pop(L, 1); - } - std::string s(input, size); - auto result = MoonP::MoonCompiler(L, nullptr, sameModule).compile(s, config); - if (result.codes.empty() && !result.error.empty()) { - lua_pushnil(L); - } else { - lua_pushlstring(L, result.codes.c_str(), result.codes.size()); - } - if (result.error.empty()) { - lua_pushnil(L); - } else { - lua_pushlstring(L, result.error.c_str(), result.error.size()); - } - if (result.globals) { - lua_createtable(L, static_cast(result.globals->size()), 0); - int i = 1; - for (const auto& var : *result.globals) { - lua_createtable(L, 3, 0); - lua_pushlstring(L, var.name.c_str(), var.name.size()); - lua_rawseti(L, -2, 1); - lua_pushinteger(L, var.line); - lua_rawseti(L, -2, 2); - lua_pushinteger(L, var.col); - lua_rawseti(L, -2, 3); - lua_rawseti(L, -2, i); - i++; - } - } else { - lua_pushnil(L); - } - return 3; -} - -int luaopen_moonp(lua_State* L) { - lua_getglobal(L, "package"); // package - lua_getfield(L, -1, "loaded"); // package loaded - lua_createtable(L, 0, 0); // package loaded moonp - lua_pushcfunction(L, moontolua); // package loaded moonp func - lua_setfield(L, -2, "to_lua"); // moonp["to_lua"] = func, package loaded moonp - lua_pushlstring(L, &MoonP::version.front(), MoonP::version.size()); // package loaded moonp version - lua_setfield(L, -2, "version"); // moonp["version"] = version, package loaded moonp - lua_createtable(L, 0, 0); // package loaded moonp options - lua_pushlstring(L, &MoonP::extension.front(), MoonP::extension.size()); // package loaded moonp options ext - lua_setfield(L, -2, "extension"); // options["extension"] = ext, package loaded moonp options - lua_setfield(L, -2, "options"); // moonp["options"] = options, package loaded moonp - lua_pushcfunction(L, init_stacktraceplus); // package loaded moonp func1 - lua_setfield(L, -2, "load_stacktraceplus"); // moonp["load_stacktraceplus"] = func1, package loaded moonp - lua_setfield(L, -2, "moonp"); // loaded["moonp"] = moonp, package loaded - lua_pop(L, 2); // empty - init_moonplus(L); - return 0; -} - -} // extern "C" - diff --git a/src/MoonP/moonplus.h b/src/MoonP/moonplus.h deleted file mode 100644 index ff82e9f..0000000 --- a/src/MoonP/moonplus.h +++ /dev/null @@ -1,251 +0,0 @@ -R"moonplus_codes( ---[[ -Copyright (C) 2020 by Leaf Corcoran, modified by Li Jin - -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.]] - -local moonp = require("moonp") -local concat, insert, remove = table.concat, table.insert, table.remove -local unpack = unpack or table.unpack -local lua = { - loadstring = loadstring, - load = load -} -local split, get_options, create_moonpath, moon_loader, load_text, moon_call, loadstring, loadfile, dofile, insert_loader, remove_loader, moon_require, find_modulepath -moonp.dirsep = "/" -moonp.moon_compiled = { } -moonp.file_exist = function(fname) - local file = io.open(fname) - if file then - file:close() - return true - else - return false - end -end -moonp.read_file = 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 text -end -split = function(str, delim) - if str == "" then - return { } - end - str = str .. delim - 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 -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 -create_moonpath = function(package_path) - local extension = moonp.options.extension - local moonpaths - do - local _accum_0 = { } - local _len_0 = 1 - local _list_0 = split(package_path, ";") - for _index_0 = 1, #_list_0 do - local path = _list_0[_index_0] - local _continue_0 = false - repeat - local prefix = path:match("^(.-)%.lua$") - if not prefix then - _continue_0 = true - break - end - _accum_0[_len_0] = prefix .. "." .. extension - _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 -find_modulepath = function(name) - if not package.moonpath then - package.moonpath = create_moonpath(package.path) - end - local name_path = name:gsub("%.", moonp.dirsep) - local file_exist, file_path - for path in package.moonpath:gmatch("[^;]+") do - file_path = path:gsub("?", name_path) - file_exist = moonp.file_exist(file_path) - if file_exist then - break - end - end - if file_exist then - return file_path - else - return nil - end -end -load_text = function(name) - local file_path = find_modulepath(name) - if file_path then - return moonp.read_file(file_path), file_path - end - return nil, nil -end -moon_loader = function(name) - local text, file_path = load_text(name) - if text then - local res, err = loadstring(text, file_path) - if not res then - error(file_path .. ": " .. err) - end - return res - end - return nil, "Could not find moonp file" -end -moon_call = function(f, ...) - local args = { - ... - } - return xpcall((function() - return f(unpack(args)) - end), function(err) - return moonp.stp.stacktrace(err, 1) - end) -end -loadstring = function(...) - local options, str, chunk_name, mode, env = get_options(...) - chunk_name = chunk_name or "=(moonplus.loadstring)" - local code, err = moonp.to_lua(str, options) - if not code then - return nil, err - end - if chunk_name then - moonp.moon_compiled["@" .. chunk_name] = code - end - return (lua.loadstring or lua.load)(code, chunk_name, unpack({ - mode, - env - })) -end -loadfile = function(fname, ...) - local text = moonp.read_file(fname) - 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 -moon_require = function(name) - insert_loader() - local success, res = xpcall((function() - return require(name) - end), function(err) - local msg = moonp.stp.stacktrace(err, 1) - print(msg) - return msg - end) - if success then - return res - else - return nil - end -end -setmetatable(moonp, { - __index = function(self, key) - if not (key == "stp") then - return nil - end - local stp = rawget(moonp, "stp") - if not stp then - do - local _with_0 = moonp.load_stacktraceplus() - _with_0.dump_locals = false - _with_0.simplified = true - stp = _with_0 - end - rawset(moonp, "stp", stp) - rawset(moonp, "load_stacktraceplus", nil) - end - return stp - end, - __call = function(self, name) - return self.require(name) - end -}) -for k, v in pairs({ - insert_loader = insert_loader, - remove_loader = remove_loader, - loader = moon_loader, - dofile = dofile, - loadfile = loadfile, - loadstring = loadstring, - create_moonpath = create_moonpath, - find_modulepath = find_modulepath, - pcall = moon_call, - require = moon_require -}) do - moonp[k] = v -end -)moonplus_codes"; diff --git a/src/MoonP/parser.cpp b/src/MoonP/parser.cpp deleted file mode 100644 index 8dc2ff9..0000000 --- a/src/MoonP/parser.cpp +++ /dev/null @@ -1,1412 +0,0 @@ -/* Copyright (c) 2012, Achilleas Margaritis, modified by Jin Li -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/ - -#include -#include -#include -#include -#include -#include - -#include "MoonP/parser.hpp" - - -namespace parserlib { - - -//internal private class that manages access to the public classes' internals. -class _private { -public: - //get the internal expression object from the expression. - static _expr* get_expr(const expr& e) { - return e.m_expr; - } - - //create new expression from given expression - static expr construct_expr(_expr* e) { - return e; - } - - //get the internal expression object from the rule. - static _expr* get_expr(rule& r) { - return r.m_expr; - } - - //get the internal parse proc from the rule. - static parse_proc get_parse_proc(rule& r) { - return r.m_parse_proc; - } -}; - - -class _context; - - -//parser state -class _state { -public: - //position - pos m_pos; - - //size of match vector - size_t m_matches; - - //constructor - _state(_context& con); -}; - - -//match -class _match { -public: - //rule matched - rule* m_rule; - - //begin position - pos m_begin; - - //end position - pos m_end; - - //null constructor - _match() {} - - //constructor from parameters - _match(rule* r, const pos& b, const pos& e) : - m_rule(r), - m_begin(b), - m_end(e) - { - } -}; - - -//match vector -typedef std::vector<_match> _match_vector; - - -//parsing context -class _context { -public: - //user data - void* m_user_data; - - //current position - pos m_pos; - - //error position - pos m_error_pos; - - //input begin - input::iterator m_begin; - - //input end - input::iterator m_end; - - //matches - _match_vector m_matches; - - //constructor - _context(input& i, void* ud) : - m_user_data(ud), - m_pos(i), - m_error_pos(i), - m_begin(i.begin()), - m_end(i.end()) - { - } - - //check if the end is reached - bool end() const { - return m_pos.m_it == m_end; - } - - //get the current symbol - input::value_type symbol() const { - assert(!end()); - return *m_pos.m_it; - } - - //set the longest possible error - void set_error_pos() { - if (m_pos.m_it > m_error_pos.m_it) { - m_error_pos = m_pos; - } - } - - //next column - void next_col() { - ++m_pos.m_it; - ++m_pos.m_col; - } - - //next line - void next_line() { - ++m_pos.m_line; - m_pos.m_col = 1; - } - - //restore the state - void restore(const _state& st) { - m_pos = st.m_pos; - m_matches.resize(st.m_matches); - } - - //parse non-term rule. - bool parse_non_term(rule& r); - - //parse term rule. - bool parse_term(rule& r); - - //execute all the parse procs - void do_parse_procs(void* d) const { - for(_match_vector::const_iterator it = m_matches.begin(); - it != m_matches.end(); - ++it) { - const _match &m = *it; - parse_proc p = _private::get_parse_proc(*m.m_rule); - p(m.m_begin, m.m_end, d); - } - } - -private: - //parse non-term rule. - bool _parse_non_term(rule& r); - - //parse term rule. - bool _parse_term(rule& r); -}; - - -//base class for expressions -class _expr { -public: - //destructor. - virtual ~_expr() { - } - - //parse with whitespace - virtual bool parse_non_term(_context& con) const = 0; - - //parse terminal - virtual bool parse_term(_context& con) const = 0; -}; - - -//single character expression. -class _char : public _expr { -public: - //constructor. - _char(char c) : - m_char(c) - { - } - - //parse with whitespace - virtual bool parse_non_term(_context& con) const { - return _parse(con); - } - - //parse terminal - virtual bool parse_term(_context& con) const { - return _parse(con); - } - -private: - //character - input::value_type m_char; - - //internal parse - bool _parse(_context& con) const { - if (!con.end()) { - input::value_type ch = con.symbol(); - if (ch == m_char) { - con.next_col(); - return true; - } - } - con.set_error_pos(); - return false; - } -}; - - -//string expression. -class _string : public _expr { -public: - //constructor from ansi string. - _string(const char* s) : - m_string(Converter{}.from_bytes(s)) - { - } - - //parse with whitespace - virtual bool parse_non_term(_context& con) const { - return _parse(con); - } - - //parse terminal - virtual bool parse_term(_context& con) const { - return _parse(con); - } - -private: - //string - input m_string; - - //parse the string - bool _parse(_context& con) const { - for(input::const_iterator it = m_string.begin(), - end = m_string.end();;) { - if (it == end) return true; - if (con.end()) break; - if (con.symbol() != *it) break; - ++it; - con.next_col(); - } - con.set_error_pos(); - return false; - } -}; - - -//set expression. -class _set : public _expr { -public: - //constructor from ansi string. - _set(const char* s) { - auto str = Converter{}.from_bytes(s); - for (auto ch : str) { - _add(ch); - } - } - - //constructor from range. - _set(int min, int max) { - assert(min >= 0); - assert(min <= max); - m_quick_set.resize((size_t)max + 1U); - for(; min <= max; ++min) { - m_quick_set[(size_t)min] = true; - } - } - - //parse with whitespace - virtual bool parse_non_term(_context& con) const { - return _parse(con); - } - - //parse terminal - virtual bool parse_term(_context& con) const { - return _parse(con); - } - -private: - //set is kept as an array of flags, for quick access - std::vector m_quick_set; - std::unordered_set m_large_set; - - //add character - void _add(size_t i) { - if (i <= m_quick_set.size() || i <= 255) { - if (i >= m_quick_set.size()) { - m_quick_set.resize(i + 1); - } - m_quick_set[i] = true; - } else { - m_large_set.insert(i); - } - } - - //internal parse - bool _parse(_context& con) const { - if (!con.end()) { - size_t ch = con.symbol(); - if (ch < m_quick_set.size()) { - if (m_quick_set[ch]) { - con.next_col(); - return true; - } - } else if (m_large_set.find(ch) != m_large_set.end()) { - con.next_col(); - return true; - } - } - con.set_error_pos(); - return false; - } -}; - - -//base class for unary expressions -class _unary : public _expr { -public: - //constructor. - _unary(_expr* e) : - m_expr(e) - { - } - - //destructor. - virtual ~_unary() { - delete m_expr; - } - -protected: - //expression - _expr *m_expr; -}; - - -//terminal -class _term : public _unary { -public: - //constructor. - _term(_expr* e) : - _unary(e) - { - } - - //parse with whitespace - virtual bool parse_non_term(_context& con) const { - return m_expr->parse_term(con); - } - - //parse terminal - virtual bool parse_term(_context& con) const { - return m_expr->parse_term(con); - } -}; - - -//user -class _user : public _unary { -public: - //constructor. - _user(_expr* e, const user_handler& callback) : - _unary(e), - m_handler(callback) - { - } - - //parse with whitespace - virtual bool parse_non_term(_context& con) const { - pos pos = con.m_pos; - if (m_expr->parse_non_term(con)) { - item_t item = {pos.m_it, con.m_pos.m_it, con.m_user_data}; - return m_handler(item); - } - return false; - } - - //parse terminal - virtual bool parse_term(_context& con) const { - pos pos = con.m_pos; - if (m_expr->parse_term(con)) { - item_t item = {pos.m_it, con.m_pos.m_it, con.m_user_data}; - return m_handler(item); - } - return false; - } -private: - user_handler m_handler; -}; - - -//loop 0 -class _loop0 : public _unary { -public: - //constructor. - _loop0(_expr* e) : - _unary(e) - { - } - - //parse with whitespace - virtual bool parse_non_term(_context& con) const { - //if parsing of the first fails, restore the context and stop - _state st(con); - if (!m_expr->parse_non_term(con)) { - con.restore(st); - return true; - } - - //parse the rest - for(;;) { - _state st(con); - if (!m_expr->parse_non_term(con)) { - con.restore(st); - break; - } - } - - return true; - } - - //parse terminal - virtual bool parse_term(_context& con) const { - //if parsing of the first fails, restore the context and stop - _state st(con); - if (!m_expr->parse_term(con)) { - con.restore(st); - return true; - } - - //parse the rest until no more parsing is possible - for(;;) { - _state st(con); - if (!m_expr->parse_term(con)) { - con.restore(st); - break; - } - } - - return true; - } -}; - - -//loop 1 -class _loop1 : public _unary { -public: - //constructor. - _loop1(_expr* e) : - _unary(e) - { - } - - //parse with whitespace - virtual bool parse_non_term(_context& con) const { - //parse the first; if the first fails, stop - if (!m_expr->parse_non_term(con)) return false; - - //parse the rest until no more parsing is possible - for(;;) { - _state st(con); - if (!m_expr->parse_non_term(con)) { - con.restore(st); - break; - } - } - - return true; - } - - //parse terminal - virtual bool parse_term(_context& con) const { - //parse the first; if the first fails, stop - if (!m_expr->parse_term(con)) return false; - - //parse the rest until no more parsing is possible - for(;;) { - _state st(con); - if (!m_expr->parse_term(con)) { - con.restore(st); - break; - } - } - - return true; - } -}; - - -//optional -class _optional : public _unary { -public: - //constructor. - _optional(_expr* e) : - _unary(e) - { - } - - //parse with whitespace - virtual bool parse_non_term(_context& con) const { - _state st(con); - if (!m_expr->parse_non_term(con)) con.restore(st); - return true; - } - - //parse terminal - virtual bool parse_term(_context& con) const { - _state st(con); - if (!m_expr->parse_term(con)) con.restore(st); - return true; - } -}; - - -//and -class _and : public _unary { -public: - //constructor. - _and(_expr* e) : - _unary(e) - { - } - - //parse with whitespace - virtual bool parse_non_term(_context& con) const { - _state st(con); - bool ok = m_expr->parse_non_term(con); - con.restore(st); - return ok; - } - - //parse terminal - virtual bool parse_term(_context& con) const { - _state st(con); - bool ok = m_expr->parse_term(con); - con.restore(st); - return ok; - } -}; - - -//not -class _not : public _unary { -public: - //constructor. - _not(_expr* e) : - _unary(e) - { - } - - //parse with whitespace - virtual bool parse_non_term(_context& con) const { - _state st(con); - bool ok = !m_expr->parse_non_term(con); - con.restore(st); - return ok; - } - - //parse terminal - virtual bool parse_term(_context& con) const { - _state st(con); - bool ok = !m_expr->parse_term(con); - con.restore(st); - return ok; - } -}; - - -//newline -class _nl : public _unary { -public: - //constructor. - _nl(_expr* e) : - _unary(e) - { - } - - //parse with whitespace - virtual bool parse_non_term(_context& con) const { - if (!m_expr->parse_non_term(con)) return false; - con.next_line(); - return true; - } - - //parse terminal - virtual bool parse_term(_context& con) const { - if (!m_expr->parse_term(con)) return false; - con.next_line(); - return true; - } -}; - - -//base class for binary expressions -class _binary : public _expr { -public: - //constructor. - _binary(_expr* left, _expr* right) : - m_left(left), m_right(right) - { - } - - //destructor. - virtual ~_binary() { - delete m_left; - delete m_right; - } - -protected: - //left and right expressions - _expr* m_left, *m_right; -}; - - -//sequence -class _seq : public _binary { -public: - //constructor. - _seq(_expr* left, _expr* right) : - _binary(left, right) - { - } - - //parse with whitespace - virtual bool parse_non_term(_context& con) const { - if (!m_left->parse_non_term(con)) return false; - return m_right->parse_non_term(con); - } - - //parse terminal - virtual bool parse_term(_context& con) const { - if (!m_left->parse_term(con)) return false; - return m_right->parse_term(con); - } -}; - - -//choice -class _choice : public _binary { -public: - //constructor. - _choice(_expr* left, _expr* right) : - _binary(left, right) - { - } - - //parse with whitespace - virtual bool parse_non_term(_context& con) const { - _state st(con); - if (m_left->parse_non_term(con)) return true; - con.restore(st); - return m_right->parse_non_term(con); - } - - //parse terminal - virtual bool parse_term(_context& con) const { - _state st(con); - if (m_left->parse_term(con)) return true; - con.restore(st); - return m_right->parse_term(con); - } -}; - - -//reference to rule -class _ref : public _expr { -public: - //constructor. - _ref(rule& r) : - m_rule(r) - { - } - - //parse with whitespace - virtual bool parse_non_term(_context& con) const { - return con.parse_non_term(m_rule); - } - - //parse terminal - virtual bool parse_term(_context& con) const { - return con.parse_term(m_rule); - } - -private: - //reference - rule &m_rule; -}; - - -//eof -class _eof : public _expr { -public: - //parse with whitespace - virtual bool parse_non_term(_context& con) const { - return parse_term(con); - } - - //parse terminal - virtual bool parse_term(_context& con) const { - return con.end(); - } -}; - - -//any -class _any : public _expr { -public: - //parse with whitespace - virtual bool parse_non_term(_context& con) const { - return parse_term(con); - } - - //parse terminal - virtual bool parse_term(_context& con) const { - if (!con.end()) { - con.next_col(); - return true; - } - con.set_error_pos(); - return false; - } -}; - - -//true -class _true : public _expr { -public: - //parse with whitespace - virtual bool parse_non_term(_context&) const { - return true; - } - - //parse terminal - virtual bool parse_term(_context&) const { - return true; - } -}; - - -//false -class _false: public _expr { -public: - //parse with whitespace - virtual bool parse_non_term(_context&) const { - return false; - } - - //parse terminal - virtual bool parse_term(_context&) const { - return false; - } -}; - -//exception thrown when left recursion terminates successfully -struct _lr_ok { - rule* m_rule; - _lr_ok(rule* r) : m_rule(r) {} -}; - - -//constructor -_state::_state(_context& con) : - m_pos(con.m_pos), - m_matches(con.m_matches.size()) -{ -} - - -//parse non-term rule. -bool _context::parse_non_term(rule& r) { - //save the state of the rule - rule::_state old_state = r.m_state; - - //success/failure result - bool ok = false; - - //compute the new position - size_t new_pos = m_pos.m_it - m_begin; - - //check if we have left recursion - bool lr = new_pos == r.m_state.m_pos; - - //update the rule's state - r.m_state.m_pos = new_pos; - - //handle the mode of the rule - switch (r.m_state.m_mode) { - //normal parse - case rule::_PARSE: - if (lr) { - //first try to parse the rule by rejecting it, so alternative branches are examined - r.m_state.m_mode = rule::_REJECT; - ok = _parse_non_term(r); - - //if the first try is successful, try accepting the rule, - //so other elements of the sequence are parsed - if (ok) { - r.m_state.m_mode = rule::_ACCEPT; - - //loop until no more parsing can be done - for(;;) { - //store the correct state, in order to backtrack if the call fails - _state st(*this); - - //update the rule position to the current position, - //because at this state the rule is resolving the left recursion - r.m_state.m_pos = m_pos.m_it - m_begin; - - //if parsing fails, restore the last good state and stop - if (!_parse_non_term(r)) { - restore(st); - break; - } - } - - //since the left recursion was resolved successfully, - //return via a non-local exit - r.m_state = old_state; - throw _lr_ok(r.this_ptr()); - } - } - else { - try { - ok = _parse_non_term(r); - } - catch (const _lr_ok &ex) { - //since left recursions may be mutual, we must test which rule's left recursion - //was ended successfully - if (ex.m_rule == r.this_ptr()) { - ok = true; - } - else { - r.m_state = old_state; - throw; - } - } - } - break; - - //reject the left recursive rule - case rule::_REJECT: - if (lr) { - ok = false; - } - else { - r.m_state.m_mode = rule::_PARSE; - ok = _parse_non_term(r); - r.m_state.m_mode = rule::_REJECT; - } - break; - - //accept the left recursive rule - case rule::_ACCEPT: - if (lr) { - ok = true; - } - else { - r.m_state.m_mode = rule::_PARSE; - ok = _parse_non_term(r); - r.m_state.m_mode = rule::_ACCEPT; - } - break; - } - - //restore the rule's state - r.m_state = old_state; - - return ok; -} - - -//parse term rule. -bool _context::parse_term(rule& r) { - //save the state of the rule - rule::_state old_state = r.m_state; - - //success/failure result - bool ok = false; - - //compute the new position - size_t new_pos = m_pos.m_it - m_begin; - - //check if we have left recursion - bool lr = new_pos == r.m_state.m_pos; - - //update the rule's state - r.m_state.m_pos = new_pos; - - //handle the mode of the rule - switch (r.m_state.m_mode) { - //normal parse - case rule::_PARSE: - if (lr) { - //first try to parse the rule by rejecting it, so alternative branches are examined - r.m_state.m_mode = rule::_REJECT; - ok = _parse_term(r); - - //if the first try is successful, try accepting the rule, - //so other elements of the sequence are parsed - if (ok) { - r.m_state.m_mode = rule::_ACCEPT; - - //loop until no more parsing can be done - for(;;) { - //store the correct state, in order to backtrack if the call fails - _state st(*this); - - //update the rule position to the current position, - //because at this state the rule is resolving the left recursion - r.m_state.m_pos = m_pos.m_it - m_begin; - - //if parsing fails, restore the last good state and stop - if (!_parse_term(r)) { - restore(st); - break; - } - } - - //since the left recursion was resolved successfully, - //return via a non-local exit - r.m_state = old_state; - throw _lr_ok(r.this_ptr()); - } - } else { - try { - ok = _parse_term(r); - } - catch (const _lr_ok& ex) { - //since left recursions may be mutual, we must test which rule's left recursion - //was ended successfully - if (ex.m_rule == r.this_ptr()) { - ok = true; - } - else { - r.m_state = old_state; - throw; - } - } - } - break; - - //reject the left recursive rule - case rule::_REJECT: - if (lr) { - ok = false; - } else { - r.m_state.m_mode = rule::_PARSE; - ok = _parse_term(r); - r.m_state.m_mode = rule::_REJECT; - } - break; - - //accept the left recursive rule - case rule::_ACCEPT: - if (lr) { - ok = true; - } else { - r.m_state.m_mode = rule::_PARSE; - ok = _parse_term(r); - r.m_state.m_mode = rule::_ACCEPT; - } - break; - } - - //restore the rule's state - r.m_state = old_state; - - return ok; -} - - -//parse non-term rule internal. -bool _context::_parse_non_term(rule& r) { - bool ok = false; - if (_private::get_parse_proc(r)) { - pos b = m_pos; - ok = _private::get_expr(r)->parse_non_term(*this); - if (ok) { - m_matches.push_back(_match(r.this_ptr(), b, m_pos)); - } - } else { - ok = _private::get_expr(r)->parse_non_term(*this); - } - return ok; -} - - -//parse term rule internal. -bool _context::_parse_term(rule& r) { - bool ok = false; - if (_private::get_parse_proc(r)) { - pos b = m_pos; - ok = _private::get_expr(r)->parse_term(*this); - if (ok) { - m_matches.push_back(_match(r.this_ptr(), b, m_pos)); - } - } else { - ok = _private::get_expr(r)->parse_term(*this); - } - return ok; -} - - -//get syntax error -static error _syntax_error(_context& con) { - return error(con.m_error_pos, con.m_error_pos, ERROR_SYNTAX_ERROR); -} - - -//get eof error -static error _eof_error(_context& con) { - return error(con.m_error_pos, con.m_error_pos, ERROR_INVALID_EOF); -} - - -/** constructor from input. - @param i input. -*/ -pos::pos(input& i) : - m_it(i.begin()), - m_line(1), - m_col(1) -{ -} - - -/** character terminal constructor. - @param c character. -*/ -expr::expr(char c) : - m_expr(new _char(c)) -{ -} - - -/** null-terminated string terminal constructor. - @param s null-terminated string. -*/ -expr::expr(const char* s) : - m_expr(new _string(s)) -{ -} - - -/** rule reference constructor. - @param r rule. -*/ -expr::expr(rule& r) : - m_expr(new _ref(r)) -{ -} - - -/** creates a zero-or-more loop out of this expression. - @return a zero-or-more loop expression. -*/ -expr expr::operator*() const { - return _private::construct_expr(new _loop0(m_expr)); -} - - -/** creates a one-or-more loop out of this expression. - @return a one-or-more loop expression. -*/ -expr expr::operator+() const { - return _private::construct_expr(new _loop1(m_expr)); -} - - -/** creates an optional out of this expression. - @return an optional expression. -*/ -expr expr::operator-() const { - return _private::construct_expr(new _optional(m_expr)); -} - - -/** creates an AND-expression. - @return an AND-expression. -*/ -expr expr::operator&() const { - return _private::construct_expr((new _and(m_expr))); -} - - -/** creates a NOT-expression. - @return a NOT-expression. -*/ -expr expr::operator!() const { - return _private::construct_expr(new _not(m_expr)); -} - - -/** constructor. - @param b begin position. - @param e end position. -*/ -input_range::input_range(const pos& b, const pos& e) : -m_begin(b), -m_end(e) {} - - -/** constructor. - @param b begin position. - @param e end position. - @param t error type. -*/ -error::error(const pos& b, const pos& e, int t) : -input_range(b, e), -m_type(t) {} - - -/** compare on begin position. - @param e the other error to compare this with. - @return true if this comes before the previous error, false otherwise. -*/ -bool error::operator<(const error& e) const { - return m_begin.m_it < e.m_begin.m_it; -} - -rule::rule() : -m_expr(nullptr), -m_parse_proc(nullptr) {} - -/** character terminal constructor. - @param c character. -*/ -rule::rule(char c) : -m_expr(new _char(c)), -m_parse_proc(nullptr) {} - -/** null-terminated string terminal constructor. - @param s null-terminated string. -*/ -rule::rule(const char* s) : -m_expr(new _string(s)), -m_parse_proc(nullptr) {} - -/** constructor from expression. - @param e expression. -*/ -rule::rule(const expr& e) : -m_expr(_private::get_expr(e)), -m_parse_proc(nullptr) {} - - -/** constructor from rule. - @param r rule. -*/ -rule::rule(rule& r) : -m_expr(new _ref(r)), -m_parse_proc(nullptr) {} - -rule& rule::operator=(rule& r) { - m_expr = new _ref(r); - return *this; -} - -rule &rule::operator=(const expr& e) { - m_expr = _private::get_expr(e); - return *this; -} - -/** invalid constructor from rule (required by gcc). - @exception std::logic_error always thrown. -*/ -rule::rule(const rule&) { - throw std::logic_error("invalid operation"); -} - - -/** deletes the internal object that represents the expression. -*/ -rule::~rule() { - delete m_expr; -} - - -/** creates a zero-or-more loop out of this rule. - @return a zero-or-more loop rule. -*/ -expr rule::operator*() { - return _private::construct_expr(new _loop0(new _ref(*this))); -} - - -/** creates a one-or-more loop out of this rule. - @return a one-or-more loop rule. -*/ -expr rule::operator+() { - return _private::construct_expr(new _loop1(new _ref(*this))); -} - - -/** creates an optional out of this rule. - @return an optional rule. -*/ -expr rule::operator-() { - return _private::construct_expr(new _optional(new _ref(*this))); -} - - -/** creates an AND-expression out of this rule. - @return an AND-expression out of this rule. -*/ -expr rule::operator&() { - return _private::construct_expr(new _and(new _ref(*this))); -} - - -/** creates a NOT-expression out of this rule. - @return a NOT-expression out of this rule. -*/ -expr rule::operator!() { - return _private::construct_expr(new _not(new _ref(*this))); -} - - -/** sets the parse procedure. - @param p procedure. -*/ -void rule::set_parse_proc(parse_proc p) { - assert(p); - m_parse_proc = p; -} - - -/** creates a sequence of expressions. - @param left left operand. - @param right right operand. - @return an expression which parses a sequence. -*/ -expr operator >> (const expr& left, const expr& right) { - return _private::construct_expr( - new _seq(_private::get_expr(left), _private::get_expr(right))); -} - - -/** creates a choice of expressions. - @param left left operand. - @param right right operand. - @return an expression which parses a choice. -*/ -expr operator | (const expr& left, const expr& right) { - return _private::construct_expr( - new _choice(_private::get_expr(left), _private::get_expr(right))); -} - - -/** converts a parser expression into a terminal. - @param e expression. - @return an expression which parses a terminal. -*/ -expr term(const expr& e) { - return _private::construct_expr( - new _term(_private::get_expr(e))); -} - - -/** creates a set expression from a null-terminated string. - @param s null-terminated string with characters of the set. - @return an expression which parses a single character out of a set. -*/ -expr set(const char* s) { - return _private::construct_expr(new _set(s)); -} - - -/** creates a range expression. - @param min min character. - @param max max character. - @return an expression which parses a single character out of range. -*/ -expr range(int min, int max) { - return _private::construct_expr(new _set(min, max)); -} - - -/** creates an expression which increments the line counter - and resets the column counter when the given expression - is parsed successfully; used for newline characters. - @param e expression to wrap into a newline parser. - @return an expression that handles newlines. -*/ -expr nl(const expr& e) { - return _private::construct_expr(new _nl(_private::get_expr(e))); -} - - -/** creates an expression which tests for the end of input. - @return an expression that handles the end of input. -*/ -expr eof() { - return _private::construct_expr(new _eof()); -} - - -/** creates a not expression. - @param e expression. - @return the appropriate expression. -*/ -expr not_(const expr& e) { - return !e; -} - - -/** creates an and expression. - @param e expression. - @return the appropriate expression. -*/ -expr and_(const expr& e) { - return &e; -} - - -/** creates an expression that parses any character. - @return the appropriate expression. -*/ -expr any() { - return _private::construct_expr(new _any()); -} - - -/** parsing succeeds without consuming any input. -*/ -expr true_() { - return _private::construct_expr(new _true()); -} - - -/** parsing fails without consuming any input. -*/ -expr false_() { - return _private::construct_expr(new _false()); -} - - -/** parse with target expression and let user handle result. -*/ -expr user(const expr& e, const user_handler& handler) { - return _private::construct_expr(new _user(_private::get_expr(e), handler)); -} - - -/** parses the given input. - The parse procedures of each rule parsed are executed - before this function returns, if parsing succeeds. - @param i input. - @param g root rule of grammar. - @param el list of errors. - @param d user data, passed to the parse procedures. - @return true on parsing success, false on failure. -*/ -bool parse(input& i, rule& g, error_list& el, void* d, void* ud) { - //prepare context - _context con(i, ud); - - //parse grammar - if (!con.parse_non_term(g)) { - el.push_back(_syntax_error(con)); - return false; - } - - //if end is not reached, there was an error - if (!con.end()) { - if (con.m_error_pos.m_it < con.m_end) { - el.push_back(_syntax_error(con)); - } else { - el.push_back(_eof_error(con)); - } - return false; - } - - //success; execute the parse procedures - con.do_parse_procs(d); - return true; -} - - -} //namespace parserlib diff --git a/src/MoonP/parser.hpp b/src/MoonP/parser.hpp deleted file mode 100644 index f70475f..0000000 --- a/src/MoonP/parser.hpp +++ /dev/null @@ -1,422 +0,0 @@ -/* Copyright (c) 2012, Achilleas Margaritis, modified by Jin Li -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/ - -#pragma once - - -//gcc chokes without rule::rule(const rule &), -//msvc complains when rule::rule(const rule &) is defined. -#ifdef _MSC_VER -#pragma warning (disable: 4521) -#endif - - -#include -#include -#include -#include -#include -#include - -namespace parserlib { - -///type of the parser's input. -typedef std::basic_string input; -typedef input::iterator input_it; -typedef std::wstring_convert> Converter; - -class _private; -class _expr; -class _context; -class rule; - - -struct item_t { - input_it begin; - input_it end; - void* user_data; -}; -typedef std::function user_handler; - - -///position into the input. -class pos { -public: - ///interator into the input. - input::iterator m_it; - - ///line. - int m_line; - - ///column. - int m_col; - - ///null constructor. - pos():m_line(0),m_col(0) {} - - /** constructor from input. - @param i input. - */ - pos(input &i); -}; - - -/** a grammar expression. -*/ -class expr { -public: - /** character terminal constructor. - @param c character. - */ - expr(char c); - - /** null-terminated string terminal constructor. - @param s null-terminated string. - */ - expr(const char* s); - - /** rule reference constructor. - @param r rule. - */ - expr(rule& r); - - /** creates a zero-or-more loop out of this expression. - @return a zero-or-more loop expression. - */ - expr operator*() const; - - /** creates a one-or-more loop out of this expression. - @return a one-or-more loop expression. - */ - expr operator+() const; - - /** creates an optional out of this expression. - @return an optional expression. - */ - expr operator-() const; - - /** creates an AND-expression. - @return an AND-expression. - */ - expr operator&() const; - - /** creates a NOT-expression. - @return a NOT-expression. - */ - expr operator!() const; - -private: - //internal expression - _expr* m_expr; - - //internal constructor from internal expression - expr(_expr* e) : m_expr(e) {} - - //assignment not allowed - expr& operator=(expr&); - - friend class _private; -}; - - -/** type of procedure to invoke when a rule is successfully parsed. - @param b begin position of input. - @param e end position of input. - @param d pointer to user data. -*/ -typedef void (*parse_proc)(const pos& b, const pos& e, void* d); - - -///input range. -class input_range { -public: - virtual ~input_range() {} - - ///begin position. - pos m_begin; - - ///end position. - pos m_end; - - ///empty constructor. - input_range() {} - - /** constructor. - @param b begin position. - @param e end position. - */ - input_range(const pos& b, const pos& e); -}; - - -///enum with error types. -enum ERROR_TYPE { - ///syntax error - ERROR_SYNTAX_ERROR = 1, - - ///invalid end of file - ERROR_INVALID_EOF, - - ///first user error - ERROR_USER = 100 -}; - - -///error. -class error : public input_range { -public: - ///type - int m_type; - - /** constructor. - @param b begin position. - @param e end position. - @param t type. - */ - error(const pos& b, const pos& e, int t); - - /** compare on begin position. - @param e the other error to compare this with. - @return true if this comes before the previous error, false otherwise. - */ - bool operator<(const error& e) const; -}; - - -///type of error list. -typedef std::list error_list; - - -/** represents a rule. -*/ -class rule { -public: - /** character terminal constructor. - @param c character. - */ - rule(); - rule(char c); - - /** null-terminated string terminal constructor. - @param s null-terminated string. - */ - rule(const char* s); - - /** constructor from expression. - @param e expression. - */ - rule(const expr& e); - - /** constructor from rule. - @param r rule. - */ - rule(rule& r); - - /** invalid constructor from rule (required by gcc). - @param r rule. - @exception std::logic_error always thrown. - */ - rule(const rule& r); - - /** deletes the internal object that represents the expression. - */ - ~rule(); - - /** creates a zero-or-more loop out of this rule. - @return a zero-or-more loop rule. - */ - expr operator*(); - - /** creates a one-or-more loop out of this rule. - @return a one-or-more loop rule. - */ - expr operator+(); - - /** creates an optional out of this rule. - @return an optional rule. - */ - expr operator-(); - - /** creates an AND-expression out of this rule. - @return an AND-expression out of this rule. - */ - expr operator&(); - - /** creates a NOT-expression out of this rule. - @return a NOT-expression out of this rule. - */ - expr operator!(); - - /** sets the parse procedure. - @param p procedure. - */ - void set_parse_proc(parse_proc p); - - /** get the this ptr (since operator & is overloaded). - @return pointer to this. - */ - rule* this_ptr() { return this; } - - rule& operator=(rule&); - - rule& operator=(const expr&); - -private: - //mode - enum _MODE { - _PARSE, - _REJECT, - _ACCEPT - }; - - //state - struct _state { - //position in source code, relative to start - size_t m_pos; - - //mode - _MODE m_mode; - - //constructor - _state(size_t pos = -1, _MODE mode = _PARSE) : - m_pos(pos), m_mode(mode) {} - }; - - //internal expression - _expr* m_expr; - - //associated parse procedure. - parse_proc m_parse_proc; - - //state - _state m_state; - - friend class _private; - friend class _context; -}; - - -/** creates a sequence of expressions. - @param left left operand. - @param right right operand. - @return an expression which parses a sequence. -*/ -expr operator>>(const expr& left, const expr& right); - - -/** creates a choice of expressions. - @param left left operand. - @param right right operand. - @return an expression which parses a choice. -*/ -expr operator|(const expr& left, const expr& right); - - -/** converts a parser expression into a terminal. - @param e expression. - @return an expression which parses a terminal. -*/ -expr term(const expr& e); - - -/** creates a set expression from a null-terminated string. - @param s null-terminated string with characters of the set. - @return an expression which parses a single character out of a set. -*/ -expr set(const char* s); - - -/** creates a range expression. - @param min min character. - @param max max character. - @return an expression which parses a single character out of range. -*/ -expr range(int min, int max); - - -/** creates an expression which increments the line counter - and resets the column counter when the given expression - is parsed successfully; used for newline characters. - @param e expression to wrap into a newline parser. - @return an expression that handles newlines. -*/ -expr nl(const expr& e); - - -/** creates an expression which tests for the end of input. - @return an expression that handles the end of input. -*/ -expr eof(); - - -/** creates a not expression. - @param e expression. - @return the appropriate expression. -*/ -expr not_(const expr& e); - - -/** creates an and expression. - @param e expression. - @return the appropriate expression. -*/ -expr and_(const expr& e); - - -/** creates an expression that parses any character. - @return the appropriate expression. -*/ -expr any(); - - -/** parsing succeeds without consuming any input. -*/ -expr true_(); - - -/** parsing fails without consuming any input. -*/ -expr false_(); - - -/** parse with target expression and let user handle result. -*/ -expr user(const expr& e, const user_handler& handler); - - -/** parses the given input. - The parse procedures of each rule parsed are executed - before this function returns, if parsing succeeds. - @param i input. - @param g root rule of grammar. - @param el list of errors. - @param d user data, passed to the parse procedures. - @return true on parsing success, false on failure. -*/ -bool parse(input& i, rule& g, error_list& el, void* d, void* ud); - - -/** output the specific input range to the specific stream. - @param stream stream. - @param ir input range. - @return the stream. -*/ -template T& operator<<(T& stream, const input_range& ir) { - for(input::const_iterator it = ir.m_begin.m_it; - it != ir.m_end.m_it; - ++it) { - stream << (typename T::char_type)*it; - } - return stream; -} - - -} //namespace parserlib diff --git a/src/MoonP/stacktraceplus.h b/src/MoonP/stacktraceplus.h deleted file mode 100644 index e884a2c..0000000 --- a/src/MoonP/stacktraceplus.h +++ /dev/null @@ -1,528 +0,0 @@ -R"lua_codes( ---[[ -Copyright (c) 2010 Ignacio Burgueño, modified by Li Jin - -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.]] - --- tables -local _G = _G -local string, io, debug, coroutine = string, io, debug, coroutine - --- functions -local tostring, require = tostring, require -local next, assert = next, assert -local pcall, type, pairs, ipairs = pcall, type, pairs, ipairs -local error = error - -assert(debug, "debug table must be available at this point") - -local string_gmatch = string.gmatch -local string_sub = string.sub -local table_concat = table.concat - -local moonp = require("moonp") - -local _M = { - max_tb_output_len = 70, -- controls the maximum length of the 'stringified' table before cutting with ' (more...)' - dump_locals = true, - simplified = false -} - --- this tables should be weak so the elements in them won't become uncollectable -local m_known_tables = { [_G] = "_G (global table)" } -local function add_known_module(name, desc) - local ok, mod = pcall(require, name) - if ok then - m_known_tables[mod] = desc - end -end - -add_known_module("string", "string module") -add_known_module("io", "io module") -add_known_module("os", "os module") -add_known_module("table", "table module") -add_known_module("math", "math module") -add_known_module("package", "package module") -add_known_module("debug", "debug module") -add_known_module("coroutine", "coroutine module") - --- lua5.2 -add_known_module("bit32", "bit32 module") --- luajit -add_known_module("bit", "bit module") -add_known_module("jit", "jit module") --- lua5.3 -if _VERSION >= "Lua 5.3" then - add_known_module("utf8", "utf8 module") -end - - -local m_user_known_tables = {} - -local m_known_functions = {} -for _, name in ipairs{ - -- Lua 5.2, 5.1 - "assert", - "collectgarbage", - "dofile", - "error", - "getmetatable", - "ipairs", - "load", - "loadfile", - "next", - "pairs", - "pcall", - "print", - "rawequal", - "rawget", - "rawlen", - "rawset", - "require", - "select", - "setmetatable", - "tonumber", - "tostring", - "type", - "xpcall", - - -- Lua 5.1 - "gcinfo", - "getfenv", - "loadstring", - "module", - "newproxy", - "setfenv", - "unpack", - -- TODO: add table.* etc functions -} do - if _G[name] then - m_known_functions[_G[name]] = name - end -end - -local m_user_known_functions = {} - -local function safe_tostring (value) - local ok, err = pcall(tostring, value) - if ok then return err else return (": '%s'"):format(err) end -end - --- Private: --- Parses a line, looking for possible function definitions (in a very naive way) --- Returns '(anonymous)' if no function name was found in the line -local function ParseLine(line) - assert(type(line) == "string") - local match = line:match("^%s*function%s+(%w+)") - if match then - --print("+++++++++++++function", match) - return match - end - match = line:match("^%s*local%s+function%s+(%w+)") - if match then - --print("++++++++++++local", match) - return match - end - match = line:match("^%s*local%s+(%w+)%s+=%s+function") - if match then - --print("++++++++++++local func", match) - return match - end - match = line:match("%s*function%s*%(") -- this is an anonymous function - if match then - --print("+++++++++++++function2", match) - return "(anonymous)" - end - return "(anonymous)" -end - --- Private: --- Tries to guess a function's name when the debug info structure does not have it. --- It parses either the file or the string where the function is defined. --- Returns '?' if the line where the function is defined is not found -local function GuessFunctionName(info) - -- print("guessing function name") - if type(info.source) == "string" and info.source:sub(1,1) == "@" then - local fname = info.source:sub(2) - local text - if moonp.file_exist(fname) then - text = moonp.read_file(fname) - end - if not text then - -- print("file not found: "..tostring(err)) -- whoops! - return "?" - end - local line - local count = 0 - for lineText in (text.."\n"):gmatch("(.-)\n") do - line = lineText - count = count + 1 - if count == info.linedefined then - break - end - end - if not line then - --print("line not found") -- whoops! - return "?" - end - return ParseLine(line) - else - local line - local lineNumber = 0 - for l in string_gmatch(info.source, "([^\n]+)\n-") do - lineNumber = lineNumber + 1 - if lineNumber == info.linedefined then - line = l - break - end - end - if not line then - -- print("line not found") -- whoops! - return "?" - end - return ParseLine(line) - end -end - ---- --- Dumper instances are used to analyze stacks and collect its information. --- -local Dumper = {} - -Dumper.new = function(thread) - local t = { lines = {} } - for k,v in pairs(Dumper) do t[k] = v end - - t.dumping_same_thread = (thread == coroutine.running()) - - -- if a thread was supplied, bind it to debug.info and debug.get - -- we also need to skip this additional level we are introducing in the callstack (only if we are running - -- in the same thread we're inspecting) - if type(thread) == "thread" then - t.getinfo = function(level, what) - if t.dumping_same_thread and type(level) == "number" then - level = level + 1 - end - return debug.getinfo(thread, level, what) - end - t.getlocal = function(level, loc) - if t.dumping_same_thread then - level = level + 1 - end - return debug.getlocal(thread, level, loc) - end - else - t.getinfo = debug.getinfo - t.getlocal = debug.getlocal - end - - return t -end - --- helpers for collecting strings to be used when assembling the final trace -function Dumper:add (text) - self.lines[#self.lines + 1] = text -end -function Dumper:add_f (fmt, ...) - self:add(fmt:format(...)) -end -function Dumper:concat_lines () - return table_concat(self.lines) -end - ---- --- Private: --- Iterates over the local variables of a given function. --- --- @param level The stack level where the function is. --- -function Dumper:DumpLocals (level) - if not _M.dump_locals then return end - - local prefix = "\t " - local i = 1 - - if self.dumping_same_thread then - level = level + 1 - end - - local name, value = self.getlocal(level, i) - if not name then - return - end - self:add("\tLocal variables:\r\n") - while name do - if type(value) == "number" then - self:add_f("%s%s = number: %g\r\n", prefix, name, value) - elseif type(value) == "boolean" then - self:add_f("%s%s = boolean: %s\r\n", prefix, name, tostring(value)) - elseif type(value) == "string" then - self:add_f("%s%s = string: %q\r\n", prefix, name, value) - elseif type(value) == "userdata" then - self:add_f("%s%s = %s\r\n", prefix, name, safe_tostring(value)) - elseif type(value) == "nil" then - self:add_f("%s%s = nil\r\n", prefix, name) - elseif type(value) == "table" then - if m_known_tables[value] then - self:add_f("%s%s = %s\r\n", prefix, name, m_known_tables[value]) - elseif m_user_known_tables[value] then - self:add_f("%s%s = %s\r\n", prefix, name, m_user_known_tables[value]) - else - local txt = "{" - for k,v in pairs(value) do - txt = txt..safe_tostring(k)..":"..safe_tostring(v) - if #txt > _M.max_tb_output_len then - txt = txt.." (more...)" - break - end - if next(value, k) then txt = txt..", " end - end - self:add_f("%s%s = %s %s\r\n", prefix, name, safe_tostring(value), txt.."}") - end - elseif type(value) == "function" then - local info = self.getinfo(value, "nS") - local fun_name = info.name or m_known_functions[value] or m_user_known_functions[value] - if info.what == "C" then - self:add_f("%s%s = C %s\r\n", prefix, name, (fun_name and ("function: " .. fun_name) or tostring(value))) - else - local source = info.short_src - if source:sub(2,7) == "string" then - source = source:sub(9) - end - --for k,v in pairs(info) do print(k,v) end - fun_name = fun_name or GuessFunctionName(info) - self:add_f("%s%s = Lua function '%s' (defined at line %d of chunk %s)\r\n", prefix, name, fun_name, info.linedefined, source) - end - elseif type(value) == "thread" then - self:add_f("%sthread %q = %s\r\n", prefix, name, tostring(value)) - end - i = i + 1 - name, value = self.getlocal(level, i) - end -end - -local function getMoonLineNumber(fname, line) - local moonCompiled = require("moonp").moon_compiled - local source = moonCompiled["@"..fname] - if not source then - source = moonCompiled["@="..fname] - end - if not source then - local name_path = fname:gsub("%.", moonp.dirsep) - local file_exist, file_path - for path in package.path:gmatch("[^;]+") do - file_path = path:gsub("?", name_path) - file_exist = moonp.file_exist(file_path) - if file_exist then - break - end - end - if file_exist then - local codes = moonp.read_file(file_path) - local moonFile = codes:match("^%s*--%s*%[moonp%]:%s*([^\n]*)") - if moonFile then - fname = moonFile:gsub("^%s*(.-)%s*$", "%1") - source = codes - end - end - end - if source then - local current, target = 1, tonumber(line) - local findLine = line - for lineCode in source:gmatch("([^\n]*)\n") do - local num = lineCode:match("--%s*(%d+)%s*$") - if num then - findLine = num - end - if current == target then - return fname, findLine or line - end - current = current + 1 - end - end - return fname, line -end - ---- --- Public: --- Collects a detailed stack trace, dumping locals, resolving function names when they're not available, etc. --- This function is suitable to be used as an error handler with pcall or xpcall --- --- @param thread An optional thread whose stack is to be inspected (defaul is the current thread) --- @param message An optional error string or object. --- @param level An optional number telling at which level to start the traceback (default is 1) --- --- Returns a string with the stack trace. --- -function _M.stacktrace(thread, message, level) - if type(thread) ~= "thread" then - -- shift parameters left - thread, message, level = nil, thread, message - end - - thread = thread or coroutine.running() - - level = level or 1 - - local dumper = Dumper.new(thread) - - if type(message) == "table" then - dumper:add("an error object {\r\n") - local first = true - for k,v in pairs(message) do - if first then - dumper:add(" ") - first = false - else - dumper:add(",\r\n ") - end - dumper:add(safe_tostring(k)) - dumper:add(": ") - dumper:add(safe_tostring(v)) - end - dumper:add("\r\n}") - elseif type(message) == "string" then - local fname, line, msg = message:match('(.+):(%d+): (.*)$') - if fname then - local nfname, nline, nmsg = fname:match('(.+):(%d+): (.*)$') - if nfname then - fname = nmsg - end - end - if fname then - local fn = fname:match("%[string \"(.-)\"%]") - if fn then fname = fn end - fname = fname:gsub("^%s*(.-)%s*$", "%1") - fname, line = getMoonLineNumber(fname, line) - if _M.simplified then - message = table.concat({ - "", fname, ":", - line, ": ", msg}) - message = message:gsub("^%(moonplus%):%s*%d+:%s*", "") - message = message:gsub("%s(%d+):", "%1:") - else - message = table.concat({ - "[string \"", fname, "\"]:", - line, ": ", msg}) - end - end - dumper:add(message) - end - - dumper:add("\r\n") - dumper:add[[ -Stack Traceback -=============== -]] - - local level_to_show = 1 - if dumper.dumping_same_thread then level = level + 1 end - - local info = dumper.getinfo(level, "nSlf") - while info do - if info.source and info.source:sub(1,1) == "@" then - info.source = info.source:sub(2) - elseif info.what == "main" or info.what == "Lua" then - info.source = info.source - end - info.source, info.currentline = getMoonLineNumber(info.source, info.currentline) - if info.what == "main" then - if _M.simplified then - dumper:add_f("(%d) '%s':%d\r\n", level_to_show, info.source, info.currentline) - else - dumper:add_f("(%d) main chunk of file '%s' at line %d\r\n", level_to_show, info.source, info.currentline) - end - elseif info.what == "C" then - --print(info.namewhat, info.name) - --for k,v in pairs(info) do print(k,v, type(v)) end - local function_name = m_user_known_functions[info.func] or m_known_functions[info.func] or info.name or tostring(info.func) - dumper:add_f("(%d) %s C function '%s'\r\n", level_to_show, info.namewhat, function_name) - --dumper:add_f("%s%s = C %s\r\n", prefix, name, (m_known_functions[value] and ("function: " .. m_known_functions[value]) or tostring(value))) - elseif info.what == "tail" then - --print("tail") - --for k,v in pairs(info) do print(k,v, type(v)) end--print(info.namewhat, info.name) - dumper:add_f("(%d) tail call\r\n", level_to_show) - dumper:DumpLocals(level) - elseif info.what == "Lua" then - local source = info.source - local function_name = m_user_known_functions[info.func] or m_known_functions[info.func] or info.name - if source:sub(2, 7) == "string" then - source = source:sub(10,-3) - end - local was_guessed = false - if not function_name or function_name == "?" then - --for k,v in pairs(info) do print(k,v, type(v)) end - function_name = GuessFunctionName(info) - was_guessed = true - end - -- test if we have a file name - local function_type = (info.namewhat == "") and "function" or info.namewhat - if info.source and info.source:sub(1, 1) == "@" then - if _M.simplified then - dumper:add_f("(%d) '%s':%d%s\r\n", level_to_show, info.source:sub(2), info.currentline, was_guessed and " (guess)" or "") - else - dumper:add_f("(%d) Lua %s '%s' at file '%s':%d%s\r\n", level_to_show, function_type, function_name, info.source:sub(2), info.currentline, was_guessed and " (best guess)" or "") - end - elseif info.source and info.source:sub(1,1) == '#' then - if _M.simplified then - dumper:add_f("(%d) '%s':%d%s\r\n", level_to_show, info.source:sub(2), info.currentline, was_guessed and " (guess)" or "") - else - dumper:add_f("(%d) Lua %s '%s' at template '%s':%d%s\r\n", level_to_show, function_type, function_name, info.source:sub(2), info.currentline, was_guessed and " (best guess)" or "") - end - else - if _M.simplified then - dumper:add_f("(%d) '%s':%d\r\n", level_to_show, source, info.currentline) - else - dumper:add_f("(%d) Lua %s '%s' at chunk '%s':%d\r\n", level_to_show, function_type, function_name, source, info.currentline) - end - end - dumper:DumpLocals(level) - else - dumper:add_f("(%d) unknown frame %s\r\n", level_to_show, info.what) - end - - level = level + 1 - level_to_show = level_to_show + 1 - info = dumper.getinfo(level, "nSlf") - end - - return dumper:concat_lines() -end - --- --- Adds a table to the list of known tables -function _M.add_known_table(tab, description) - if m_known_tables[tab] then - error("Cannot override an already known table") - end - m_user_known_tables[tab] = description -end - --- --- Adds a function to the list of known functions -function _M.add_known_function(fun, description) - if m_known_functions[fun] then - error("Cannot override an already known function") - end - m_user_known_functions[fun] = description -end - -return _M - -)lua_codes"; diff --git a/src/moonp.cpp b/src/moonp.cpp deleted file mode 100644 index 27c6016..0000000 --- a/src/moonp.cpp +++ /dev/null @@ -1,570 +0,0 @@ -/* Copyright (c) 2021 Jin Li, http://www.luvfight.me - -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. */ - -#include "MoonP/moon_compiler.h" -#include "MoonP/moon_parser.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -using namespace std::string_view_literals; -#include "ghc/fs_std.hpp" -#include "linenoise.hpp" - -#if not (defined MOONP_NO_MACRO && defined MOONP_COMPILER_ONLY) -#define _DEFER(code,line) std::shared_ptr _defer_##line(nullptr, [&](auto){code;}) -#define DEFER(code) _DEFER(code,__LINE__) -extern "C" { -#include "lua.h" -#include "lauxlib.h" -#include "lualib.h" -int luaopen_moonp(lua_State* L); -} // extern "C" - -static void openlibs(void* state) { - lua_State* L = static_cast(state); - luaL_openlibs(L); - luaopen_moonp(L); -} - -void pushMoonp(lua_State* L, std::string_view name) { - lua_getglobal(L, "package"); // package - lua_getfield(L, -1, "loaded"); // package loaded - lua_getfield(L, -1, "moonp"); // package loaded moonp - lua_pushlstring(L, &name.front(), name.size()); // package loaded moonp name - lua_gettable(L, -2); // loaded[name], package loaded moonp item - lua_insert(L, -4); // item package loaded moonp - lua_pop(L, 3); // item -} - -void pushOptions(lua_State* L, int lineOffset) { - lua_newtable(L); - lua_pushliteral(L, "lint_global"); - lua_pushboolean(L, 0); - lua_rawset(L, -3); - lua_pushliteral(L, "implicit_return_root"); - lua_pushboolean(L, 1); - lua_rawset(L, -3); - lua_pushliteral(L, "reserve_line_number"); - lua_pushboolean(L, 1); - lua_rawset(L, -3); - lua_pushliteral(L, "space_over_tab"); - lua_pushboolean(L, 0); - lua_rawset(L, -3); - lua_pushliteral(L, "same_module"); - lua_pushboolean(L, 1); - lua_rawset(L, -3); - lua_pushliteral(L, "line_offset"); - lua_pushinteger(L, lineOffset); - lua_rawset(L, -3); -} -#endif // not (defined MOONP_NO_MACRO && defined MOONP_COMPILER_ONLY) - -#ifndef MOONP_NO_MACRO -#define MOONP_ARGS nullptr,openlibs -#else -#define MOONP_ARGS -#endif // MOONP_NO_MACRO - -#ifndef MOONP_COMPILER_ONLY -static const char luaminifyCodes[] = -#include "LuaMinify.h" - -static void pushLuaminify(lua_State* L) { - if (luaL_loadbuffer(L, luaminifyCodes, sizeof(luaminifyCodes) / sizeof(luaminifyCodes[0]) - 1, "=(luaminify)") != 0) { - std::string err = std::string("failed to load luaminify module.\n") + lua_tostring(L, -1); - luaL_error(L, err.c_str()); - } else if (lua_pcall(L, 0, 1, 0) != 0) { - std::string err = std::string("failed to init luaminify module.\n") + lua_tostring(L, -1); - luaL_error(L, err.c_str()); - } -} -#endif // MOONP_COMPILER_ONLY - -int main(int narg, const char** args) { - const char* help = -"Usage: moonp [options|files|directories] ...\n\n" -" -h Print this message\n" -#ifndef MOONP_COMPILER_ONLY -" -e str Execute a file or raw codes\n" -" -m Generate minified codes\n" -#endif // MOONP_COMPILER_ONLY -" -t path Specify where to place compiled files\n" -" -o file Write output to file\n" -" -s Use spaces in generated codes instead of tabs\n" -" -p Write output to standard out\n" -" -b Dump compile time (doesn't write output)\n" -" -l Write line numbers from source codes\n" -" -v Print version\n" -#ifndef MOONP_COMPILER_ONLY -" -- Read from standard in, print to standard out\n" -" (Must be first and only argument)\n\n" -" Execute without options to enter REPL, type symbol '$'\n" -" in a single line to start/stop multi-line mode\n" -#endif // MOONP_COMPILER_ONLY -; -#ifndef MOONP_COMPILER_ONLY - if (narg == 1) { - lua_State* L = luaL_newstate(); - openlibs(L); - DEFER(lua_close(L)); - pushMoonp(L, "insert_loader"sv); - if (lua_pcall(L, 0, 0, 0) != 0) { - std::cout << lua_tostring(L, -1) << '\n'; - return 1; - } - int count = 0; - linenoise::SetMultiLine(false); - linenoise::SetCompletionCallback([](const char* editBuffer, std::vector& completions) { - std::string buf = editBuffer; - std::string tmp = buf; - MoonP::Utils::trim(tmp); - if (tmp.empty()) return; - std::string pre; - auto pos = buf.find_first_not_of(" \t\n"); - if (pos != std::string::npos) { - pre = buf.substr(0, pos); - } - switch (tmp[0]) { - case 'b': - completions.push_back(pre + "break"); - break; - case 'c': - completions.push_back(pre + "class "); - completions.push_back(pre + "continue"); - break; - case 'e': - completions.push_back(pre + "else"); - completions.push_back(pre + "export "); - break; - case 'i': - completions.push_back(pre + "import \""); - break; - case 'g': - completions.push_back(pre + "global "); - break; - case 'l': - completions.push_back(pre + "local "); - break; - case 'm': - completions.push_back(pre + "macro expr "); - completions.push_back(pre + "macro block "); - completions.push_back(pre + "macro lua "); - break; - case 's': - completions.push_back(pre + "switch "); - break; - case 'u': - completions.push_back(pre + "unless "); - break; - case 'w': - completions.push_back(pre + "with "); - completions.push_back(pre + "when "); - break; - } - }); - std::cout << "Moonscript+ "sv << MoonP::version << '\n'; - while (true) { - count++; - std::string codes; - bool quit = linenoise::Readline("> ", codes); - if (quit) return 0; - linenoise::AddHistory(codes.c_str()); - MoonP::Utils::trim(codes); - if (codes == "$"sv) { - codes.clear(); - for (std::string line; !(quit = linenoise::Readline("", line));) { - auto temp = line; - MoonP::Utils::trim(temp); - if (temp == "$"sv) { - break; - } - codes += '\n'; - codes += line; - linenoise::AddHistory(line.c_str()); - MoonP::Utils::trim(codes); - } - if (quit) return 0; - } - codes.insert(0, "global *\n"sv); - int top = lua_gettop(L); - DEFER(lua_settop(L, top)); - pushMoonp(L, "loadstring"sv); - lua_pushlstring(L, codes.c_str(), codes.size()); - lua_pushstring(L, (std::string("=(repl ") + std::to_string(count) + ')').c_str()); - pushOptions(L, -1); - const std::string_view Err = "\033[35m"sv, Val = "\033[33m"sv, Stop = "\033[0m\n"sv; - if (lua_pcall(L, 3, 2, 0) != 0) { - std::cout << Err << lua_tostring(L, -1) << Stop; - continue; - } - if (lua_isnil(L, -2) != 0) { - std::string err = lua_tostring(L, -1); - auto modName = std::string("(repl "sv) + std::to_string(count) + "):"; - if (err.substr(0, modName.size()) == modName) { - err = err.substr(modName.size()); - } - auto pos = err.find(':'); - if (pos != std::string::npos) { - int lineNum = std::stoi(err.substr(0, pos)); - err = std::to_string(lineNum - 1) + err.substr(pos); - } - std::cout << Err << err << Stop; - continue; - } - lua_pop(L, 1); - pushMoonp(L, "pcall"sv); - lua_insert(L, -2); - int last = lua_gettop(L) - 2; - if (lua_pcall(L, 1, LUA_MULTRET, 0) != 0) { - std::cout << Err << lua_tostring(L, -1) << Stop; - continue; - } - int cur = lua_gettop(L); - int retCount = cur - last; - bool success = lua_toboolean(L, -retCount) != 0; - if (success) { - if (retCount > 1) { - for (int i = 1; i < retCount; ++i) { - std::cout << Val << luaL_tolstring(L, -retCount + i, nullptr) << Stop; - lua_pop(L, 1); - } - } - } else { - std::cout << Err << lua_tostring(L, -1) << Stop; - } - } - std::cout << '\n'; - return 0; - } - bool minify = false; -#endif // MOONP_COMPILER_ONLY - MoonP::MoonConfig config; - config.implicitReturnRoot = true; - config.lintGlobalVariable = false; - config.reserveLineNumber = false; - config.useSpaceOverTab = false; - bool writeToFile = true; - bool dumpCompileTime = false; - std::string targetPath; - std::string resultFile; - std::list> files; - for (int i = 1; i < narg; ++i) { - std::string arg = args[i]; - if (arg == "--"sv) { - if (i != 1) { - std::cout << help; - return 1; - } - char ch; - std::string codes; - while ((ch = std::cin.get()) != EOF) { - codes += ch; - } - MoonP::MoonConfig conf; - conf.implicitReturnRoot = true; - conf.lintGlobalVariable = false; - conf.reserveLineNumber = false; - conf.useSpaceOverTab = true; - auto result = MoonP::MoonCompiler{MOONP_ARGS}.compile(codes, conf); - if (result.error.empty()) { - std::cout << result.codes; - return 0; - } else { - std::ostringstream buf; - std::cout << result.error << '\n'; - return 1; - } -#ifndef MOONP_COMPILER_ONLY - } else if (arg == "-e"sv) { - ++i; - if (i < narg) { - lua_State* L = luaL_newstate(); - openlibs(L); - DEFER(lua_close(L)); - pushMoonp(L, "insert_loader"sv); - if (lua_pcall(L, 0, 0, 0) != 0) { - std::cout << lua_tostring(L, -1) << '\n'; - return 1; - } - std::string evalStr = args[i]; - std::ifstream input(evalStr, std::ios::in); - if (input) { - auto ext = fs::path(evalStr).extension().string(); - for (auto& ch : ext) ch = std::tolower(ch); - if (ext == ".lua") { - lua_getglobal(L, "load"); - } else { - pushMoonp(L, "loadstring"sv); - } - std::string s( - (std::istreambuf_iterator(input)), - std::istreambuf_iterator()); - lua_pushlstring(L, s.c_str(), s.size()); - lua_pushlstring(L, evalStr.c_str(), evalStr.size()); - } else { - pushMoonp(L, "loadstring"sv); - lua_pushlstring(L, evalStr.c_str(), evalStr.size()); - lua_pushliteral(L, "=(eval str)"); - } - if (lua_pcall(L, 2, 2, 0) != 0) { - std::cout << lua_tostring(L, -1) << '\n'; - return 1; - } - if (lua_isnil(L, -2) != 0) { - std::cout << lua_tostring(L, -1) << '\n'; - return 1; - } - lua_pop(L, 1); - pushMoonp(L, "pcall"sv); - lua_insert(L, -2); - int argCount = 0; - i++; - while (i < narg) { - argCount++; - lua_pushstring(L, args[i]); - i++; - } - if (lua_pcall(L, 1 + argCount, 2, 0) != 0) { - std::cout << lua_tostring(L, -1) << '\n'; - return 1; - } - bool success = lua_toboolean(L, -2) != 0; - if (!success) { - std::cout << lua_tostring(L, -1) << '\n'; - return 1; - } - return 0; - } else { - std::cout << help; - return 1; - } - } else if (arg == "-m"sv) { - minify = true; -#endif // MOONP_COMPILER_ONLY - } else if (arg == "-s"sv) { - config.useSpaceOverTab = true; - } else if (arg == "-l"sv) { - config.reserveLineNumber = true; - } else if (arg == "-p"sv) { - writeToFile = false; - } else if (arg == "-t"sv) { - ++i; - if (i < narg) { - targetPath = args[i]; - } else { - std::cout << help; - return 1; - } - } else if (arg == "-b"sv) { - dumpCompileTime = true; - } else if (arg == "-h"sv) { - std::cout << help; - return 0; - } else if (arg == "-v"sv) { - std::cout << "Moonscript+ version: "sv << MoonP::version << '\n'; - return 0; - } else if (arg == "-o"sv) { - ++i; - if (i < narg) { - resultFile = args[i]; - } else { - std::cout << help; - return 1; - } - } else if (arg.substr(0, 1) == "-"sv && arg.find('=') != std::string::npos) { - auto argStr = arg.substr(1); - size_t idx = argStr.find('='); - auto key = argStr.substr(0, idx); - auto value = argStr.substr(idx + 1); - config.options[key] = value; - } else { - if (fs::is_directory(arg)) { - for (auto item : fs::recursive_directory_iterator(arg)) { - if (!item.is_directory()) { - auto ext = item.path().extension().string(); - for (char& ch : ext) ch = std::tolower(ch); - if (!ext.empty() && ext.substr(1) == MoonP::extension) { - files.emplace_back(item.path().string(), item.path().lexically_relative(arg).string()); - } - } - } - } else { - files.emplace_back(arg, arg); - } - } - } - if (files.empty()) { - std::cout << help; - return 0; - } - if (!resultFile.empty() && files.size() > 1) { - std::cout << "Error: -o can not be used with multiple input files.\n"sv; - std::cout << help; - } - std::list>> results; - for (const auto& file : files) { - auto task = std::async(std::launch::async, [=]() { - std::ifstream input(file.first, std::ios::in); - if (input) { - std::string s( - (std::istreambuf_iterator(input)), - std::istreambuf_iterator()); - if (dumpCompileTime) { - auto start = std::chrono::high_resolution_clock::now(); - auto result = MoonP::MoonCompiler{MOONP_ARGS}.compile(s, config); - auto end = std::chrono::high_resolution_clock::now(); - if (!result.codes.empty()) { - std::chrono::duration diff = end - start; - start = std::chrono::high_resolution_clock::now(); - MoonP::MoonParser{}.parse(s); - end = std::chrono::high_resolution_clock::now(); - std::chrono::duration parseDiff = end - start; - std::ostringstream buf; - buf << file.first << " \n"sv; - buf << "Parse time: "sv << std::setprecision(5) << parseDiff.count() * 1000 << " ms\n"; - buf << "Compile time: "sv << std::setprecision(5) << (diff.count() - parseDiff.count()) * 1000 << " ms\n\n"; - return std::tuple{0, file.first, buf.str()}; - } else { - std::ostringstream buf; - buf << "Fail to compile: "sv << file.first << ".\n"sv; - buf << result.error << '\n'; - return std::tuple{1, file.first, buf.str()}; - } - } - auto result = MoonP::MoonCompiler{MOONP_ARGS}.compile(s, config); - if (result.error.empty()) { - if (!writeToFile) { - return std::tuple{0, file.first, result.codes + '\n'}; - } else { - std::string targetExtension("lua"sv); - if (result.options) { - auto it = result.options->find("target_extension"); - if (it != result.options->end()) { - targetExtension = it->second; - } - } - fs::path targetFile; - if (!resultFile.empty()) { - targetFile = resultFile; - } else { - if (!targetPath.empty()) { - targetFile = fs::path(targetPath) / file.second; - } else { - targetFile = file.first; - } - targetFile.replace_extension('.' + targetExtension); - } - if (!targetPath.empty()) { - fs::create_directories(targetFile.parent_path()); - } - std::ofstream output(targetFile, std::ios::trunc | std::ios::out); - if (output) { - const auto& codes = result.codes; - if (config.reserveLineNumber) { - auto head = std::string("-- [moonp]: "sv) + file.first + '\n'; - output.write(head.c_str(), head.size()); - } - output.write(codes.c_str(), codes.size()); - return std::tuple{0, targetFile.string(), std::string("Built "sv) + file.first + '\n'}; - } else { - return std::tuple{1, std::string(), std::string("Fail to write file: "sv) + targetFile.string() + '\n'}; - } - } - } else { - std::ostringstream buf; - buf << "Fail to compile: "sv << file.first << ".\n"; - buf << result.error << '\n'; - return std::tuple{1, std::string(), buf.str()}; - } - } else { - return std::tuple{1, std::string(), std::string("Fail to read file: "sv) + file.first + ".\n"}; - } - }); - results.push_back(std::move(task)); - } - int ret = 0; -#ifndef MOONP_COMPILER_ONLY - lua_State* L = nullptr; - DEFER({ - if (L) lua_close(L); - }); - if (minify) { - L = luaL_newstate(); - luaL_openlibs(L); - pushLuaminify(L); - } -#endif // MOONP_COMPILER_ONLY - std::list errs; - for (auto& result : results) { - int val = 0; - std::string file; - std::string msg; - std::tie(val, file, msg) = result.get(); - if (val != 0) { - ret = val; - errs.push_back(msg); - } else { -#ifndef MOONP_COMPILER_ONLY - if (minify) { - std::ifstream input(file, std::ios::in); - if (input) { - std::string s; - if (writeToFile) { - s = std::string( - (std::istreambuf_iterator(input)), - std::istreambuf_iterator()); - } else { - s = msg; - } - input.close(); - int top = lua_gettop(L); - DEFER(lua_settop(L, top)); - lua_pushvalue(L, -1); - lua_pushlstring(L, s.c_str(), s.size()); - if (lua_pcall(L, 1, 1, 0) != 0) { - ret = 2; - std::string err = lua_tostring(L, -1); - errs.push_back(std::string("Fail to minify: "sv) + file + '\n' + err + '\n'); - } else { - size_t size = 0; - const char* minifiedCodes = lua_tolstring(L, -1, &size); - if (writeToFile) { - std::ofstream output(file, std::ios::trunc | std::ios::out); - output.write(minifiedCodes, size); - output.close(); - std::cout << "Minified built "sv << file << '\n'; - } else { - std::cout << minifiedCodes << '\n'; - } - } - } else { - ret = 2; - errs.push_back(std::string("Fail to minify: "sv) + file + '\n'); - } - } else { - std::cout << msg; - } -#else - std::cout << msg; -#endif // MOONP_COMPILER_ONLY - } - } - for (const auto& err : errs) { - std::cout << err; - } - return ret; -} diff --git a/src/yue.cpp b/src/yue.cpp new file mode 100644 index 0000000..b5f91ff --- /dev/null +++ b/src/yue.cpp @@ -0,0 +1,570 @@ +/* Copyright (c) 2021 Jin Li, http://www.luvfight.me + +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. */ + +#include "yuescript/yue_compiler.h" +#include "yuescript/yue_parser.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std::string_view_literals; +#include "ghc/fs_std.hpp" +#include "linenoise.hpp" + +#if not (defined YUE_NO_MACRO && defined YUE_COMPILER_ONLY) +#define _DEFER(code,line) std::shared_ptr _defer_##line(nullptr, [&](auto){code;}) +#define DEFER(code) _DEFER(code,__LINE__) +extern "C" { +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" +int luaopen_yue(lua_State* L); +} // extern "C" + +static void openlibs(void* state) { + lua_State* L = static_cast(state); + luaL_openlibs(L); + luaopen_yue(L); +} + +void pushYue(lua_State* L, std::string_view name) { + lua_getglobal(L, "package"); // package + lua_getfield(L, -1, "loaded"); // package loaded + lua_getfield(L, -1, "yue"); // package loaded yue + lua_pushlstring(L, &name.front(), name.size()); // package loaded yue name + lua_gettable(L, -2); // loaded[name], package loaded yue item + lua_insert(L, -4); // item package loaded yue + lua_pop(L, 3); // item +} + +void pushOptions(lua_State* L, int lineOffset) { + lua_newtable(L); + lua_pushliteral(L, "lint_global"); + lua_pushboolean(L, 0); + lua_rawset(L, -3); + lua_pushliteral(L, "implicit_return_root"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); + lua_pushliteral(L, "reserve_line_number"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); + lua_pushliteral(L, "space_over_tab"); + lua_pushboolean(L, 0); + lua_rawset(L, -3); + lua_pushliteral(L, "same_module"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); + lua_pushliteral(L, "line_offset"); + lua_pushinteger(L, lineOffset); + lua_rawset(L, -3); +} +#endif // not (defined YUE_NO_MACRO && defined YUE_COMPILER_ONLY) + +#ifndef YUE_NO_MACRO +#define YUE_ARGS nullptr,openlibs +#else +#define YUE_ARGS +#endif // YUE_NO_MACRO + +#ifndef YUE_COMPILER_ONLY +static const char luaminifyCodes[] = +#include "LuaMinify.h" + +static void pushLuaminify(lua_State* L) { + if (luaL_loadbuffer(L, luaminifyCodes, sizeof(luaminifyCodes) / sizeof(luaminifyCodes[0]) - 1, "=(luaminify)") != 0) { + std::string err = std::string("failed to load luaminify module.\n") + lua_tostring(L, -1); + luaL_error(L, err.c_str()); + } else if (lua_pcall(L, 0, 1, 0) != 0) { + std::string err = std::string("failed to init luaminify module.\n") + lua_tostring(L, -1); + luaL_error(L, err.c_str()); + } +} +#endif // YUE_COMPILER_ONLY + +int main(int narg, const char** args) { + const char* help = +"Usage: yue [options|files|directories] ...\n\n" +" -h Print this message\n" +#ifndef YUE_COMPILER_ONLY +" -e str Execute a file or raw codes\n" +" -m Generate minified codes\n" +#endif // YUE_COMPILER_ONLY +" -t path Specify where to place compiled files\n" +" -o file Write output to file\n" +" -s Use spaces in generated codes instead of tabs\n" +" -p Write output to standard out\n" +" -b Dump compile time (doesn't write output)\n" +" -l Write line numbers from source codes\n" +" -v Print version\n" +#ifndef YUE_COMPILER_ONLY +" -- Read from standard in, print to standard out\n" +" (Must be first and only argument)\n\n" +" Execute without options to enter REPL, type symbol '$'\n" +" in a single line to start/stop multi-line mode\n" +#endif // YUE_COMPILER_ONLY +; +#ifndef YUE_COMPILER_ONLY + if (narg == 1) { + lua_State* L = luaL_newstate(); + openlibs(L); + DEFER(lua_close(L)); + pushYue(L, "insert_loader"sv); + if (lua_pcall(L, 0, 0, 0) != 0) { + std::cout << lua_tostring(L, -1) << '\n'; + return 1; + } + int count = 0; + linenoise::SetMultiLine(false); + linenoise::SetCompletionCallback([](const char* editBuffer, std::vector& completions) { + std::string buf = editBuffer; + std::string tmp = buf; + yue::Utils::trim(tmp); + if (tmp.empty()) return; + std::string pre; + auto pos = buf.find_first_not_of(" \t\n"); + if (pos != std::string::npos) { + pre = buf.substr(0, pos); + } + switch (tmp[0]) { + case 'b': + completions.push_back(pre + "break"); + break; + case 'c': + completions.push_back(pre + "class "); + completions.push_back(pre + "continue"); + break; + case 'e': + completions.push_back(pre + "else"); + completions.push_back(pre + "export "); + break; + case 'i': + completions.push_back(pre + "import \""); + break; + case 'g': + completions.push_back(pre + "global "); + break; + case 'l': + completions.push_back(pre + "local "); + break; + case 'm': + completions.push_back(pre + "macro expr "); + completions.push_back(pre + "macro block "); + completions.push_back(pre + "macro lua "); + break; + case 's': + completions.push_back(pre + "switch "); + break; + case 'u': + completions.push_back(pre + "unless "); + break; + case 'w': + completions.push_back(pre + "with "); + completions.push_back(pre + "when "); + break; + } + }); + std::cout << "Yuescript "sv << yue::version << '\n'; + while (true) { + count++; + std::string codes; + bool quit = linenoise::Readline("> ", codes); + if (quit) return 0; + linenoise::AddHistory(codes.c_str()); + yue::Utils::trim(codes); + if (codes == "$"sv) { + codes.clear(); + for (std::string line; !(quit = linenoise::Readline("", line));) { + auto temp = line; + yue::Utils::trim(temp); + if (temp == "$"sv) { + break; + } + codes += '\n'; + codes += line; + linenoise::AddHistory(line.c_str()); + yue::Utils::trim(codes); + } + if (quit) return 0; + } + codes.insert(0, "global *\n"sv); + int top = lua_gettop(L); + DEFER(lua_settop(L, top)); + pushYue(L, "loadstring"sv); + lua_pushlstring(L, codes.c_str(), codes.size()); + lua_pushstring(L, (std::string("=(repl ") + std::to_string(count) + ')').c_str()); + pushOptions(L, -1); + const std::string_view Err = "\033[35m"sv, Val = "\033[33m"sv, Stop = "\033[0m\n"sv; + if (lua_pcall(L, 3, 2, 0) != 0) { + std::cout << Err << lua_tostring(L, -1) << Stop; + continue; + } + if (lua_isnil(L, -2) != 0) { + std::string err = lua_tostring(L, -1); + auto modName = std::string("(repl "sv) + std::to_string(count) + "):"; + if (err.substr(0, modName.size()) == modName) { + err = err.substr(modName.size()); + } + auto pos = err.find(':'); + if (pos != std::string::npos) { + int lineNum = std::stoi(err.substr(0, pos)); + err = std::to_string(lineNum - 1) + err.substr(pos); + } + std::cout << Err << err << Stop; + continue; + } + lua_pop(L, 1); + pushYue(L, "pcall"sv); + lua_insert(L, -2); + int last = lua_gettop(L) - 2; + if (lua_pcall(L, 1, LUA_MULTRET, 0) != 0) { + std::cout << Err << lua_tostring(L, -1) << Stop; + continue; + } + int cur = lua_gettop(L); + int retCount = cur - last; + bool success = lua_toboolean(L, -retCount) != 0; + if (success) { + if (retCount > 1) { + for (int i = 1; i < retCount; ++i) { + std::cout << Val << luaL_tolstring(L, -retCount + i, nullptr) << Stop; + lua_pop(L, 1); + } + } + } else { + std::cout << Err << lua_tostring(L, -1) << Stop; + } + } + std::cout << '\n'; + return 0; + } + bool minify = false; +#endif // YUE_COMPILER_ONLY + yue::YueConfig config; + config.implicitReturnRoot = true; + config.lintGlobalVariable = false; + config.reserveLineNumber = false; + config.useSpaceOverTab = false; + bool writeToFile = true; + bool dumpCompileTime = false; + std::string targetPath; + std::string resultFile; + std::list> files; + for (int i = 1; i < narg; ++i) { + std::string arg = args[i]; + if (arg == "--"sv) { + if (i != 1) { + std::cout << help; + return 1; + } + char ch; + std::string codes; + while ((ch = std::cin.get()) != EOF) { + codes += ch; + } + yue::YueConfig conf; + conf.implicitReturnRoot = true; + conf.lintGlobalVariable = false; + conf.reserveLineNumber = false; + conf.useSpaceOverTab = true; + auto result = yue::YueCompiler{YUE_ARGS}.compile(codes, conf); + if (result.error.empty()) { + std::cout << result.codes; + return 0; + } else { + std::ostringstream buf; + std::cout << result.error << '\n'; + return 1; + } +#ifndef YUE_COMPILER_ONLY + } else if (arg == "-e"sv) { + ++i; + if (i < narg) { + lua_State* L = luaL_newstate(); + openlibs(L); + DEFER(lua_close(L)); + pushYue(L, "insert_loader"sv); + if (lua_pcall(L, 0, 0, 0) != 0) { + std::cout << lua_tostring(L, -1) << '\n'; + return 1; + } + std::string evalStr = args[i]; + std::ifstream input(evalStr, std::ios::in); + if (input) { + auto ext = fs::path(evalStr).extension().string(); + for (auto& ch : ext) ch = std::tolower(ch); + if (ext == ".lua") { + lua_getglobal(L, "load"); + } else { + pushYue(L, "loadstring"sv); + } + std::string s( + (std::istreambuf_iterator(input)), + std::istreambuf_iterator()); + lua_pushlstring(L, s.c_str(), s.size()); + lua_pushlstring(L, evalStr.c_str(), evalStr.size()); + } else { + pushYue(L, "loadstring"sv); + lua_pushlstring(L, evalStr.c_str(), evalStr.size()); + lua_pushliteral(L, "=(eval str)"); + } + if (lua_pcall(L, 2, 2, 0) != 0) { + std::cout << lua_tostring(L, -1) << '\n'; + return 1; + } + if (lua_isnil(L, -2) != 0) { + std::cout << lua_tostring(L, -1) << '\n'; + return 1; + } + lua_pop(L, 1); + pushYue(L, "pcall"sv); + lua_insert(L, -2); + int argCount = 0; + i++; + while (i < narg) { + argCount++; + lua_pushstring(L, args[i]); + i++; + } + if (lua_pcall(L, 1 + argCount, 2, 0) != 0) { + std::cout << lua_tostring(L, -1) << '\n'; + return 1; + } + bool success = lua_toboolean(L, -2) != 0; + if (!success) { + std::cout << lua_tostring(L, -1) << '\n'; + return 1; + } + return 0; + } else { + std::cout << help; + return 1; + } + } else if (arg == "-m"sv) { + minify = true; +#endif // YUE_COMPILER_ONLY + } else if (arg == "-s"sv) { + config.useSpaceOverTab = true; + } else if (arg == "-l"sv) { + config.reserveLineNumber = true; + } else if (arg == "-p"sv) { + writeToFile = false; + } else if (arg == "-t"sv) { + ++i; + if (i < narg) { + targetPath = args[i]; + } else { + std::cout << help; + return 1; + } + } else if (arg == "-b"sv) { + dumpCompileTime = true; + } else if (arg == "-h"sv) { + std::cout << help; + return 0; + } else if (arg == "-v"sv) { + std::cout << "Yuescript version: "sv << yue::version << '\n'; + return 0; + } else if (arg == "-o"sv) { + ++i; + if (i < narg) { + resultFile = args[i]; + } else { + std::cout << help; + return 1; + } + } else if (arg.substr(0, 1) == "-"sv && arg.find('=') != std::string::npos) { + auto argStr = arg.substr(1); + size_t idx = argStr.find('='); + auto key = argStr.substr(0, idx); + auto value = argStr.substr(idx + 1); + config.options[key] = value; + } else { + if (fs::is_directory(arg)) { + for (auto item : fs::recursive_directory_iterator(arg)) { + if (!item.is_directory()) { + auto ext = item.path().extension().string(); + for (char& ch : ext) ch = std::tolower(ch); + if (!ext.empty() && ext.substr(1) == yue::extension) { + files.emplace_back(item.path().string(), item.path().lexically_relative(arg).string()); + } + } + } + } else { + files.emplace_back(arg, arg); + } + } + } + if (files.empty()) { + std::cout << help; + return 0; + } + if (!resultFile.empty() && files.size() > 1) { + std::cout << "Error: -o can not be used with multiple input files.\n"sv; + std::cout << help; + } + std::list>> results; + for (const auto& file : files) { + auto task = std::async(std::launch::async, [=]() { + std::ifstream input(file.first, std::ios::in); + if (input) { + std::string s( + (std::istreambuf_iterator(input)), + std::istreambuf_iterator()); + if (dumpCompileTime) { + auto start = std::chrono::high_resolution_clock::now(); + auto result = yue::YueCompiler{YUE_ARGS}.compile(s, config); + auto end = std::chrono::high_resolution_clock::now(); + if (!result.codes.empty()) { + std::chrono::duration diff = end - start; + start = std::chrono::high_resolution_clock::now(); + yue::YueParser{}.parse(s); + end = std::chrono::high_resolution_clock::now(); + std::chrono::duration parseDiff = end - start; + std::ostringstream buf; + buf << file.first << " \n"sv; + buf << "Parse time: "sv << std::setprecision(5) << parseDiff.count() * 1000 << " ms\n"; + buf << "Compile time: "sv << std::setprecision(5) << (diff.count() - parseDiff.count()) * 1000 << " ms\n\n"; + return std::tuple{0, file.first, buf.str()}; + } else { + std::ostringstream buf; + buf << "Fail to compile: "sv << file.first << ".\n"sv; + buf << result.error << '\n'; + return std::tuple{1, file.first, buf.str()}; + } + } + auto result = yue::YueCompiler{YUE_ARGS}.compile(s, config); + if (result.error.empty()) { + if (!writeToFile) { + return std::tuple{0, file.first, result.codes + '\n'}; + } else { + std::string targetExtension("lua"sv); + if (result.options) { + auto it = result.options->find("target_extension"); + if (it != result.options->end()) { + targetExtension = it->second; + } + } + fs::path targetFile; + if (!resultFile.empty()) { + targetFile = resultFile; + } else { + if (!targetPath.empty()) { + targetFile = fs::path(targetPath) / file.second; + } else { + targetFile = file.first; + } + targetFile.replace_extension('.' + targetExtension); + } + if (!targetPath.empty()) { + fs::create_directories(targetFile.parent_path()); + } + std::ofstream output(targetFile, std::ios::trunc | std::ios::out); + if (output) { + const auto& codes = result.codes; + if (config.reserveLineNumber) { + auto head = std::string("-- [yue]: "sv) + file.first + '\n'; + output.write(head.c_str(), head.size()); + } + output.write(codes.c_str(), codes.size()); + return std::tuple{0, targetFile.string(), std::string("Built "sv) + file.first + '\n'}; + } else { + return std::tuple{1, std::string(), std::string("Fail to write file: "sv) + targetFile.string() + '\n'}; + } + } + } else { + std::ostringstream buf; + buf << "Fail to compile: "sv << file.first << ".\n"; + buf << result.error << '\n'; + return std::tuple{1, std::string(), buf.str()}; + } + } else { + return std::tuple{1, std::string(), std::string("Fail to read file: "sv) + file.first + ".\n"}; + } + }); + results.push_back(std::move(task)); + } + int ret = 0; +#ifndef YUE_COMPILER_ONLY + lua_State* L = nullptr; + DEFER({ + if (L) lua_close(L); + }); + if (minify) { + L = luaL_newstate(); + luaL_openlibs(L); + pushLuaminify(L); + } +#endif // YUE_COMPILER_ONLY + std::list errs; + for (auto& result : results) { + int val = 0; + std::string file; + std::string msg; + std::tie(val, file, msg) = result.get(); + if (val != 0) { + ret = val; + errs.push_back(msg); + } else { +#ifndef YUE_COMPILER_ONLY + if (minify) { + std::ifstream input(file, std::ios::in); + if (input) { + std::string s; + if (writeToFile) { + s = std::string( + (std::istreambuf_iterator(input)), + std::istreambuf_iterator()); + } else { + s = msg; + } + input.close(); + int top = lua_gettop(L); + DEFER(lua_settop(L, top)); + lua_pushvalue(L, -1); + lua_pushlstring(L, s.c_str(), s.size()); + if (lua_pcall(L, 1, 1, 0) != 0) { + ret = 2; + std::string err = lua_tostring(L, -1); + errs.push_back(std::string("Fail to minify: "sv) + file + '\n' + err + '\n'); + } else { + size_t size = 0; + const char* minifiedCodes = lua_tolstring(L, -1, &size); + if (writeToFile) { + std::ofstream output(file, std::ios::trunc | std::ios::out); + output.write(minifiedCodes, size); + output.close(); + std::cout << "Minified built "sv << file << '\n'; + } else { + std::cout << minifiedCodes << '\n'; + } + } + } else { + ret = 2; + errs.push_back(std::string("Fail to minify: "sv) + file + '\n'); + } + } else { + std::cout << msg; + } +#else + std::cout << msg; +#endif // YUE_COMPILER_ONLY + } + } + for (const auto& err : errs) { + std::cout << err; + } + return ret; +} diff --git a/src/yuescript/ast.cpp b/src/yuescript/ast.cpp new file mode 100644 index 0000000..3deefb1 --- /dev/null +++ b/src/yuescript/ast.cpp @@ -0,0 +1,147 @@ +/* Copyright (c) 2012, Achilleas Margaritis, modified by Jin Li +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/ + +#include + +#include "yuescript/ast.hpp" + + +namespace parserlib { + + +traversal ast_node::traverse(const std::function& func) { + return func(this); +} + +ast_node* ast_node::getByTypeIds(int* begin, int* end) { + ast_node* current = this; + auto it = begin; + while (it != end) { + ast_node* findNode = nullptr; + int i = *it; + current->visitChild([&](ast_node* node) { + if (node->getId() == i) { + findNode = node; + return true; + } + return false; + }); + if (findNode) { + current = findNode; + } else { + current = nullptr; + break; + } + ++it; + } + return current; +} + +bool ast_node::visitChild(const std::function&) { + return false; +} + + +/** Asks all members to construct themselves from the stack. + The members are asked to construct themselves in reverse order. + from a node stack. + @param st stack. +*/ +void ast_container::construct(ast_stack &st) { + for(ast_member_vector::reverse_iterator it = m_members.rbegin(); + it != m_members.rend(); + ++it) + { + ast_member* member = *it; + member->construct(st); + } +} + +traversal ast_container::traverse(const std::function& func) { + traversal action = func(this); + switch (action) { + case traversal::Stop: return traversal::Stop; + case traversal::Return: return traversal::Continue; + default: break; + } + const auto& members = this->members(); + for (auto member : members) { + switch (member->get_type()) { + case ast_holder_type::Pointer: { + _ast_ptr* ptr = static_cast<_ast_ptr*>(member); + if (ptr->get() && ptr->get()->traverse(func) == traversal::Stop) { + return traversal::Stop; + } + break; + } + case ast_holder_type::List: { + _ast_list* list = static_cast<_ast_list*>(member); + for (auto obj : list->objects()) { + if (obj->traverse(func) == traversal::Stop) { + return traversal::Stop; + } + } + break; + } + } + } + return traversal::Continue; +} + +bool ast_container::visitChild(const std::function& func) { + const auto& members = this->members(); + for (auto member : members) { + switch (member->get_type()) { + case ast_holder_type::Pointer: { + _ast_ptr* ptr = static_cast<_ast_ptr*>(member); + if (ptr->get()) { + if (func(ptr->get())) return true; + } + break; + } + case ast_holder_type::List: { + _ast_list* list = static_cast<_ast_list*>(member); + for (auto obj : list->objects()) { + if (obj) { + if (func(obj)) return true; + } + } + break; + } + } + } + return false; +} + + +/** parses the given input. + @param i input. + @param g root rule of grammar. + @param el list of errors. + @param ud user data, passed to the parse procedures. + @return pointer to ast node created, or null if there was an error. + The return object must be deleted by the caller. +*/ +ast_node* parse(input& i, rule& g, error_list& el, void* ud) { + ast_stack st; + if (!parse(i, g, el, &st, ud)) { + for (auto node : st) { + delete node; + } + st.clear(); + return nullptr; + } + assert(st.size() == 1); + return st.front(); +} + + +} //namespace parserlib diff --git a/src/yuescript/ast.hpp b/src/yuescript/ast.hpp new file mode 100644 index 0000000..c88fcf9 --- /dev/null +++ b/src/yuescript/ast.hpp @@ -0,0 +1,571 @@ +/* Copyright (c) 2012, Achilleas Margaritis, modified by Jin Li +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/ + +#pragma once + + +#include +#include +#include +#include + +#include "yuescript/parser.hpp" + + +namespace parserlib { + + +class ast_node; +template class ast_ptr; +template class ast_list; +template class ast; + + +/** type of AST node stack. +*/ +typedef std::vector ast_stack; +typedef std::list node_container; + + +template struct Counter { enum { value = Counter::value }; }; +template<> struct Counter<0> { enum { value = 0 }; }; + +#define COUNTER_READ Counter<__LINE__>::value +#define COUNTER_INC template<> struct Counter<__LINE__> { enum { value = Counter<__LINE__-1>::value + 1}; } + +class ast_node; +template +constexpr typename std::enable_if::value,int>::type +id(); + +enum class traversal { + Continue, + Return, + Stop +}; + +/** Base class for AST nodes. +*/ +class ast_node : public input_range { +public: + ast_node() : _ref(0) {} + + void retain() { + ++_ref; + } + + void release() { + --_ref; + if (_ref == 0) { + delete this; + } + } + + /** interface for filling the contents of the node + from a node stack. + @param st stack. + */ + virtual void construct(ast_stack&) {} + + /** interface for visiting AST tree use. + */ + virtual traversal traverse(const std::function& func); + + template + struct select_last { + using type = typename decltype((std::enable_if{}, ...))::type; + }; + template + using select_last_t = typename select_last::type; + + template + select_last_t* getByPath() { + int types[] = {id()...}; + return static_cast*>(getByTypeIds(std::begin(types), std::end(types))); + } + + virtual bool visitChild(const std::function& func); + + virtual int getId() const = 0; + + template + inline ast_ptr new_ptr() const { + auto item = new T; + item->m_begin.m_line = m_begin.m_line; + item->m_end.m_line = m_begin.m_line; + return ast_ptr(item); + } +private: + int _ref; + ast_node* getByTypeIds(int* begin, int* end); +}; + +template +constexpr typename std::enable_if::value,int>::type +id() { return 0; } + +template +T* ast_cast(ast_node* node) { + return node && id() == node->getId() ? static_cast(node) : nullptr; +} + +template +T* ast_to(ast_node* node) { + assert(node->getId() == id()); + return static_cast(node); +} + +template +bool ast_is(ast_node* node) { + if (!node) return false; + bool result = false; + int i = node->getId(); + using swallow = bool[]; + (void)swallow{result || (result = id() == i)...}; + return result; +} + +class ast_member; + +/** type of ast member vector. +*/ +typedef std::vector ast_member_vector; + + +/** base class for AST nodes with children. +*/ +class ast_container : public ast_node { +public: + void add_members(std::initializer_list members) { + for (auto member : members) { + m_members.push_back(member); + } + } + + /** returns the vector of AST members. + @return the vector of AST members. + */ + const ast_member_vector& members() const { + return m_members; + } + + /** Asks all members to construct themselves from the stack. + The members are asked to construct themselves in reverse order. + from a node stack. + @param st stack. + */ + virtual void construct(ast_stack& st) override; + + virtual traversal traverse(const std::function& func) override; + + virtual bool visitChild(const std::function& func) override; +private: + ast_member_vector m_members; + + friend class ast_member; +}; + +enum class ast_holder_type { + Pointer, + List +}; + +/** Base class for children of ast_container. +*/ +class ast_member { +public: + virtual ~ast_member() {} + + /** interface for filling the the member from a node stack. + @param st stack. + */ + virtual void construct(ast_stack& st) = 0; + + virtual bool accept(ast_node* node) = 0; + + virtual ast_holder_type get_type() const = 0; +}; + + +class _ast_ptr : public ast_member { +public: + _ast_ptr(ast_node* node) : m_ptr(node) { + if (node) node->retain(); + } + + virtual ~_ast_ptr() { + if (m_ptr) { + m_ptr->release(); + m_ptr = nullptr; + } + } + + ast_node* get() const { + return m_ptr; + } + + template + T* as() const { + return ast_cast(m_ptr); + } + + template + T* to() const { + assert(m_ptr && m_ptr->getId() == id()); + return static_cast(m_ptr); + } + + template + bool is() const { + return m_ptr && m_ptr->getId() == id(); + } + + void set(ast_node* node) { + if (node == m_ptr) { + return; + } else if (!node) { + if (m_ptr) m_ptr->release(); + m_ptr = nullptr; + } else { + assert(accept(node)); + if (m_ptr) m_ptr->release(); + m_ptr = node; + node->retain(); + } + } + + virtual ast_holder_type get_type() const override { + return ast_holder_type::Pointer; + } +protected: + ast_node* m_ptr; +}; + +/** pointer to an AST object. + It assumes ownership of the object. + It pops an object of the given type from the stack. + @tparam Required if true, the object is required. + @tparam T type of object to control. +*/ +template class ast_ptr : public _ast_ptr { +public: + ast_ptr(T* node = nullptr) : _ast_ptr(node) {} + + ast_ptr(const ast_ptr& other) : _ast_ptr(other.get()) {} + + ast_ptr& operator=(const ast_ptr& other) { + set(other.get()); + return *this; + } + + /** gets the underlying ptr value. + @return the underlying ptr value. + */ + T* get() const { + return static_cast(m_ptr); + } + + /** auto conversion to the underlying object ptr. + @return the underlying ptr value. + */ + operator T*() const { + return static_cast(m_ptr); + } + + /** member access. + @return the underlying ptr value. + */ + T* operator->() const { + assert(m_ptr); + return static_cast(m_ptr); + } + + /** Pops a node from the stack. + @param st stack. + @exception std::logic_error thrown if the node is not of the appropriate type; + thrown only if Required == true or if the stack is empty. + */ + virtual void construct(ast_stack& st) override { + // check the stack node + if (st.empty()) { + if (!Required) return; + throw std::logic_error("Invalid AST stack."); + } + ast_node* node = st.back(); + if (!ast_ptr::accept(node)) { + // if the object is not required, simply return + if (!Required) return; + // else if the object is mandatory, throw an exception + throw std::logic_error("Invalid AST node."); + } + st.pop_back(); + m_ptr = node; + node->retain(); + } +private: + virtual bool accept(ast_node* node) override { + return node && (std::is_same() || id() == node->getId()); + } +}; + +template class ast_sel : public _ast_ptr { +public: + ast_sel() : _ast_ptr(nullptr) {} + + ast_sel(const ast_sel& other) : _ast_ptr(other.get()) {} + + ast_sel& operator=(const ast_sel& other) { + set(other.get()); + return *this; + } + + operator ast_node*() const { + return m_ptr; + } + + ast_node* operator->() const { + assert(m_ptr); + return m_ptr; + } + + virtual void construct(ast_stack& st) override { + if (st.empty()) { + if (!Required) return; + throw std::logic_error("Invalid AST stack."); + } + ast_node* node = st.back(); + if (!ast_sel::accept(node)) { + if (!Required) return; + throw std::logic_error("Invalid AST node."); + } + st.pop_back(); + m_ptr = node; + node->retain(); + } +private: + virtual bool accept(ast_node* node) override { + if (!node) return false; + using swallow = bool[]; + bool result = false; + (void)swallow{result || (result = id() == node->getId())...}; + return result; + } +}; + +class _ast_list : public ast_member { +public: + ~_ast_list() { + clear(); + } + + inline ast_node* back() const { + return m_objects.back(); + } + + inline ast_node* front() const { + return m_objects.front(); + } + + inline size_t size() const { + return m_objects.size(); + } + + inline bool empty() const { + return m_objects.empty(); + } + + void push_back(ast_node* node) { + assert(node && accept(node)); + m_objects.push_back(node); + node->retain(); + } + + void push_front(ast_node* node) { + assert(node && accept(node)); + m_objects.push_front(node); + node->retain(); + } + + void pop_front() { + auto node = m_objects.front(); + m_objects.pop_front(); + node->release(); + } + + void pop_back() { + auto node = m_objects.back(); + m_objects.pop_back(); + node->release(); + } + + bool swap(ast_node* node, ast_node* other) { + for (auto it = m_objects.begin(); it != m_objects.end(); ++it) { + if (*it == node) { + *it = other; + other->retain(); + node->release(); + return true; + } + } + return false; + } + + const node_container& objects() const { + return m_objects; + } + + void clear() { + for(ast_node* obj : m_objects) { + if (obj) obj->release(); + } + m_objects.clear(); + } + + void dup(const _ast_list& src) { + for(ast_node* obj : src.m_objects) { + m_objects.push_back(obj); + obj->retain(); + } + } + + virtual ast_holder_type get_type() const override { + return ast_holder_type::List; + } +protected: + node_container m_objects; +}; + +/** A list of objects. + It pops objects of the given type from the ast stack, until no more objects can be popped. + It assumes ownership of objects. + @tparam Required if true, the object is required. + @tparam T type of object to control. +*/ +template class ast_list : public _ast_list { +public: + ast_list() { } + + ast_list(const ast_list& other) { + dup(other); + } + + ast_list& operator=(const ast_list& other) { + clear(); + dup(other); + return *this; + } + + /** Pops objects of type T from the stack until no more objects can be popped. + @param st stack. + */ + virtual void construct(ast_stack &st) override { + while (!st.empty()) { + ast_node* node = st.back(); + // if the object was not not of the appropriate type, + // end the list parsing + if (!ast_list::accept(node)) { + if (Required && m_objects.empty()) { + throw std::logic_error("Invalid AST node."); + } + return; + } + st.pop_back(); + // insert the object in the list, in reverse order + m_objects.push_front(node); + node->retain(); + } + if (Required && m_objects.empty()) { + throw std::logic_error("Invalid AST stack."); + } + } +private: + virtual bool accept(ast_node* node) override { + return node && (std::is_same() || id() == node->getId()); + } +}; + +template class ast_sel_list : public _ast_list { +public: + ast_sel_list() { } + + ast_sel_list(const ast_sel_list& other) { + dup(other); + } + + ast_sel_list& operator=(const ast_sel_list& other) { + clear(); + dup(other); + return *this; + } + + virtual void construct(ast_stack &st) override { + while (!st.empty()) { + ast_node* node = st.back(); + if (!ast_sel_list::accept(node)) { + if (Required && m_objects.empty()) { + throw std::logic_error("Invalid AST node."); + } + return; + } + st.pop_back(); + m_objects.push_front(node); + node->retain(); + } + if (Required && m_objects.empty()) { + throw std::logic_error("Invalid AST stack."); + } + } +private: + virtual bool accept(ast_node* node) override { + if (!node) return false; + using swallow = bool[]; + bool result = false; + (void)swallow{result || (result = id() == node->getId())...}; + return result; + } +}; + +/** AST function which creates an object of type T + and pushes it to the node stack. +*/ +template class ast { +public: + /** constructor. + @param r rule to attach the AST function to. + */ + ast(rule& r) { + r.set_parse_proc(&_parse_proc); + } +private: + //parse proc + static void _parse_proc(const pos& b, const pos& e, void* d) { + ast_stack* st = reinterpret_cast(d); + T* obj = new T; + obj->m_begin = b; + obj->m_end = e; + obj->construct(*st); + st->push_back(obj); + } +}; + + +/** parses the given input. + @param i input. + @param g root rule of grammar. + @param el list of errors. + @param ud user data, passed to the parse procedures. + @return pointer to ast node created, or null if there was an error. + The return object must be deleted by the caller. +*/ +ast_node* parse(input& i, rule& g, error_list& el, void* ud); + + +} //namespace parserlib diff --git a/src/yuescript/parser.cpp b/src/yuescript/parser.cpp new file mode 100644 index 0000000..a44ae34 --- /dev/null +++ b/src/yuescript/parser.cpp @@ -0,0 +1,1412 @@ +/* Copyright (c) 2012, Achilleas Margaritis, modified by Jin Li +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/ + +#include +#include +#include +#include +#include +#include + +#include "yuescript/parser.hpp" + + +namespace parserlib { + + +//internal private class that manages access to the public classes' internals. +class _private { +public: + //get the internal expression object from the expression. + static _expr* get_expr(const expr& e) { + return e.m_expr; + } + + //create new expression from given expression + static expr construct_expr(_expr* e) { + return e; + } + + //get the internal expression object from the rule. + static _expr* get_expr(rule& r) { + return r.m_expr; + } + + //get the internal parse proc from the rule. + static parse_proc get_parse_proc(rule& r) { + return r.m_parse_proc; + } +}; + + +class _context; + + +//parser state +class _state { +public: + //position + pos m_pos; + + //size of match vector + size_t m_matches; + + //constructor + _state(_context& con); +}; + + +//match +class _match { +public: + //rule matched + rule* m_rule; + + //begin position + pos m_begin; + + //end position + pos m_end; + + //null constructor + _match() {} + + //constructor from parameters + _match(rule* r, const pos& b, const pos& e) : + m_rule(r), + m_begin(b), + m_end(e) + { + } +}; + + +//match vector +typedef std::vector<_match> _match_vector; + + +//parsing context +class _context { +public: + //user data + void* m_user_data; + + //current position + pos m_pos; + + //error position + pos m_error_pos; + + //input begin + input::iterator m_begin; + + //input end + input::iterator m_end; + + //matches + _match_vector m_matches; + + //constructor + _context(input& i, void* ud) : + m_user_data(ud), + m_pos(i), + m_error_pos(i), + m_begin(i.begin()), + m_end(i.end()) + { + } + + //check if the end is reached + bool end() const { + return m_pos.m_it == m_end; + } + + //get the current symbol + input::value_type symbol() const { + assert(!end()); + return *m_pos.m_it; + } + + //set the longest possible error + void set_error_pos() { + if (m_pos.m_it > m_error_pos.m_it) { + m_error_pos = m_pos; + } + } + + //next column + void next_col() { + ++m_pos.m_it; + ++m_pos.m_col; + } + + //next line + void next_line() { + ++m_pos.m_line; + m_pos.m_col = 1; + } + + //restore the state + void restore(const _state& st) { + m_pos = st.m_pos; + m_matches.resize(st.m_matches); + } + + //parse non-term rule. + bool parse_non_term(rule& r); + + //parse term rule. + bool parse_term(rule& r); + + //execute all the parse procs + void do_parse_procs(void* d) const { + for(_match_vector::const_iterator it = m_matches.begin(); + it != m_matches.end(); + ++it) { + const _match &m = *it; + parse_proc p = _private::get_parse_proc(*m.m_rule); + p(m.m_begin, m.m_end, d); + } + } + +private: + //parse non-term rule. + bool _parse_non_term(rule& r); + + //parse term rule. + bool _parse_term(rule& r); +}; + + +//base class for expressions +class _expr { +public: + //destructor. + virtual ~_expr() { + } + + //parse with whitespace + virtual bool parse_non_term(_context& con) const = 0; + + //parse terminal + virtual bool parse_term(_context& con) const = 0; +}; + + +//single character expression. +class _char : public _expr { +public: + //constructor. + _char(char c) : + m_char(c) + { + } + + //parse with whitespace + virtual bool parse_non_term(_context& con) const { + return _parse(con); + } + + //parse terminal + virtual bool parse_term(_context& con) const { + return _parse(con); + } + +private: + //character + input::value_type m_char; + + //internal parse + bool _parse(_context& con) const { + if (!con.end()) { + input::value_type ch = con.symbol(); + if (ch == m_char) { + con.next_col(); + return true; + } + } + con.set_error_pos(); + return false; + } +}; + + +//string expression. +class _string : public _expr { +public: + //constructor from ansi string. + _string(const char* s) : + m_string(Converter{}.from_bytes(s)) + { + } + + //parse with whitespace + virtual bool parse_non_term(_context& con) const { + return _parse(con); + } + + //parse terminal + virtual bool parse_term(_context& con) const { + return _parse(con); + } + +private: + //string + input m_string; + + //parse the string + bool _parse(_context& con) const { + for(input::const_iterator it = m_string.begin(), + end = m_string.end();;) { + if (it == end) return true; + if (con.end()) break; + if (con.symbol() != *it) break; + ++it; + con.next_col(); + } + con.set_error_pos(); + return false; + } +}; + + +//set expression. +class _set : public _expr { +public: + //constructor from ansi string. + _set(const char* s) { + auto str = Converter{}.from_bytes(s); + for (auto ch : str) { + _add(ch); + } + } + + //constructor from range. + _set(int min, int max) { + assert(min >= 0); + assert(min <= max); + m_quick_set.resize((size_t)max + 1U); + for(; min <= max; ++min) { + m_quick_set[(size_t)min] = true; + } + } + + //parse with whitespace + virtual bool parse_non_term(_context& con) const { + return _parse(con); + } + + //parse terminal + virtual bool parse_term(_context& con) const { + return _parse(con); + } + +private: + //set is kept as an array of flags, for quick access + std::vector m_quick_set; + std::unordered_set m_large_set; + + //add character + void _add(size_t i) { + if (i <= m_quick_set.size() || i <= 255) { + if (i >= m_quick_set.size()) { + m_quick_set.resize(i + 1); + } + m_quick_set[i] = true; + } else { + m_large_set.insert(i); + } + } + + //internal parse + bool _parse(_context& con) const { + if (!con.end()) { + size_t ch = con.symbol(); + if (ch < m_quick_set.size()) { + if (m_quick_set[ch]) { + con.next_col(); + return true; + } + } else if (m_large_set.find(ch) != m_large_set.end()) { + con.next_col(); + return true; + } + } + con.set_error_pos(); + return false; + } +}; + + +//base class for unary expressions +class _unary : public _expr { +public: + //constructor. + _unary(_expr* e) : + m_expr(e) + { + } + + //destructor. + virtual ~_unary() { + delete m_expr; + } + +protected: + //expression + _expr *m_expr; +}; + + +//terminal +class _term : public _unary { +public: + //constructor. + _term(_expr* e) : + _unary(e) + { + } + + //parse with whitespace + virtual bool parse_non_term(_context& con) const { + return m_expr->parse_term(con); + } + + //parse terminal + virtual bool parse_term(_context& con) const { + return m_expr->parse_term(con); + } +}; + + +//user +class _user : public _unary { +public: + //constructor. + _user(_expr* e, const user_handler& callback) : + _unary(e), + m_handler(callback) + { + } + + //parse with whitespace + virtual bool parse_non_term(_context& con) const { + pos pos = con.m_pos; + if (m_expr->parse_non_term(con)) { + item_t item = {pos.m_it, con.m_pos.m_it, con.m_user_data}; + return m_handler(item); + } + return false; + } + + //parse terminal + virtual bool parse_term(_context& con) const { + pos pos = con.m_pos; + if (m_expr->parse_term(con)) { + item_t item = {pos.m_it, con.m_pos.m_it, con.m_user_data}; + return m_handler(item); + } + return false; + } +private: + user_handler m_handler; +}; + + +//loop 0 +class _loop0 : public _unary { +public: + //constructor. + _loop0(_expr* e) : + _unary(e) + { + } + + //parse with whitespace + virtual bool parse_non_term(_context& con) const { + //if parsing of the first fails, restore the context and stop + _state st(con); + if (!m_expr->parse_non_term(con)) { + con.restore(st); + return true; + } + + //parse the rest + for(;;) { + _state st(con); + if (!m_expr->parse_non_term(con)) { + con.restore(st); + break; + } + } + + return true; + } + + //parse terminal + virtual bool parse_term(_context& con) const { + //if parsing of the first fails, restore the context and stop + _state st(con); + if (!m_expr->parse_term(con)) { + con.restore(st); + return true; + } + + //parse the rest until no more parsing is possible + for(;;) { + _state st(con); + if (!m_expr->parse_term(con)) { + con.restore(st); + break; + } + } + + return true; + } +}; + + +//loop 1 +class _loop1 : public _unary { +public: + //constructor. + _loop1(_expr* e) : + _unary(e) + { + } + + //parse with whitespace + virtual bool parse_non_term(_context& con) const { + //parse the first; if the first fails, stop + if (!m_expr->parse_non_term(con)) return false; + + //parse the rest until no more parsing is possible + for(;;) { + _state st(con); + if (!m_expr->parse_non_term(con)) { + con.restore(st); + break; + } + } + + return true; + } + + //parse terminal + virtual bool parse_term(_context& con) const { + //parse the first; if the first fails, stop + if (!m_expr->parse_term(con)) return false; + + //parse the rest until no more parsing is possible + for(;;) { + _state st(con); + if (!m_expr->parse_term(con)) { + con.restore(st); + break; + } + } + + return true; + } +}; + + +//optional +class _optional : public _unary { +public: + //constructor. + _optional(_expr* e) : + _unary(e) + { + } + + //parse with whitespace + virtual bool parse_non_term(_context& con) const { + _state st(con); + if (!m_expr->parse_non_term(con)) con.restore(st); + return true; + } + + //parse terminal + virtual bool parse_term(_context& con) const { + _state st(con); + if (!m_expr->parse_term(con)) con.restore(st); + return true; + } +}; + + +//and +class _and : public _unary { +public: + //constructor. + _and(_expr* e) : + _unary(e) + { + } + + //parse with whitespace + virtual bool parse_non_term(_context& con) const { + _state st(con); + bool ok = m_expr->parse_non_term(con); + con.restore(st); + return ok; + } + + //parse terminal + virtual bool parse_term(_context& con) const { + _state st(con); + bool ok = m_expr->parse_term(con); + con.restore(st); + return ok; + } +}; + + +//not +class _not : public _unary { +public: + //constructor. + _not(_expr* e) : + _unary(e) + { + } + + //parse with whitespace + virtual bool parse_non_term(_context& con) const { + _state st(con); + bool ok = !m_expr->parse_non_term(con); + con.restore(st); + return ok; + } + + //parse terminal + virtual bool parse_term(_context& con) const { + _state st(con); + bool ok = !m_expr->parse_term(con); + con.restore(st); + return ok; + } +}; + + +//newline +class _nl : public _unary { +public: + //constructor. + _nl(_expr* e) : + _unary(e) + { + } + + //parse with whitespace + virtual bool parse_non_term(_context& con) const { + if (!m_expr->parse_non_term(con)) return false; + con.next_line(); + return true; + } + + //parse terminal + virtual bool parse_term(_context& con) const { + if (!m_expr->parse_term(con)) return false; + con.next_line(); + return true; + } +}; + + +//base class for binary expressions +class _binary : public _expr { +public: + //constructor. + _binary(_expr* left, _expr* right) : + m_left(left), m_right(right) + { + } + + //destructor. + virtual ~_binary() { + delete m_left; + delete m_right; + } + +protected: + //left and right expressions + _expr* m_left, *m_right; +}; + + +//sequence +class _seq : public _binary { +public: + //constructor. + _seq(_expr* left, _expr* right) : + _binary(left, right) + { + } + + //parse with whitespace + virtual bool parse_non_term(_context& con) const { + if (!m_left->parse_non_term(con)) return false; + return m_right->parse_non_term(con); + } + + //parse terminal + virtual bool parse_term(_context& con) const { + if (!m_left->parse_term(con)) return false; + return m_right->parse_term(con); + } +}; + + +//choice +class _choice : public _binary { +public: + //constructor. + _choice(_expr* left, _expr* right) : + _binary(left, right) + { + } + + //parse with whitespace + virtual bool parse_non_term(_context& con) const { + _state st(con); + if (m_left->parse_non_term(con)) return true; + con.restore(st); + return m_right->parse_non_term(con); + } + + //parse terminal + virtual bool parse_term(_context& con) const { + _state st(con); + if (m_left->parse_term(con)) return true; + con.restore(st); + return m_right->parse_term(con); + } +}; + + +//reference to rule +class _ref : public _expr { +public: + //constructor. + _ref(rule& r) : + m_rule(r) + { + } + + //parse with whitespace + virtual bool parse_non_term(_context& con) const { + return con.parse_non_term(m_rule); + } + + //parse terminal + virtual bool parse_term(_context& con) const { + return con.parse_term(m_rule); + } + +private: + //reference + rule &m_rule; +}; + + +//eof +class _eof : public _expr { +public: + //parse with whitespace + virtual bool parse_non_term(_context& con) const { + return parse_term(con); + } + + //parse terminal + virtual bool parse_term(_context& con) const { + return con.end(); + } +}; + + +//any +class _any : public _expr { +public: + //parse with whitespace + virtual bool parse_non_term(_context& con) const { + return parse_term(con); + } + + //parse terminal + virtual bool parse_term(_context& con) const { + if (!con.end()) { + con.next_col(); + return true; + } + con.set_error_pos(); + return false; + } +}; + + +//true +class _true : public _expr { +public: + //parse with whitespace + virtual bool parse_non_term(_context&) const { + return true; + } + + //parse terminal + virtual bool parse_term(_context&) const { + return true; + } +}; + + +//false +class _false: public _expr { +public: + //parse with whitespace + virtual bool parse_non_term(_context&) const { + return false; + } + + //parse terminal + virtual bool parse_term(_context&) const { + return false; + } +}; + +//exception thrown when left recursion terminates successfully +struct _lr_ok { + rule* m_rule; + _lr_ok(rule* r) : m_rule(r) {} +}; + + +//constructor +_state::_state(_context& con) : + m_pos(con.m_pos), + m_matches(con.m_matches.size()) +{ +} + + +//parse non-term rule. +bool _context::parse_non_term(rule& r) { + //save the state of the rule + rule::_state old_state = r.m_state; + + //success/failure result + bool ok = false; + + //compute the new position + size_t new_pos = m_pos.m_it - m_begin; + + //check if we have left recursion + bool lr = new_pos == r.m_state.m_pos; + + //update the rule's state + r.m_state.m_pos = new_pos; + + //handle the mode of the rule + switch (r.m_state.m_mode) { + //normal parse + case rule::_PARSE: + if (lr) { + //first try to parse the rule by rejecting it, so alternative branches are examined + r.m_state.m_mode = rule::_REJECT; + ok = _parse_non_term(r); + + //if the first try is successful, try accepting the rule, + //so other elements of the sequence are parsed + if (ok) { + r.m_state.m_mode = rule::_ACCEPT; + + //loop until no more parsing can be done + for(;;) { + //store the correct state, in order to backtrack if the call fails + _state st(*this); + + //update the rule position to the current position, + //because at this state the rule is resolving the left recursion + r.m_state.m_pos = m_pos.m_it - m_begin; + + //if parsing fails, restore the last good state and stop + if (!_parse_non_term(r)) { + restore(st); + break; + } + } + + //since the left recursion was resolved successfully, + //return via a non-local exit + r.m_state = old_state; + throw _lr_ok(r.this_ptr()); + } + } + else { + try { + ok = _parse_non_term(r); + } + catch (const _lr_ok &ex) { + //since left recursions may be mutual, we must test which rule's left recursion + //was ended successfully + if (ex.m_rule == r.this_ptr()) { + ok = true; + } + else { + r.m_state = old_state; + throw; + } + } + } + break; + + //reject the left recursive rule + case rule::_REJECT: + if (lr) { + ok = false; + } + else { + r.m_state.m_mode = rule::_PARSE; + ok = _parse_non_term(r); + r.m_state.m_mode = rule::_REJECT; + } + break; + + //accept the left recursive rule + case rule::_ACCEPT: + if (lr) { + ok = true; + } + else { + r.m_state.m_mode = rule::_PARSE; + ok = _parse_non_term(r); + r.m_state.m_mode = rule::_ACCEPT; + } + break; + } + + //restore the rule's state + r.m_state = old_state; + + return ok; +} + + +//parse term rule. +bool _context::parse_term(rule& r) { + //save the state of the rule + rule::_state old_state = r.m_state; + + //success/failure result + bool ok = false; + + //compute the new position + size_t new_pos = m_pos.m_it - m_begin; + + //check if we have left recursion + bool lr = new_pos == r.m_state.m_pos; + + //update the rule's state + r.m_state.m_pos = new_pos; + + //handle the mode of the rule + switch (r.m_state.m_mode) { + //normal parse + case rule::_PARSE: + if (lr) { + //first try to parse the rule by rejecting it, so alternative branches are examined + r.m_state.m_mode = rule::_REJECT; + ok = _parse_term(r); + + //if the first try is successful, try accepting the rule, + //so other elements of the sequence are parsed + if (ok) { + r.m_state.m_mode = rule::_ACCEPT; + + //loop until no more parsing can be done + for(;;) { + //store the correct state, in order to backtrack if the call fails + _state st(*this); + + //update the rule position to the current position, + //because at this state the rule is resolving the left recursion + r.m_state.m_pos = m_pos.m_it - m_begin; + + //if parsing fails, restore the last good state and stop + if (!_parse_term(r)) { + restore(st); + break; + } + } + + //since the left recursion was resolved successfully, + //return via a non-local exit + r.m_state = old_state; + throw _lr_ok(r.this_ptr()); + } + } else { + try { + ok = _parse_term(r); + } + catch (const _lr_ok& ex) { + //since left recursions may be mutual, we must test which rule's left recursion + //was ended successfully + if (ex.m_rule == r.this_ptr()) { + ok = true; + } + else { + r.m_state = old_state; + throw; + } + } + } + break; + + //reject the left recursive rule + case rule::_REJECT: + if (lr) { + ok = false; + } else { + r.m_state.m_mode = rule::_PARSE; + ok = _parse_term(r); + r.m_state.m_mode = rule::_REJECT; + } + break; + + //accept the left recursive rule + case rule::_ACCEPT: + if (lr) { + ok = true; + } else { + r.m_state.m_mode = rule::_PARSE; + ok = _parse_term(r); + r.m_state.m_mode = rule::_ACCEPT; + } + break; + } + + //restore the rule's state + r.m_state = old_state; + + return ok; +} + + +//parse non-term rule internal. +bool _context::_parse_non_term(rule& r) { + bool ok = false; + if (_private::get_parse_proc(r)) { + pos b = m_pos; + ok = _private::get_expr(r)->parse_non_term(*this); + if (ok) { + m_matches.push_back(_match(r.this_ptr(), b, m_pos)); + } + } else { + ok = _private::get_expr(r)->parse_non_term(*this); + } + return ok; +} + + +//parse term rule internal. +bool _context::_parse_term(rule& r) { + bool ok = false; + if (_private::get_parse_proc(r)) { + pos b = m_pos; + ok = _private::get_expr(r)->parse_term(*this); + if (ok) { + m_matches.push_back(_match(r.this_ptr(), b, m_pos)); + } + } else { + ok = _private::get_expr(r)->parse_term(*this); + } + return ok; +} + + +//get syntax error +static error _syntax_error(_context& con) { + return error(con.m_error_pos, con.m_error_pos, ERROR_SYNTAX_ERROR); +} + + +//get eof error +static error _eof_error(_context& con) { + return error(con.m_error_pos, con.m_error_pos, ERROR_INVALID_EOF); +} + + +/** constructor from input. + @param i input. +*/ +pos::pos(input& i) : + m_it(i.begin()), + m_line(1), + m_col(1) +{ +} + + +/** character terminal constructor. + @param c character. +*/ +expr::expr(char c) : + m_expr(new _char(c)) +{ +} + + +/** null-terminated string terminal constructor. + @param s null-terminated string. +*/ +expr::expr(const char* s) : + m_expr(new _string(s)) +{ +} + + +/** rule reference constructor. + @param r rule. +*/ +expr::expr(rule& r) : + m_expr(new _ref(r)) +{ +} + + +/** creates a zero-or-more loop out of this expression. + @return a zero-or-more loop expression. +*/ +expr expr::operator*() const { + return _private::construct_expr(new _loop0(m_expr)); +} + + +/** creates a one-or-more loop out of this expression. + @return a one-or-more loop expression. +*/ +expr expr::operator+() const { + return _private::construct_expr(new _loop1(m_expr)); +} + + +/** creates an optional out of this expression. + @return an optional expression. +*/ +expr expr::operator-() const { + return _private::construct_expr(new _optional(m_expr)); +} + + +/** creates an AND-expression. + @return an AND-expression. +*/ +expr expr::operator&() const { + return _private::construct_expr((new _and(m_expr))); +} + + +/** creates a NOT-expression. + @return a NOT-expression. +*/ +expr expr::operator!() const { + return _private::construct_expr(new _not(m_expr)); +} + + +/** constructor. + @param b begin position. + @param e end position. +*/ +input_range::input_range(const pos& b, const pos& e) : +m_begin(b), +m_end(e) {} + + +/** constructor. + @param b begin position. + @param e end position. + @param t error type. +*/ +error::error(const pos& b, const pos& e, int t) : +input_range(b, e), +m_type(t) {} + + +/** compare on begin position. + @param e the other error to compare this with. + @return true if this comes before the previous error, false otherwise. +*/ +bool error::operator<(const error& e) const { + return m_begin.m_it < e.m_begin.m_it; +} + +rule::rule() : +m_expr(nullptr), +m_parse_proc(nullptr) {} + +/** character terminal constructor. + @param c character. +*/ +rule::rule(char c) : +m_expr(new _char(c)), +m_parse_proc(nullptr) {} + +/** null-terminated string terminal constructor. + @param s null-terminated string. +*/ +rule::rule(const char* s) : +m_expr(new _string(s)), +m_parse_proc(nullptr) {} + +/** constructor from expression. + @param e expression. +*/ +rule::rule(const expr& e) : +m_expr(_private::get_expr(e)), +m_parse_proc(nullptr) {} + + +/** constructor from rule. + @param r rule. +*/ +rule::rule(rule& r) : +m_expr(new _ref(r)), +m_parse_proc(nullptr) {} + +rule& rule::operator=(rule& r) { + m_expr = new _ref(r); + return *this; +} + +rule &rule::operator=(const expr& e) { + m_expr = _private::get_expr(e); + return *this; +} + +/** invalid constructor from rule (required by gcc). + @exception std::logic_error always thrown. +*/ +rule::rule(const rule&) { + throw std::logic_error("invalid operation"); +} + + +/** deletes the internal object that represents the expression. +*/ +rule::~rule() { + delete m_expr; +} + + +/** creates a zero-or-more loop out of this rule. + @return a zero-or-more loop rule. +*/ +expr rule::operator*() { + return _private::construct_expr(new _loop0(new _ref(*this))); +} + + +/** creates a one-or-more loop out of this rule. + @return a one-or-more loop rule. +*/ +expr rule::operator+() { + return _private::construct_expr(new _loop1(new _ref(*this))); +} + + +/** creates an optional out of this rule. + @return an optional rule. +*/ +expr rule::operator-() { + return _private::construct_expr(new _optional(new _ref(*this))); +} + + +/** creates an AND-expression out of this rule. + @return an AND-expression out of this rule. +*/ +expr rule::operator&() { + return _private::construct_expr(new _and(new _ref(*this))); +} + + +/** creates a NOT-expression out of this rule. + @return a NOT-expression out of this rule. +*/ +expr rule::operator!() { + return _private::construct_expr(new _not(new _ref(*this))); +} + + +/** sets the parse procedure. + @param p procedure. +*/ +void rule::set_parse_proc(parse_proc p) { + assert(p); + m_parse_proc = p; +} + + +/** creates a sequence of expressions. + @param left left operand. + @param right right operand. + @return an expression which parses a sequence. +*/ +expr operator >> (const expr& left, const expr& right) { + return _private::construct_expr( + new _seq(_private::get_expr(left), _private::get_expr(right))); +} + + +/** creates a choice of expressions. + @param left left operand. + @param right right operand. + @return an expression which parses a choice. +*/ +expr operator | (const expr& left, const expr& right) { + return _private::construct_expr( + new _choice(_private::get_expr(left), _private::get_expr(right))); +} + + +/** converts a parser expression into a terminal. + @param e expression. + @return an expression which parses a terminal. +*/ +expr term(const expr& e) { + return _private::construct_expr( + new _term(_private::get_expr(e))); +} + + +/** creates a set expression from a null-terminated string. + @param s null-terminated string with characters of the set. + @return an expression which parses a single character out of a set. +*/ +expr set(const char* s) { + return _private::construct_expr(new _set(s)); +} + + +/** creates a range expression. + @param min min character. + @param max max character. + @return an expression which parses a single character out of range. +*/ +expr range(int min, int max) { + return _private::construct_expr(new _set(min, max)); +} + + +/** creates an expression which increments the line counter + and resets the column counter when the given expression + is parsed successfully; used for newline characters. + @param e expression to wrap into a newline parser. + @return an expression that handles newlines. +*/ +expr nl(const expr& e) { + return _private::construct_expr(new _nl(_private::get_expr(e))); +} + + +/** creates an expression which tests for the end of input. + @return an expression that handles the end of input. +*/ +expr eof() { + return _private::construct_expr(new _eof()); +} + + +/** creates a not expression. + @param e expression. + @return the appropriate expression. +*/ +expr not_(const expr& e) { + return !e; +} + + +/** creates an and expression. + @param e expression. + @return the appropriate expression. +*/ +expr and_(const expr& e) { + return &e; +} + + +/** creates an expression that parses any character. + @return the appropriate expression. +*/ +expr any() { + return _private::construct_expr(new _any()); +} + + +/** parsing succeeds without consuming any input. +*/ +expr true_() { + return _private::construct_expr(new _true()); +} + + +/** parsing fails without consuming any input. +*/ +expr false_() { + return _private::construct_expr(new _false()); +} + + +/** parse with target expression and let user handle result. +*/ +expr user(const expr& e, const user_handler& handler) { + return _private::construct_expr(new _user(_private::get_expr(e), handler)); +} + + +/** parses the given input. + The parse procedures of each rule parsed are executed + before this function returns, if parsing succeeds. + @param i input. + @param g root rule of grammar. + @param el list of errors. + @param d user data, passed to the parse procedures. + @return true on parsing success, false on failure. +*/ +bool parse(input& i, rule& g, error_list& el, void* d, void* ud) { + //prepare context + _context con(i, ud); + + //parse grammar + if (!con.parse_non_term(g)) { + el.push_back(_syntax_error(con)); + return false; + } + + //if end is not reached, there was an error + if (!con.end()) { + if (con.m_error_pos.m_it < con.m_end) { + el.push_back(_syntax_error(con)); + } else { + el.push_back(_eof_error(con)); + } + return false; + } + + //success; execute the parse procedures + con.do_parse_procs(d); + return true; +} + + +} //namespace parserlib diff --git a/src/yuescript/parser.hpp b/src/yuescript/parser.hpp new file mode 100644 index 0000000..f70475f --- /dev/null +++ b/src/yuescript/parser.hpp @@ -0,0 +1,422 @@ +/* Copyright (c) 2012, Achilleas Margaritis, modified by Jin Li +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/ + +#pragma once + + +//gcc chokes without rule::rule(const rule &), +//msvc complains when rule::rule(const rule &) is defined. +#ifdef _MSC_VER +#pragma warning (disable: 4521) +#endif + + +#include +#include +#include +#include +#include +#include + +namespace parserlib { + +///type of the parser's input. +typedef std::basic_string input; +typedef input::iterator input_it; +typedef std::wstring_convert> Converter; + +class _private; +class _expr; +class _context; +class rule; + + +struct item_t { + input_it begin; + input_it end; + void* user_data; +}; +typedef std::function user_handler; + + +///position into the input. +class pos { +public: + ///interator into the input. + input::iterator m_it; + + ///line. + int m_line; + + ///column. + int m_col; + + ///null constructor. + pos():m_line(0),m_col(0) {} + + /** constructor from input. + @param i input. + */ + pos(input &i); +}; + + +/** a grammar expression. +*/ +class expr { +public: + /** character terminal constructor. + @param c character. + */ + expr(char c); + + /** null-terminated string terminal constructor. + @param s null-terminated string. + */ + expr(const char* s); + + /** rule reference constructor. + @param r rule. + */ + expr(rule& r); + + /** creates a zero-or-more loop out of this expression. + @return a zero-or-more loop expression. + */ + expr operator*() const; + + /** creates a one-or-more loop out of this expression. + @return a one-or-more loop expression. + */ + expr operator+() const; + + /** creates an optional out of this expression. + @return an optional expression. + */ + expr operator-() const; + + /** creates an AND-expression. + @return an AND-expression. + */ + expr operator&() const; + + /** creates a NOT-expression. + @return a NOT-expression. + */ + expr operator!() const; + +private: + //internal expression + _expr* m_expr; + + //internal constructor from internal expression + expr(_expr* e) : m_expr(e) {} + + //assignment not allowed + expr& operator=(expr&); + + friend class _private; +}; + + +/** type of procedure to invoke when a rule is successfully parsed. + @param b begin position of input. + @param e end position of input. + @param d pointer to user data. +*/ +typedef void (*parse_proc)(const pos& b, const pos& e, void* d); + + +///input range. +class input_range { +public: + virtual ~input_range() {} + + ///begin position. + pos m_begin; + + ///end position. + pos m_end; + + ///empty constructor. + input_range() {} + + /** constructor. + @param b begin position. + @param e end position. + */ + input_range(const pos& b, const pos& e); +}; + + +///enum with error types. +enum ERROR_TYPE { + ///syntax error + ERROR_SYNTAX_ERROR = 1, + + ///invalid end of file + ERROR_INVALID_EOF, + + ///first user error + ERROR_USER = 100 +}; + + +///error. +class error : public input_range { +public: + ///type + int m_type; + + /** constructor. + @param b begin position. + @param e end position. + @param t type. + */ + error(const pos& b, const pos& e, int t); + + /** compare on begin position. + @param e the other error to compare this with. + @return true if this comes before the previous error, false otherwise. + */ + bool operator<(const error& e) const; +}; + + +///type of error list. +typedef std::list error_list; + + +/** represents a rule. +*/ +class rule { +public: + /** character terminal constructor. + @param c character. + */ + rule(); + rule(char c); + + /** null-terminated string terminal constructor. + @param s null-terminated string. + */ + rule(const char* s); + + /** constructor from expression. + @param e expression. + */ + rule(const expr& e); + + /** constructor from rule. + @param r rule. + */ + rule(rule& r); + + /** invalid constructor from rule (required by gcc). + @param r rule. + @exception std::logic_error always thrown. + */ + rule(const rule& r); + + /** deletes the internal object that represents the expression. + */ + ~rule(); + + /** creates a zero-or-more loop out of this rule. + @return a zero-or-more loop rule. + */ + expr operator*(); + + /** creates a one-or-more loop out of this rule. + @return a one-or-more loop rule. + */ + expr operator+(); + + /** creates an optional out of this rule. + @return an optional rule. + */ + expr operator-(); + + /** creates an AND-expression out of this rule. + @return an AND-expression out of this rule. + */ + expr operator&(); + + /** creates a NOT-expression out of this rule. + @return a NOT-expression out of this rule. + */ + expr operator!(); + + /** sets the parse procedure. + @param p procedure. + */ + void set_parse_proc(parse_proc p); + + /** get the this ptr (since operator & is overloaded). + @return pointer to this. + */ + rule* this_ptr() { return this; } + + rule& operator=(rule&); + + rule& operator=(const expr&); + +private: + //mode + enum _MODE { + _PARSE, + _REJECT, + _ACCEPT + }; + + //state + struct _state { + //position in source code, relative to start + size_t m_pos; + + //mode + _MODE m_mode; + + //constructor + _state(size_t pos = -1, _MODE mode = _PARSE) : + m_pos(pos), m_mode(mode) {} + }; + + //internal expression + _expr* m_expr; + + //associated parse procedure. + parse_proc m_parse_proc; + + //state + _state m_state; + + friend class _private; + friend class _context; +}; + + +/** creates a sequence of expressions. + @param left left operand. + @param right right operand. + @return an expression which parses a sequence. +*/ +expr operator>>(const expr& left, const expr& right); + + +/** creates a choice of expressions. + @param left left operand. + @param right right operand. + @return an expression which parses a choice. +*/ +expr operator|(const expr& left, const expr& right); + + +/** converts a parser expression into a terminal. + @param e expression. + @return an expression which parses a terminal. +*/ +expr term(const expr& e); + + +/** creates a set expression from a null-terminated string. + @param s null-terminated string with characters of the set. + @return an expression which parses a single character out of a set. +*/ +expr set(const char* s); + + +/** creates a range expression. + @param min min character. + @param max max character. + @return an expression which parses a single character out of range. +*/ +expr range(int min, int max); + + +/** creates an expression which increments the line counter + and resets the column counter when the given expression + is parsed successfully; used for newline characters. + @param e expression to wrap into a newline parser. + @return an expression that handles newlines. +*/ +expr nl(const expr& e); + + +/** creates an expression which tests for the end of input. + @return an expression that handles the end of input. +*/ +expr eof(); + + +/** creates a not expression. + @param e expression. + @return the appropriate expression. +*/ +expr not_(const expr& e); + + +/** creates an and expression. + @param e expression. + @return the appropriate expression. +*/ +expr and_(const expr& e); + + +/** creates an expression that parses any character. + @return the appropriate expression. +*/ +expr any(); + + +/** parsing succeeds without consuming any input. +*/ +expr true_(); + + +/** parsing fails without consuming any input. +*/ +expr false_(); + + +/** parse with target expression and let user handle result. +*/ +expr user(const expr& e, const user_handler& handler); + + +/** parses the given input. + The parse procedures of each rule parsed are executed + before this function returns, if parsing succeeds. + @param i input. + @param g root rule of grammar. + @param el list of errors. + @param d user data, passed to the parse procedures. + @return true on parsing success, false on failure. +*/ +bool parse(input& i, rule& g, error_list& el, void* d, void* ud); + + +/** output the specific input range to the specific stream. + @param stream stream. + @param ir input range. + @return the stream. +*/ +template T& operator<<(T& stream, const input_range& ir) { + for(input::const_iterator it = ir.m_begin.m_it; + it != ir.m_end.m_it; + ++it) { + stream << (typename T::char_type)*it; + } + return stream; +} + + +} //namespace parserlib diff --git a/src/yuescript/stacktraceplus.h b/src/yuescript/stacktraceplus.h new file mode 100644 index 0000000..b165754 --- /dev/null +++ b/src/yuescript/stacktraceplus.h @@ -0,0 +1,528 @@ +R"lua_codes( +--[[ +Copyright (c) 2010 Ignacio Burgueño, modified by Li Jin + +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.]] + +-- tables +local _G = _G +local string, io, debug, coroutine = string, io, debug, coroutine + +-- functions +local tostring, require = tostring, require +local next, assert = next, assert +local pcall, type, pairs, ipairs = pcall, type, pairs, ipairs +local error = error + +assert(debug, "debug table must be available at this point") + +local string_gmatch = string.gmatch +local string_sub = string.sub +local table_concat = table.concat + +local yue = require("yue") + +local _M = { + max_tb_output_len = 70, -- controls the maximum length of the 'stringified' table before cutting with ' (more...)' + dump_locals = true, + simplified = false +} + +-- this tables should be weak so the elements in them won't become uncollectable +local m_known_tables = { [_G] = "_G (global table)" } +local function add_known_module(name, desc) + local ok, mod = pcall(require, name) + if ok then + m_known_tables[mod] = desc + end +end + +add_known_module("string", "string module") +add_known_module("io", "io module") +add_known_module("os", "os module") +add_known_module("table", "table module") +add_known_module("math", "math module") +add_known_module("package", "package module") +add_known_module("debug", "debug module") +add_known_module("coroutine", "coroutine module") + +-- lua5.2 +add_known_module("bit32", "bit32 module") +-- luajit +add_known_module("bit", "bit module") +add_known_module("jit", "jit module") +-- lua5.3 +if _VERSION >= "Lua 5.3" then + add_known_module("utf8", "utf8 module") +end + + +local m_user_known_tables = {} + +local m_known_functions = {} +for _, name in ipairs{ + -- Lua 5.2, 5.1 + "assert", + "collectgarbage", + "dofile", + "error", + "getmetatable", + "ipairs", + "load", + "loadfile", + "next", + "pairs", + "pcall", + "print", + "rawequal", + "rawget", + "rawlen", + "rawset", + "require", + "select", + "setmetatable", + "tonumber", + "tostring", + "type", + "xpcall", + + -- Lua 5.1 + "gcinfo", + "getfenv", + "loadstring", + "module", + "newproxy", + "setfenv", + "unpack", + -- TODO: add table.* etc functions +} do + if _G[name] then + m_known_functions[_G[name]] = name + end +end + +local m_user_known_functions = {} + +local function safe_tostring (value) + local ok, err = pcall(tostring, value) + if ok then return err else return (": '%s'"):format(err) end +end + +-- Private: +-- Parses a line, looking for possible function definitions (in a very naive way) +-- Returns '(anonymous)' if no function name was found in the line +local function ParseLine(line) + assert(type(line) == "string") + local match = line:match("^%s*function%s+(%w+)") + if match then + --print("+++++++++++++function", match) + return match + end + match = line:match("^%s*local%s+function%s+(%w+)") + if match then + --print("++++++++++++local", match) + return match + end + match = line:match("^%s*local%s+(%w+)%s+=%s+function") + if match then + --print("++++++++++++local func", match) + return match + end + match = line:match("%s*function%s*%(") -- this is an anonymous function + if match then + --print("+++++++++++++function2", match) + return "(anonymous)" + end + return "(anonymous)" +end + +-- Private: +-- Tries to guess a function's name when the debug info structure does not have it. +-- It parses either the file or the string where the function is defined. +-- Returns '?' if the line where the function is defined is not found +local function GuessFunctionName(info) + -- print("guessing function name") + if type(info.source) == "string" and info.source:sub(1,1) == "@" then + local fname = info.source:sub(2) + local text + if yue.file_exist(fname) then + text = yue.read_file(fname) + end + if not text then + -- print("file not found: "..tostring(err)) -- whoops! + return "?" + end + local line + local count = 0 + for lineText in (text.."\n"):gmatch("(.-)\n") do + line = lineText + count = count + 1 + if count == info.linedefined then + break + end + end + if not line then + --print("line not found") -- whoops! + return "?" + end + return ParseLine(line) + else + local line + local lineNumber = 0 + for l in string_gmatch(info.source, "([^\n]+)\n-") do + lineNumber = lineNumber + 1 + if lineNumber == info.linedefined then + line = l + break + end + end + if not line then + -- print("line not found") -- whoops! + return "?" + end + return ParseLine(line) + end +end + +--- +-- Dumper instances are used to analyze stacks and collect its information. +-- +local Dumper = {} + +Dumper.new = function(thread) + local t = { lines = {} } + for k,v in pairs(Dumper) do t[k] = v end + + t.dumping_same_thread = (thread == coroutine.running()) + + -- if a thread was supplied, bind it to debug.info and debug.get + -- we also need to skip this additional level we are introducing in the callstack (only if we are running + -- in the same thread we're inspecting) + if type(thread) == "thread" then + t.getinfo = function(level, what) + if t.dumping_same_thread and type(level) == "number" then + level = level + 1 + end + return debug.getinfo(thread, level, what) + end + t.getlocal = function(level, loc) + if t.dumping_same_thread then + level = level + 1 + end + return debug.getlocal(thread, level, loc) + end + else + t.getinfo = debug.getinfo + t.getlocal = debug.getlocal + end + + return t +end + +-- helpers for collecting strings to be used when assembling the final trace +function Dumper:add (text) + self.lines[#self.lines + 1] = text +end +function Dumper:add_f (fmt, ...) + self:add(fmt:format(...)) +end +function Dumper:concat_lines () + return table_concat(self.lines) +end + +--- +-- Private: +-- Iterates over the local variables of a given function. +-- +-- @param level The stack level where the function is. +-- +function Dumper:DumpLocals (level) + if not _M.dump_locals then return end + + local prefix = "\t " + local i = 1 + + if self.dumping_same_thread then + level = level + 1 + end + + local name, value = self.getlocal(level, i) + if not name then + return + end + self:add("\tLocal variables:\r\n") + while name do + if type(value) == "number" then + self:add_f("%s%s = number: %g\r\n", prefix, name, value) + elseif type(value) == "boolean" then + self:add_f("%s%s = boolean: %s\r\n", prefix, name, tostring(value)) + elseif type(value) == "string" then + self:add_f("%s%s = string: %q\r\n", prefix, name, value) + elseif type(value) == "userdata" then + self:add_f("%s%s = %s\r\n", prefix, name, safe_tostring(value)) + elseif type(value) == "nil" then + self:add_f("%s%s = nil\r\n", prefix, name) + elseif type(value) == "table" then + if m_known_tables[value] then + self:add_f("%s%s = %s\r\n", prefix, name, m_known_tables[value]) + elseif m_user_known_tables[value] then + self:add_f("%s%s = %s\r\n", prefix, name, m_user_known_tables[value]) + else + local txt = "{" + for k,v in pairs(value) do + txt = txt..safe_tostring(k)..":"..safe_tostring(v) + if #txt > _M.max_tb_output_len then + txt = txt.." (more...)" + break + end + if next(value, k) then txt = txt..", " end + end + self:add_f("%s%s = %s %s\r\n", prefix, name, safe_tostring(value), txt.."}") + end + elseif type(value) == "function" then + local info = self.getinfo(value, "nS") + local fun_name = info.name or m_known_functions[value] or m_user_known_functions[value] + if info.what == "C" then + self:add_f("%s%s = C %s\r\n", prefix, name, (fun_name and ("function: " .. fun_name) or tostring(value))) + else + local source = info.short_src + if source:sub(2,7) == "string" then + source = source:sub(9) + end + --for k,v in pairs(info) do print(k,v) end + fun_name = fun_name or GuessFunctionName(info) + self:add_f("%s%s = Lua function '%s' (defined at line %d of chunk %s)\r\n", prefix, name, fun_name, info.linedefined, source) + end + elseif type(value) == "thread" then + self:add_f("%sthread %q = %s\r\n", prefix, name, tostring(value)) + end + i = i + 1 + name, value = self.getlocal(level, i) + end +end + +local function getYueLineNumber(fname, line) + local yueCompiled = require("yue").yue_compiled + local source = yueCompiled["@"..fname] + if not source then + source = yueCompiled["@="..fname] + end + if not source then + local name_path = fname:gsub("%.", yue.dirsep) + local file_exist, file_path + for path in package.path:gmatch("[^;]+") do + file_path = path:gsub("?", name_path) + file_exist = yue.file_exist(file_path) + if file_exist then + break + end + end + if file_exist then + local codes = yue.read_file(file_path) + local yueFile = codes:match("^%s*--%s*%[yue%]:%s*([^\n]*)") + if yueFile then + fname = yueFile:gsub("^%s*(.-)%s*$", "%1") + source = codes + end + end + end + if source then + local current, target = 1, tonumber(line) + local findLine = line + for lineCode in source:gmatch("([^\n]*)\n") do + local num = lineCode:match("--%s*(%d+)%s*$") + if num then + findLine = num + end + if current == target then + return fname, findLine or line + end + current = current + 1 + end + end + return fname, line +end + +--- +-- Public: +-- Collects a detailed stack trace, dumping locals, resolving function names when they're not available, etc. +-- This function is suitable to be used as an error handler with pcall or xpcall +-- +-- @param thread An optional thread whose stack is to be inspected (defaul is the current thread) +-- @param message An optional error string or object. +-- @param level An optional number telling at which level to start the traceback (default is 1) +-- +-- Returns a string with the stack trace. +-- +function _M.stacktrace(thread, message, level) + if type(thread) ~= "thread" then + -- shift parameters left + thread, message, level = nil, thread, message + end + + thread = thread or coroutine.running() + + level = level or 1 + + local dumper = Dumper.new(thread) + + if type(message) == "table" then + dumper:add("an error object {\r\n") + local first = true + for k,v in pairs(message) do + if first then + dumper:add(" ") + first = false + else + dumper:add(",\r\n ") + end + dumper:add(safe_tostring(k)) + dumper:add(": ") + dumper:add(safe_tostring(v)) + end + dumper:add("\r\n}") + elseif type(message) == "string" then + local fname, line, msg = message:match('(.+):(%d+): (.*)$') + if fname then + local nfname, nline, nmsg = fname:match('(.+):(%d+): (.*)$') + if nfname then + fname = nmsg + end + end + if fname then + local fn = fname:match("%[string \"(.-)\"%]") + if fn then fname = fn end + fname = fname:gsub("^%s*(.-)%s*$", "%1") + fname, line = getYueLineNumber(fname, line) + if _M.simplified then + message = table.concat({ + "", fname, ":", + line, ": ", msg}) + message = message:gsub("^%(yuescript%):%s*%d+:%s*", "") + message = message:gsub("%s(%d+):", "%1:") + else + message = table.concat({ + "[string \"", fname, "\"]:", + line, ": ", msg}) + end + end + dumper:add(message) + end + + dumper:add("\r\n") + dumper:add[[ +Stack Traceback +=============== +]] + + local level_to_show = 1 + if dumper.dumping_same_thread then level = level + 1 end + + local info = dumper.getinfo(level, "nSlf") + while info do + if info.source and info.source:sub(1,1) == "@" then + info.source = info.source:sub(2) + elseif info.what == "main" or info.what == "Lua" then + info.source = info.source + end + info.source, info.currentline = getYueLineNumber(info.source, info.currentline) + if info.what == "main" then + if _M.simplified then + dumper:add_f("(%d) '%s':%d\r\n", level_to_show, info.source, info.currentline) + else + dumper:add_f("(%d) main chunk of file '%s' at line %d\r\n", level_to_show, info.source, info.currentline) + end + elseif info.what == "C" then + --print(info.namewhat, info.name) + --for k,v in pairs(info) do print(k,v, type(v)) end + local function_name = m_user_known_functions[info.func] or m_known_functions[info.func] or info.name or tostring(info.func) + dumper:add_f("(%d) %s C function '%s'\r\n", level_to_show, info.namewhat, function_name) + --dumper:add_f("%s%s = C %s\r\n", prefix, name, (m_known_functions[value] and ("function: " .. m_known_functions[value]) or tostring(value))) + elseif info.what == "tail" then + --print("tail") + --for k,v in pairs(info) do print(k,v, type(v)) end--print(info.namewhat, info.name) + dumper:add_f("(%d) tail call\r\n", level_to_show) + dumper:DumpLocals(level) + elseif info.what == "Lua" then + local source = info.source + local function_name = m_user_known_functions[info.func] or m_known_functions[info.func] or info.name + if source:sub(2, 7) == "string" then + source = source:sub(10,-3) + end + local was_guessed = false + if not function_name or function_name == "?" then + --for k,v in pairs(info) do print(k,v, type(v)) end + function_name = GuessFunctionName(info) + was_guessed = true + end + -- test if we have a file name + local function_type = (info.namewhat == "") and "function" or info.namewhat + if info.source and info.source:sub(1, 1) == "@" then + if _M.simplified then + dumper:add_f("(%d) '%s':%d%s\r\n", level_to_show, info.source:sub(2), info.currentline, was_guessed and " (guess)" or "") + else + dumper:add_f("(%d) Lua %s '%s' at file '%s':%d%s\r\n", level_to_show, function_type, function_name, info.source:sub(2), info.currentline, was_guessed and " (best guess)" or "") + end + elseif info.source and info.source:sub(1,1) == '#' then + if _M.simplified then + dumper:add_f("(%d) '%s':%d%s\r\n", level_to_show, info.source:sub(2), info.currentline, was_guessed and " (guess)" or "") + else + dumper:add_f("(%d) Lua %s '%s' at template '%s':%d%s\r\n", level_to_show, function_type, function_name, info.source:sub(2), info.currentline, was_guessed and " (best guess)" or "") + end + else + if _M.simplified then + dumper:add_f("(%d) '%s':%d\r\n", level_to_show, source, info.currentline) + else + dumper:add_f("(%d) Lua %s '%s' at chunk '%s':%d\r\n", level_to_show, function_type, function_name, source, info.currentline) + end + end + dumper:DumpLocals(level) + else + dumper:add_f("(%d) unknown frame %s\r\n", level_to_show, info.what) + end + + level = level + 1 + level_to_show = level_to_show + 1 + info = dumper.getinfo(level, "nSlf") + end + + return dumper:concat_lines() +end + +-- +-- Adds a table to the list of known tables +function _M.add_known_table(tab, description) + if m_known_tables[tab] then + error("Cannot override an already known table") + end + m_user_known_tables[tab] = description +end + +-- +-- Adds a function to the list of known functions +function _M.add_known_function(fun, description) + if m_known_functions[fun] then + error("Cannot override an already known function") + end + m_user_known_functions[fun] = description +end + +return _M + +)lua_codes"; diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h new file mode 100644 index 0000000..090765c --- /dev/null +++ b/src/yuescript/yue_ast.h @@ -0,0 +1,750 @@ +/* Copyright (c) 2021 Jin Li, http://www.luvfight.me + +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. */ + +#pragma once + +#include "yuescript/ast.hpp" + +namespace parserlib { + +#define AST_LEAF(type) \ +COUNTER_INC; \ +class type##_t : public ast_node \ +{ \ +public: \ + virtual int getId() const override { return COUNTER_READ; } + +#define AST_NODE(type) \ +COUNTER_INC; \ +class type##_t : public ast_container \ +{ \ +public: \ + virtual int getId() const override { return COUNTER_READ; } + +#define AST_MEMBER(type, ...) \ + type##_t() { \ + add_members({__VA_ARGS__}); \ + } + +#define AST_END(type) \ +}; \ +template<> constexpr int id() { return COUNTER_READ; } + +AST_LEAF(Num) +AST_END(Num) + +AST_LEAF(Name) +AST_END(Name) + +AST_NODE(Variable) + ast_ptr name; + AST_MEMBER(Variable, &name) +AST_END(Variable) + +AST_NODE(LabelName) + ast_ptr name; + AST_MEMBER(LabelName, &name) +AST_END(LabelName) + +AST_NODE(LuaKeyword) + ast_ptr name; + AST_MEMBER(LuaKeyword, &name) +AST_END(LuaKeyword) + +AST_LEAF(self) +AST_END(self) + +AST_NODE(self_name) + ast_ptr name; + AST_MEMBER(self_name, &name) +AST_END(self_name) + +AST_LEAF(self_class) +AST_END(self_class) + +AST_NODE(self_class_name) + ast_ptr name; + AST_MEMBER(self_class_name, &name) +AST_END(self_class_name) + +AST_NODE(SelfName) + ast_sel name; + AST_MEMBER(SelfName, &name) +AST_END(SelfName) + +AST_NODE(KeyName) + ast_sel name; + AST_MEMBER(KeyName, &name) +AST_END(KeyName) + +AST_LEAF(VarArg) +AST_END(VarArg) + +AST_LEAF(local_flag) +AST_END(local_flag) + +AST_LEAF(Seperator) +AST_END(Seperator) + +AST_NODE(NameList) + ast_ptr sep; + ast_list names; + AST_MEMBER(NameList, &sep, &names) +AST_END(NameList) + +class ExpListLow_t; +class TableBlock_t; + +AST_NODE(local_values) + ast_ptr nameList; + ast_sel valueList; + AST_MEMBER(local_values, &nameList, &valueList) +AST_END(local_values) + +AST_NODE(Local) + ast_sel item; + std::list forceDecls; + std::list decls; + bool collected = false; + bool defined = false; + AST_MEMBER(Local, &item) +AST_END(Local) + +class Assign_t; + +AST_NODE(LocalAttrib) + ast_ptr attrib; + ast_ptr nameList; + ast_ptr assign; + AST_MEMBER(LocalAttrib, &attrib, &nameList, &assign) +AST_END(LocalAttrib) + +AST_NODE(colon_import_name) + ast_ptr name; + AST_MEMBER(colon_import_name, &name) +AST_END(colon_import_name) + +class Exp_t; +class TableLit_t; + +AST_LEAF(import_literal_inner) +AST_END(import_literal_inner) + +AST_NODE(ImportLiteral) + ast_ptr sep; + ast_sel_list inners; + AST_MEMBER(ImportLiteral, &sep, &inners) +AST_END(ImportLiteral) + +AST_NODE(ImportFrom) + ast_ptr sep; + ast_sel_list names; + ast_ptr exp; + AST_MEMBER(ImportFrom, &sep, &names, &exp) +AST_END(ImportFrom) + +class MacroName_t; + +AST_NODE(macro_name_pair) + ast_ptr key; + ast_ptr value; + AST_MEMBER(macro_name_pair, &key, &value) +AST_END(macro_name_pair) + +AST_LEAF(import_all_macro) +AST_END(import_all_macro) + +class variable_pair_t; +class normal_pair_t; + +AST_NODE(ImportTabLit) + ast_ptr sep; + ast_sel_list items; + AST_MEMBER(ImportTabLit, &sep, &items) +AST_END(ImportTabLit) + +AST_NODE(ImportAs) + ast_ptr literal; + ast_sel target; + AST_MEMBER(ImportAs, &literal, &target) +AST_END(ImportAs) + +AST_NODE(Import) + ast_sel content; + AST_MEMBER(Import, &content) +AST_END(Import) + +AST_NODE(Label) + ast_ptr label; + AST_MEMBER(Label, &label) +AST_END(Label) + +AST_NODE(Goto) + ast_ptr label; + AST_MEMBER(Goto, &label) +AST_END(Goto) + +class FnArgsDef_t; + +AST_LEAF(fn_arrow_back) +AST_END(fn_arrow_back) + +class ChainValue_t; + +AST_NODE(Backcall) + ast_ptr argsDef; + ast_ptr arrow; + ast_ptr value; + AST_MEMBER(Backcall, &argsDef, &arrow, &value) +AST_END(Backcall) + +AST_NODE(ExpListLow) + ast_ptr sep; + ast_list exprs; + AST_MEMBER(ExpListLow, &sep, &exprs) +AST_END(ExpListLow) + +AST_NODE(ExpList) + ast_ptr sep; + ast_list exprs; + AST_MEMBER(ExpList, &sep, &exprs) +AST_END(ExpList) + +AST_NODE(Return) + bool allowBlockMacroReturn = false; + ast_ptr valueList; + AST_MEMBER(Return, &valueList) +AST_END(Return) + +class existential_op_t; +class Assign_t; +class Block_t; +class Statement_t; + +AST_NODE(With) + ast_ptr eop; + ast_ptr valueList; + ast_ptr assigns; + ast_sel body; + AST_MEMBER(With, &eop, &valueList, &assigns, &body) +AST_END(With) + +AST_NODE(SwitchCase) + ast_ptr valueList; + ast_sel body; + AST_MEMBER(SwitchCase, &valueList, &body) +AST_END(SwitchCase) + +AST_NODE(Switch) + ast_ptr target; + ast_ptr sep; + ast_list branches; + ast_sel lastBranch; + AST_MEMBER(Switch, &target, &sep, &branches, &lastBranch) +AST_END(Switch) + +AST_NODE(IfCond) + ast_ptr condition; + ast_ptr assign; + AST_MEMBER(IfCond, &condition, &assign) +AST_END(IfCond) + +AST_NODE(If) + ast_ptr sep; + ast_sel_list nodes; + AST_MEMBER(If, &sep, &nodes) +AST_END(If) + +AST_NODE(Unless) + ast_ptr sep; + ast_sel_list nodes; + AST_MEMBER(Unless, &sep, &nodes) +AST_END(Unless) + +AST_NODE(While) + ast_ptr condition; + ast_sel body; + AST_MEMBER(While, &condition, &body) +AST_END(While) + +class Body_t; + +AST_NODE(Repeat) + ast_ptr body; + ast_ptr condition; + AST_MEMBER(Repeat, &body, &condition) +AST_END(Repeat) + +AST_NODE(for_step_value) + ast_ptr value; + AST_MEMBER(for_step_value, &value) +AST_END(for_step_value) + +AST_NODE(For) + ast_ptr varName; + ast_ptr startValue; + ast_ptr stopValue; + ast_ptr stepValue; + ast_sel body; + AST_MEMBER(For, &varName, &startValue, &stopValue, &stepValue, &body) +AST_END(For) + +class AssignableNameList_t; +class star_exp_t; + +AST_NODE(ForEach) + ast_ptr nameList; + ast_sel loopValue; + ast_sel body; + AST_MEMBER(ForEach, &nameList, &loopValue, &body) +AST_END(ForEach) + +AST_NODE(Do) + ast_ptr body; + AST_MEMBER(Do, &body) +AST_END(Do) + +class CompInner_t; +class Statement_t; + +AST_NODE(Comprehension) + ast_sel value; + ast_ptr forLoop; + AST_MEMBER(Comprehension, &value, &forLoop) +AST_END(Comprehension) + +AST_NODE(comp_value) + ast_ptr value; + AST_MEMBER(comp_value, &value) +AST_END(comp_value) + +AST_NODE(TblComprehension) + ast_ptr key; + ast_ptr value; + ast_ptr forLoop; + AST_MEMBER(TblComprehension, &key, &value, &forLoop) +AST_END(TblComprehension) + +AST_NODE(star_exp) + ast_ptr value; + AST_MEMBER(star_exp, &value) +AST_END(star_exp) + +AST_NODE(CompForEach) + ast_ptr nameList; + ast_sel loopValue; + AST_MEMBER(CompForEach, &nameList, &loopValue) +AST_END(CompForEach) + +AST_NODE(CompFor) + ast_ptr varName; + ast_ptr startValue; + ast_ptr stopValue; + ast_ptr stepValue; + AST_MEMBER(CompFor, &varName, &startValue, &stopValue, &stepValue) +AST_END(CompFor) + +AST_NODE(CompInner) + ast_ptr sep; + ast_sel_list items; + AST_MEMBER(CompInner, &sep, &items) +AST_END(CompInner) + +class TableBlock_t; + +AST_NODE(Assign) + ast_ptr sep; + ast_sel_list values; + AST_MEMBER(Assign, &sep, &values) +AST_END(Assign) + +AST_LEAF(update_op) +AST_END(update_op) + +AST_NODE(Update) + ast_ptr op; + ast_ptr value; + AST_MEMBER(Update, &op, &value) +AST_END(Update) + +AST_LEAF(BinaryOperator) +AST_END(BinaryOperator) + +AST_LEAF(unary_operator) +AST_END(unary_operator) + +class AssignableChain_t; + +AST_NODE(Assignable) + ast_sel item; + AST_MEMBER(Assignable, &item) +AST_END(Assignable) + +class unary_exp_t; + +AST_NODE(exp_op_value) + ast_ptr op; + ast_list backcalls; + AST_MEMBER(exp_op_value, &op, &backcalls) +AST_END(exp_op_value) + +AST_NODE(Exp) + ast_ptr sep; + ast_list backcalls; + ast_list opValues; + AST_MEMBER(Exp, &sep, &backcalls, &opValues) +AST_END(Exp) + +class Parens_t; +class MacroName_t; + +AST_NODE(Callable) + ast_sel item; + AST_MEMBER(Callable, &item) +AST_END(Callable) + +AST_NODE(variable_pair) + ast_ptr name; + AST_MEMBER(variable_pair, &name) +AST_END(variable_pair) + +class DoubleString_t; +class SingleString_t; +class LuaString_t; + +AST_NODE(normal_pair) + ast_sel key; + ast_sel value; + AST_MEMBER(normal_pair, &key, &value) +AST_END(normal_pair) + +AST_NODE(simple_table) + ast_ptr sep; + ast_sel_list pairs; + AST_MEMBER(simple_table, &sep, &pairs) +AST_END(simple_table) + +class String_t; +class const_value_t; +class ClassDecl_t; +class unary_value_t; +class TableLit_t; +class FunLit_t; + +AST_NODE(SimpleValue) + ast_sel value; + AST_MEMBER(SimpleValue, &value) +AST_END(SimpleValue) + +AST_LEAF(LuaStringOpen) +AST_END(LuaStringOpen) + +AST_LEAF(LuaStringContent) +AST_END(LuaStringContent) + +AST_LEAF(LuaStringClose) +AST_END(LuaStringClose) + +AST_NODE(LuaString) + ast_ptr open; + ast_ptr content; + ast_ptr close; + AST_MEMBER(LuaString, &open, &content, &close) +AST_END(LuaString) + +AST_LEAF(SingleString) +AST_END(SingleString) + +AST_LEAF(double_string_inner) +AST_END(double_string_inner) + +AST_NODE(double_string_content) + ast_sel content; + AST_MEMBER(double_string_content, &content) +AST_END(double_string_content) + +AST_NODE(DoubleString) + ast_ptr sep; + ast_list segments; + AST_MEMBER(DoubleString, &sep, &segments) +AST_END(DoubleString) + +AST_NODE(String) + ast_sel str; + AST_MEMBER(String, &str) +AST_END(String) + +AST_NODE(DotChainItem) + ast_ptr name; + AST_MEMBER(DotChainItem, &name) +AST_END(DotChainItem) + +AST_NODE(ColonChainItem) + ast_sel name; + bool switchToDot = false; + AST_MEMBER(ColonChainItem, &name) +AST_END(ColonChainItem) + +class default_value_t; + +AST_NODE(Slice) + ast_sel startValue; + ast_sel stopValue; + ast_sel stepValue; + AST_MEMBER(Slice, &startValue, &stopValue, &stepValue) +AST_END(Slice) + +AST_NODE(Parens) + ast_ptr expr; + AST_MEMBER(Parens, &expr) +AST_END(Parens) + +AST_NODE(Invoke) + ast_ptr sep; + ast_sel_list args; + AST_MEMBER(Invoke, &sep, &args) +AST_END(Invoke) + +AST_LEAF(existential_op) +AST_END(existential_op) + +class InvokeArgs_t; + +AST_NODE(ChainValue) + ast_ptr sep; + ast_sel_list items; + AST_MEMBER(ChainValue, &sep, &items) +AST_END(ChainValue) + +AST_NODE(AssignableChain) + ast_ptr sep; + ast_sel_list items; + AST_MEMBER(AssignableChain, &sep, &items) +AST_END(AssignableChain) + +AST_NODE(Value) + ast_sel item; + AST_MEMBER(Value, &item) +AST_END(Value) + +AST_LEAF(default_value) +AST_END(default_value) + +AST_NODE(TableLit) + ast_ptr sep; + ast_sel_list values; + AST_MEMBER(TableLit, &sep, &values) +AST_END(TableLit) + +AST_NODE(TableBlockIndent) + ast_ptr sep; + ast_sel_list values; + AST_MEMBER(TableBlockIndent, &sep, &values) +AST_END(TableBlockIndent) + +AST_NODE(TableBlock) + ast_ptr sep; + ast_sel_list values; + AST_MEMBER(TableBlock, &sep, &values) +AST_END(TableBlock) + +AST_NODE(class_member_list) + ast_ptr sep; + ast_sel_list values; + AST_MEMBER(class_member_list, &sep, &values) +AST_END(class_member_list) + +AST_NODE(ClassBlock) + ast_ptr sep; + ast_sel_list contents; + AST_MEMBER(ClassBlock, &sep, &contents) +AST_END(ClassBlock) + +AST_NODE(ClassDecl) + ast_ptr name; + ast_ptr extend; + ast_ptr body; + AST_MEMBER(ClassDecl, &name, &extend, &body) +AST_END(ClassDecl) + +AST_NODE(global_values) + ast_ptr nameList; + ast_sel valueList; + AST_MEMBER(global_values, &nameList, &valueList) +AST_END(global_values) + +AST_LEAF(global_op) +AST_END(global_op) + +AST_NODE(Global) + ast_sel item; + AST_MEMBER(Global, &item) +AST_END(Global) + +AST_LEAF(export_default) +AST_END(export_default) + +class Macro_t; + +AST_NODE(Export) + ast_ptr def; + ast_sel target; + ast_ptr assign; + AST_MEMBER(Export, &def, &target, &assign) +AST_END(Export) + +AST_NODE(FnArgDef) + ast_sel name; + ast_ptr defaultValue; + AST_MEMBER(FnArgDef, &name, &defaultValue) +AST_END(FnArgDef) + +AST_NODE(FnArgDefList) + ast_ptr sep; + ast_list definitions; + ast_ptr varArg; + AST_MEMBER(FnArgDefList, &sep, &definitions, &varArg) +AST_END(FnArgDefList) + +AST_NODE(outer_var_shadow) + ast_ptr varList; + AST_MEMBER(outer_var_shadow, &varList) +AST_END(outer_var_shadow) + +AST_NODE(FnArgsDef) + ast_ptr defList; + ast_ptr shadowOption; + AST_MEMBER(FnArgsDef, &defList, &shadowOption) +AST_END(FnArgsDef) + +AST_LEAF(fn_arrow) +AST_END(fn_arrow) + +AST_NODE(FunLit) + ast_ptr argsDef; + ast_ptr arrow; + ast_ptr body; + AST_MEMBER(FunLit, &argsDef, &arrow, &body) +AST_END(FunLit) + +AST_NODE(MacroName) + ast_ptr name; + AST_MEMBER(MacroName, &name) +AST_END(MacroName) + +AST_NODE(MacroLit) + ast_ptr argsDef; + ast_ptr body; + AST_MEMBER(MacroLit, &argsDef, &body) +AST_END(MacroLit) + +AST_NODE(Macro) + ast_ptr name; + ast_ptr macroLit; + AST_MEMBER(Macro, &name, ¯oLit) +AST_END(Macro) + +AST_NODE(NameOrDestructure) + ast_sel item; + AST_MEMBER(NameOrDestructure, &item) +AST_END(NameOrDestructure) + +AST_NODE(AssignableNameList) + ast_ptr sep; + ast_list items; + AST_MEMBER(AssignableNameList, &sep, &items) +AST_END(AssignableNameList) + +AST_NODE(InvokeArgs) + ast_ptr sep; + ast_sel_list args; + AST_MEMBER(InvokeArgs, &sep, &args) +AST_END(InvokeArgs) + +AST_LEAF(const_value) +AST_END(const_value) + +AST_NODE(unary_value) + ast_list ops; + ast_ptr value; + AST_MEMBER(unary_value, &ops, &value) +AST_END(unary_value) + +AST_NODE(unary_exp) + ast_list ops; + ast_list expos; + AST_MEMBER(unary_exp, &ops, &expos) +AST_END(unary_exp) + +AST_NODE(ExpListAssign) + ast_ptr expList; + ast_sel action; + AST_MEMBER(ExpListAssign, &expList, &action) +AST_END(ExpListAssign) + +AST_NODE(if_line) + ast_ptr condition; + ast_ptr assign; + AST_MEMBER(if_line, &condition, &assign) +AST_END(if_line) + +AST_NODE(unless_line) + ast_ptr condition; + AST_MEMBER(unless_line, &condition) +AST_END(unless_line) + +AST_LEAF(BreakLoop) +AST_END(BreakLoop) + +AST_NODE(BackcallBody) + ast_ptr sep; + ast_list values; + AST_MEMBER(BackcallBody, &sep, &values) +AST_END(BackcallBody) + +AST_NODE(statement_appendix) + ast_sel item; + AST_MEMBER(statement_appendix, &item) +AST_END(statement_appendix) + +AST_LEAF(statement_sep) +AST_END(statement_sep) + +AST_NODE(Statement) + ast_sel content; + ast_ptr appendix; + ast_ptr needSep; + AST_MEMBER(Statement, &content, &appendix, &needSep) +AST_END(Statement) + +class Block_t; + +AST_NODE(Body) + ast_sel content; + AST_MEMBER(Body, &content) +AST_END(Body) + +AST_NODE(Block) + ast_ptr sep; + ast_list statements; + AST_MEMBER(Block, &sep, &statements) +AST_END(Block) + +AST_NODE(File) + ast_ptr block; + AST_MEMBER(File, &block) +AST_END(File) + +} // namespace parserlib diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp new file mode 100644 index 0000000..22311f4 --- /dev/null +++ b/src/yuescript/yue_compiler.cpp @@ -0,0 +1,5612 @@ +/* Copyright (c) 2021 Jin Li, http://www.luvfight.me + +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.*/ + +#include +#include +#include +#include +#include +#include + +#include "yuescript/yue_parser.h" +#include "yuescript/yue_compiler.h" + +#ifndef YUE_NO_MACRO + +extern "C" { +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" +} // extern "C" + +// name of table stored in lua registry +#define YUE_MODULE "__yue_modules__" + +#if LUA_VERSION_NUM > 501 + #ifndef LUA_COMPAT_5_1 + #define lua_objlen lua_rawlen + #endif // LUA_COMPAT_5_1 +#endif // LUA_VERSION_NUM + +#endif // YUE_NO_MACRO + +namespace yue { +using namespace std::string_view_literals; +using namespace parserlib; + +#define BLOCK_START do { +#define BLOCK_END } while (false); +#define BREAK_IF(cond) if (cond) break + +#define _DEFER(code,line) std::shared_ptr _defer_##line(nullptr, [&](auto){code;}) +#define DEFER(code) _DEFER(code,__LINE__) +#define YUEE(msg,node) throw std::logic_error( \ + _info.errorMessage( \ + std::string("[File] ") + __FILE__ \ + + ", [Func] " + __FUNCTION__ \ + + ", [Line] " + std::to_string(__LINE__) \ + + ", [Error] " + msg, node)) + +typedef std::list str_list; + +inline std::string s(std::string_view sv) { + return std::string(sv); +} + +const std::string_view version = "0.6.5"sv; +const std::string_view extension = "yue"sv; + +class YueCompilerImpl { +public: +#ifndef YUE_NO_MACRO + YueCompilerImpl(lua_State* sharedState, + const std::function& luaOpen, + bool sameModule, + std::string_view moduleName = {}): + L(sharedState), + _luaOpen(luaOpen), + _moduleName(moduleName) { + BLOCK_START + BREAK_IF(!sameModule); + BREAK_IF(!L); + _sameModule = true; + int top = lua_gettop(L); + DEFER(lua_settop(L, top)); + lua_pushliteral(L, YUE_MODULE); // YUE_MODULE + lua_rawget(L, LUA_REGISTRYINDEX); // reg[YUE_MODULE], tb + BREAK_IF(lua_istable(L, -1) == 0); + int idx = static_cast(lua_objlen(L, -1)); // idx = #tb, tb + BREAK_IF(idx == 0); + _useModule = true; + BLOCK_END + } + + ~YueCompilerImpl() { + if (L && _stateOwner) { + lua_close(L); + L = nullptr; + } + } +#endif // YUE_NO_MACRO + + CompileInfo compile(std::string_view codes, const YueConfig& config) { + _config = config; +#ifndef YUE_NO_MACRO + if (L) passOptions(); +#endif // YUE_NO_MACRO + _info = _parser.parse(codes); + std::unique_ptr globals; + std::unique_ptr options; + if (!config.options.empty()) { + options = std::make_unique(config.options); + } + DEFER(clear()); + if (_info.node) { + try { + str_list out; + pushScope(); + _enableReturn.push(_info.moduleName.empty()); + _varArgs.push(true); + transformBlock(_info.node.to()->block, out, + config.implicitReturnRoot ? ExpUsage::Return : ExpUsage::Common, + nullptr, true); + popScope(); + if (config.lintGlobalVariable) { + globals = std::make_unique(); + for (const auto& var : _globals) { + int line,col; + std::tie(line,col) = var.second; + globals->push_back({var.first, line, col}); + } + } +#ifndef YUE_NO_MACRO + if (L) { + int top = lua_gettop(L); + DEFER(lua_settop(L, top)); + if (!options) { + options = std::make_unique(); + } + pushYue("options"sv); + lua_pushnil(L); // options startKey + while (lua_next(L, -2) != 0) { // options key value + size_t len = 0; + auto pstr = lua_tolstring(L, -2, &len); + std::string key{pstr, len}; + pstr = lua_tolstring(L, -1, &len); + std::string value{pstr, len}; + (*options)[key] = value; + lua_pop(L, 1); // options key + } + } +#endif // YUE_NO_MACRO + return {std::move(out.back()), Empty, std::move(globals), std::move(options)}; + } catch (const std::logic_error& error) { + return {Empty, error.what(), std::move(globals), std::move(options)}; + } + } else { + return {Empty, std::move(_info.error), std::move(globals), std::move(options)}; + } + } + + void clear() { + _indentOffset = 0; + _scopes.clear(); + _codeCache.clear(); + _buf.str(""); + _buf.clear(); + _joinBuf.str(""); + _joinBuf.clear(); + _globals.clear(); + _info = {}; + _varArgs = {}; + _withVars = {}; + _continueVars = {}; + _enableReturn = {}; +#ifndef YUE_NO_MACRO + if (_useModule) { + _useModule = false; + if (!_sameModule) { + int top = lua_gettop(L); + DEFER(lua_settop(L, top)); + lua_pushliteral(L, YUE_MODULE); // YUE_MODULE + lua_rawget(L, LUA_REGISTRYINDEX); // reg[YUE_MODULE], tb + int idx = static_cast(lua_objlen(L, -1)); + lua_pushnil(L); // tb nil + lua_rawseti(L, -2, idx); // tb[idx] = nil, tb + } + } +#endif // YUE_NO_MACRO + } +private: +#ifndef YUE_NO_MACRO + bool _stateOwner = false; + bool _useModule = false; + bool _sameModule = false; + lua_State* L = nullptr; + std::function _luaOpen; +#endif // YUE_NO_MACRO + YueConfig _config; + YueParser _parser; + ParseInfo _info; + int _indentOffset = 0; + std::stack _varArgs; + std::stack _enableReturn; + std::stack _withVars; + std::stack _continueVars; + std::list> _codeCache; + std::unordered_map> _globals; + std::ostringstream _buf; + std::ostringstream _joinBuf; + const std::string _newLine = "\n"; + std::string _moduleName; + + enum class LocalMode { + None = 0, + Capital = 1, + Any = 2 + }; + enum class GlobalMode { + None = 0, + Capital = 1, + Any = 2 + }; + struct Scope { + GlobalMode mode = GlobalMode::None; + std::unique_ptr> vars; + std::unique_ptr> allows; + std::unique_ptr> globals; + }; + std::list _scopes; + static const std::string Empty; + + enum class MemType { + Builtin, + Common, + Property + }; + + struct ClassMember { + std::string item; + MemType type; + ast_node* node; + }; + + struct DestructItem { + bool isVariable = false; + std::string name; + std::string structure; + }; + + struct Destructure { + std::string value; + std::list items; + }; + + enum class ExpUsage { + Return, + Assignment, + Common, + Closure + }; + + void pushScope() { + _scopes.emplace_back(); + _scopes.back().vars = std::make_unique>(); + } + + void popScope() { + _scopes.pop_back(); + } + + bool isDefined(const std::string& name) const { + bool isDefined = false; + int mode = int(std::isupper(name[0]) ? GlobalMode::Capital : GlobalMode::Any); + const auto& current = _scopes.back(); + if (int(current.mode) >= mode) { + if (current.globals) { + if (current.globals->find(name) != current.globals->end()) { + isDefined = true; + current.vars->insert(name); + } + } else { + isDefined = true; + current.vars->insert(name); + } + } + decltype(_scopes.back().allows.get()) allows = nullptr; + for (auto it = _scopes.rbegin(); it != _scopes.rend(); ++it) { + if (it->allows) allows = it->allows.get(); + } + bool checkShadowScopeOnly = false; + if (allows) { + checkShadowScopeOnly = allows->find(name) == allows->end(); + } + for (auto it = _scopes.rbegin(); it != _scopes.rend(); ++it) { + auto vars = it->vars.get(); + if (vars->find(name) != vars->end()) { + isDefined = true; + break; + } + if (checkShadowScopeOnly && it->allows) break; + } + return isDefined; + } + + bool isSolidDefined(const std::string& name) const { + bool isDefined = false; + for (auto it = _scopes.rbegin(); it != _scopes.rend(); ++it) { + auto vars = it->vars.get(); + if (vars->find(name) != vars->end()) { + isDefined = true; + break; + } + } + return isDefined; + } + + void markVarShadowed() { + auto& scope = _scopes.back(); + scope.allows = std::make_unique>(); + } + + void markVarGlobal(GlobalMode mode, bool specified) { + auto& scope = _scopes.back(); + scope.mode = mode; + if (specified && !scope.globals) { + scope.globals = std::make_unique>(); + } + } + + void addGlobalVar(const std::string& name) { + auto& scope = _scopes.back(); + scope.globals->insert(name); + } + + void addToAllowList(const std::string& name) { + auto& scope = _scopes.back(); + scope.allows->insert(name); + } + + void forceAddToScope(const std::string& name) { + auto& scope = _scopes.back(); + scope.vars->insert(name); + } + + Scope& currentScope() { + return _scopes.back(); + } + + bool addToScope(const std::string& name) { + bool defined = isDefined(name); + if (!defined) { + auto& scope = currentScope(); + scope.vars->insert(name); + } + return !defined; + } + + std::string getUnusedName(std::string_view name) const { + int index = 0; + std::string newName; + do { + newName = s(name) + std::to_string(index); + index++; + } while (isSolidDefined(newName)); + return newName; + } + + const std::string nll(ast_node* node) const { + if (_config.reserveLineNumber) { + return s(" -- "sv) + std::to_string(node->m_begin.m_line + _config.lineOffset) + _newLine; + } else { + return _newLine; + } + } + + const std::string nlr(ast_node* node) const { + if (_config.reserveLineNumber) { + return s(" -- "sv) + std::to_string(node->m_end.m_line + _config.lineOffset) + _newLine; + } else { + return _newLine; + } + } + + void incIndentOffset() { + _indentOffset++; + } + + void decIndentOffset() { + _indentOffset--; + } + + std::string indent() const { + if (_config.useSpaceOverTab) { + return std::string((_scopes.size() - 1 + _indentOffset) * 2, ' '); + } else { + return std::string(_scopes.size() - 1 + _indentOffset, '\t'); + } + } + + std::string indent(int offset) const { + if (_config.useSpaceOverTab) { + return std::string((_scopes.size() - 1 + _indentOffset + offset) * 2, ' '); + } else { + return std::string(_scopes.size() - 1 + _indentOffset + offset, '\t'); + } + } + + std::string clearBuf() { + std::string str = _buf.str(); + _buf.str(""); + _buf.clear(); + return str; + } + + std::string join(const str_list& items) { + if (items.empty()) return Empty; + else if (items.size() == 1) return items.front(); + for (const auto& item : items) { + _joinBuf << item; + } + auto result = _joinBuf.str(); + _joinBuf.str(""); + _joinBuf.clear(); + return result; + } + + std::string join(const str_list& items, std::string_view sep) { + if (items.empty()) return Empty; + else if (items.size() == 1) return items.front(); + std::string sepStr = s(sep); + auto begin = ++items.begin(); + _joinBuf << items.front(); + for (auto it = begin; it != items.end(); ++it) { + _joinBuf << sepStr << *it; + } + auto result = _joinBuf.str(); + _joinBuf.str(""); + _joinBuf.clear(); + return result; + } + + unary_exp_t* singleUnaryExpFrom(ast_node* item) const { + Exp_t* exp = nullptr; + switch (item->getId()) { + case id(): + exp = static_cast(item); + break; + case id(): { + auto expList = static_cast(item); + if (expList->exprs.size() == 1) { + exp = static_cast(expList->exprs.front()); + } + break; + } + case id(): { + auto expList = static_cast(item); + if (expList->exprs.size() == 1) { + exp = static_cast(expList->exprs.front()); + } + break; + } + case id(): { + auto unary = static_cast(item); + if (unary->expos.size() == 1) { + return unary; + } + return nullptr; + } + default: break; + } + if (!exp) return nullptr; + BLOCK_START + BREAK_IF(!exp->opValues.empty()); + BREAK_IF(exp->backcalls.size() != 1); + auto unary = static_cast(exp->backcalls.back()); + BREAK_IF(unary->expos.size() != 1); + return unary; + BLOCK_END + return nullptr; + } + + Value_t* singleValueFrom(ast_node* item) const { + if (auto unary = singleUnaryExpFrom(item)) { + if (unary->ops.empty()) { + return static_cast(unary->expos.back()); + } + } + return nullptr; + } + + ast_ptr newExp(Value_t* value, ast_node* x) { + auto unary = x->new_ptr(); + unary->expos.push_back(value); + auto exp = x->new_ptr(); + exp->backcalls.push_back(unary); + return exp; + } + + ast_ptr newExp(Value_t* left, BinaryOperator_t* op, Value_t* right, ast_node* x) { + auto lunary = x->new_ptr(); + lunary->expos.push_back(left); + auto opValue = x->new_ptr(); + { + auto runary = x->new_ptr(); + runary->expos.push_back(right); + opValue->op.set(op); + opValue->backcalls.push_back(runary); + } + auto exp = x->new_ptr(); + exp->backcalls.push_back(lunary); + exp->opValues.push_back(opValue); + return exp; + } + + ast_ptr newExp(unary_exp_t* unary, ast_node* x) { + auto exp = x->new_ptr(); + exp->backcalls.push_back(unary); + return exp; + } + + SimpleValue_t* simpleSingleValueFrom(ast_node* expList) const { + auto value = singleValueFrom(expList); + if (value && value->item.is()) { + return static_cast(value->item.get()); + } + return nullptr; + } + + Statement_t* lastStatementFrom(ast_node* body) const { + switch (body->getId()) { + case id(): + return lastStatementFrom(static_cast(body)); + case id(): { + return static_cast(body); + } + default: YUEE("AST node mismatch", body); break; + } + return nullptr; + } + + Statement_t* lastStatementFrom(const node_container& stmts) const { + if (!stmts.empty()) { + auto it = stmts.end(); --it; + while (!static_cast(*it)->content && it != stmts.begin()) { + --it; + } + return static_cast(*it); + } + return nullptr; + } + + Statement_t* lastStatementFrom(Body_t* body) const { + if (auto stmt = body->content.as()) { + return stmt; + } else { + const auto& stmts = body->content.to()->statements.objects(); + return lastStatementFrom(stmts); + } + } + + Statement_t* lastStatementFrom(Block_t* block) const { + const auto& stmts = block->statements.objects(); + return lastStatementFrom(stmts); + } + + Exp_t* lastExpFromAssign(ast_node* action) { + switch (action->getId()) { + case id(): { + auto update = static_cast(action); + return update->value; + } + case id(): { + auto assign = static_cast(action); + return ast_cast(assign->values.back()); + } + } + return nullptr; + } + + Exp_t* lastExpFromStatement(Statement_t* stmt) { + if (!stmt->content) return nullptr; + switch (stmt->content->getId()) { + case id(): { + auto expListAssign = static_cast(stmt->content.get()); + if (auto action = expListAssign->action.get()) { + return lastExpFromAssign(action); + } else { + return static_cast(expListAssign->expList->exprs.back()); + } + } + case id(): { + auto exportNode = static_cast(stmt->content.get()); + if (auto action = exportNode->assign.get()) { + return lastExpFromAssign(action); + } else { + switch (exportNode->target->getId()) { + case id(): return exportNode->target.to(); + case id(): return static_cast(exportNode->target.to()->exprs.back()); + } + } + } + case id(): { + if (auto localValues = static_cast(stmt->content.get())->item.as()) { + if (auto expList = localValues->valueList.as()) { + return static_cast(expList->exprs.back()); + } + } + } + case id(): { + if (auto globalValues = static_cast(stmt->content.get())->item.as()) { + if (auto expList = globalValues->valueList.as()) { + return static_cast(expList->exprs.back()); + } + } + } + } + return nullptr; + } + + template + ast_ptr toAst(std::string_view codes, ast_node* parent) { + auto res = _parser.parse(s(codes)); + int line = parent->m_begin.m_line; + int col = parent->m_begin.m_line; + res.node->traverse([&](ast_node* node) { + node->m_begin.m_line = line; + node->m_end.m_line = line; + node->m_begin.m_col = col; + node->m_end.m_col = col; + return traversal::Continue; + }); + _codeCache.push_back(std::move(res.codes)); + return ast_ptr(res.node.template to()); + } + + bool isChainValueCall(ChainValue_t* chainValue) const { + return ast_is(chainValue->items.back()); + } + + enum class ChainType { + Common, + EndWithColon, + EndWithEOP, + HasEOP, + HasKeyword, + Macro + }; + + ChainType specialChainValue(ChainValue_t* chainValue) const { + if (isMacroChain(chainValue)) { + return ChainType::Macro; + } + if (ast_is(chainValue->items.back())) { + return ChainType::EndWithColon; + } + if (ast_is(chainValue->items.back())) { + return ChainType::EndWithEOP; + } + ChainType type = ChainType::Common; + for (auto item : chainValue->items.objects()) { + if (auto colonChain = ast_cast(item)) { + if (ast_is(colonChain->name)) { + type = ChainType::HasKeyword; + } + } else if (ast_is(item)) { + return ChainType::HasEOP; + } + } + return type; + } + + std::string singleVariableFrom(ChainValue_t* chainValue) { + BLOCK_START + BREAK_IF(!chainValue); + BREAK_IF(chainValue->items.size() != 1); + auto callable = ast_cast(chainValue->items.front()); + BREAK_IF(!callable); + ast_node* var = callable->item.as(); + if (!var) { + if (auto self = callable->item.as()) { + var = self->name.as(); + } + } + BREAK_IF(!var); + str_list tmp; + transformCallable(callable, tmp); + return tmp.back(); + BLOCK_END + return Empty; + } + + std::string singleVariableFrom(ast_node* expList) { + if (!ast_is(expList)) return Empty; + BLOCK_START + auto value = singleValueFrom(expList); + BREAK_IF(!value); + auto chainValue = value->item.as(); + BREAK_IF(!chainValue); + BREAK_IF(chainValue->items.size() != 1); + auto callable = ast_cast(chainValue->items.front()); + BREAK_IF(!callable || !(callable->item.is() || callable->getByPath())); + str_list tmp; + transformCallable(callable, tmp); + return tmp.back(); + BLOCK_END + return Empty; + } + + Variable_t* variableFrom(Exp_t* exp) { + BLOCK_START + auto value = singleValueFrom(exp); + BREAK_IF(!value); + auto chainValue = value->item.as(); + BREAK_IF(!chainValue); + BREAK_IF(chainValue->items.size() != 1); + auto callable = ast_cast(chainValue->items.front()); + BREAK_IF(!callable); + return callable->item.as(); + BLOCK_END + return nullptr; + } + + bool isAssignable(const node_container& chainItems) const { + if (chainItems.size() == 1) { + auto firstItem = chainItems.back(); + if (auto callable = ast_cast(firstItem)) { + switch (callable->item->getId()) { + case id(): + case id(): + return true; + } + } else if (firstItem->getId() == id()) { + return true; + } + } else { + auto lastItem = chainItems.back(); + switch (lastItem->getId()) { + case id(): + case id(): + return true; + } + } + return false; + } + + bool isAssignable(Exp_t* exp) const { + if (auto value = singleValueFrom(exp)) { + auto item = value->item.get(); + switch (item->getId()) { + case id(): + return true; + case id(): { + auto simpleValue = static_cast(item); + if (simpleValue->value.is()) { + return true; + } + return false; + } + case id(): { + auto chainValue = static_cast(item); + return isAssignable(chainValue->items.objects()); + } + } + } + return false; + } + + bool isAssignable(Assignable_t* assignable) const { + if (auto assignableChain = ast_cast(assignable->item)) { + return isAssignable(assignableChain->items.objects()); + } + return true; + } + + void checkAssignable(ExpList_t* expList) const { + for (auto exp_ : expList->exprs.objects()) { + Exp_t* exp = static_cast(exp_); + if (!isAssignable(exp)) { + throw std::logic_error(_info.errorMessage("left hand expression is not assignable"sv, exp)); + } + } + } + + bool isPureBackcall(Exp_t* exp) const { + return exp->opValues.empty() && exp->backcalls.size() > 1; + } + + bool isMacroChain(ChainValue_t* chainValue) const { + const auto& chainList = chainValue->items.objects(); + BLOCK_START + auto callable = ast_cast(chainList.front()); + BREAK_IF(!callable); + BREAK_IF(!callable->item.is()); + if (chainList.size() == 1 || + !ast_is(*(++chainList.begin()))) { + throw std::logic_error(_info.errorMessage("macro expression must be followed by arguments list"sv, callable)); + } + return true; + BLOCK_END + return false; + } + + void transformStatement(Statement_t* statement, str_list& out) { + auto x = statement; + if (statement->appendix) { + if (auto assignment = assignmentFrom(statement)) { + auto preDefine = getPredefine(assignment); + if (!preDefine.empty()) out.push_back(preDefine + nll(statement)); + } else if (auto local = statement->content.as()) { + if (!local->defined) { + local->defined = true; + transformLocalDef(local, out); + } + } + auto appendix = statement->appendix.get(); + switch (appendix->item->getId()) { + case id(): { + auto if_line = appendix->item.to(); + auto ifNode = x->new_ptr(); + + auto ifCond = x->new_ptr(); + ifCond->condition.set(if_line->condition); + ifCond->assign.set(if_line->assign); + ifNode->nodes.push_back(ifCond); + + auto stmt = x->new_ptr(); + stmt->content.set(statement->content); + ifNode->nodes.push_back(stmt); + + statement->appendix.set(nullptr); + auto simpleValue = x->new_ptr(); + simpleValue->value.set(ifNode); + auto value = x->new_ptr(); + value->item.set(simpleValue); + auto exp = newExp(value, x); + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(expList); + statement->content.set(expListAssign); + break; + } + case id(): { + auto unless_line = appendix->item.to(); + auto unless = x->new_ptr(); + + auto ifCond = x->new_ptr(); + ifCond->condition.set(unless_line->condition); + unless->nodes.push_back(ifCond); + + auto stmt = x->new_ptr(); + stmt->content.set(statement->content); + unless->nodes.push_back(stmt); + + statement->appendix.set(nullptr); + auto simpleValue = x->new_ptr(); + simpleValue->value.set(unless); + auto value = x->new_ptr(); + value->item.set(simpleValue); + auto exp = newExp(value, x); + auto exprList = x->new_ptr(); + exprList->exprs.push_back(exp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(exprList); + statement->content.set(expListAssign); + break; + } + case id(): { + auto compInner = appendix->item.to(); + auto comp = x->new_ptr(); + comp->forLoop.set(compInner); + auto stmt = x->new_ptr(); + stmt->content.set(statement->content); + comp->value.set(stmt); + auto simpleValue = x->new_ptr(); + simpleValue->value.set(comp); + auto value = x->new_ptr(); + value->item.set(simpleValue); + auto exp = newExp(value, x); + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(expList); + statement->content.set(expListAssign); + statement->appendix.set(nullptr); + break; + } + default: YUEE("AST node mismatch", appendix->item.get()); break; + } + } + auto content = statement->content.get(); + if (!content) { + out.push_back(Empty); + return; + } + switch (content->getId()) { + case id(): transformImport(static_cast(content), out); break; + case id(): transformWhile(static_cast(content), out); break; + case id(): transformRepeat(static_cast(content), out); break; + case id(): transformFor(static_cast(content), out); break; + case id(): transformForEach(static_cast(content), out); break; + case id(): transformReturn(static_cast(content), out); break; + case id(): transformLocal(static_cast(content), out); break; + case id(): transformGlobal(static_cast(content), out); break; + case id(): transformExport(static_cast(content), out); break; + case id(): transformMacro(static_cast(content), out, false); break; + case id(): transformBreakLoop(static_cast(content), out); break; + case id(): transformLabel(static_cast(content), out); break; + case id(): transformGoto(static_cast(content), out); break; + case id(): transformLocalAttrib(static_cast(content), out); break; + case id(): throw std::logic_error(_info.errorMessage("backcall chain must be following a value"sv, x)); break; + case id(): { + auto expListAssign = static_cast(content); + if (expListAssign->action) { + transformAssignment(expListAssign, out); + } else { + auto expList = expListAssign->expList.get(); + if (expList->exprs.objects().empty()) { + out.push_back(Empty); + break; + } + if (auto singleValue = singleValueFrom(expList)) { + if (auto simpleValue = singleValue->item.as()) { + auto value = simpleValue->value.get(); + bool specialSingleValue = true; + switch (value->getId()) { + case id(): transformIf(static_cast(value), out, ExpUsage::Common); break; + case id(): transformClassDecl(static_cast(value), out, ExpUsage::Common); break; + case id(): transformUnless(static_cast(value), out, ExpUsage::Common); break; + case id(): transformSwitch(static_cast(value), out, ExpUsage::Common); break; + case id(): transformWith(static_cast(value), out); break; + case id(): transformForEach(static_cast(value), out); break; + case id(): transformFor(static_cast(value), out); break; + case id(): transformWhile(static_cast(value), out); break; + case id(): transformDo(static_cast(value), out, ExpUsage::Common); break; + case id(): transformCompCommon(static_cast(value), out); break; + default: specialSingleValue = false; break; + } + if (specialSingleValue) { + break; + } + } + if (auto chainValue = singleValue->item.as()) { + if (isChainValueCall(chainValue)) { + transformChainValue(chainValue, out, ExpUsage::Common); + break; + } + } + } else if (expList->exprs.size() == 1){ + auto exp = static_cast(expList->exprs.back()); + if (isPureBackcall(exp)) { + transformExp(exp, out, ExpUsage::Common); + break; + } + } + throw std::logic_error(_info.errorMessage("expression list is not supported here"sv, expList)); + } + break; + } + default: YUEE("AST node mismatch", content); break; + } + if (statement->needSep && !out.back().empty()) { + auto index = std::string::npos; + if (_config.reserveLineNumber) { + index = out.back().rfind(" -- "sv); + } else { + index = out.back().find_last_not_of('\n'); + if (index != std::string::npos) index++; + } + if (index != std::string::npos) { + auto ending = out.back().substr(0, index); + auto ind = ending.find_last_of(" \t\n"sv); + if (ind != std::string::npos) { + ending = ending.substr(ind + 1); + } + if (Keywords.find(ending) == Keywords.end()) { + out.back().insert(index, ";"sv); + } + } + } + } + + str_list getAssignVars(ExpListAssign_t* assignment) { + str_list vars; + if (!assignment->action.is()) return vars; + for (auto exp : assignment->expList->exprs.objects()) { + auto var = singleVariableFrom(exp); + vars.push_back(var.empty() ? Empty : var); + } + return vars; + } + + str_list getAssignVars(With_t* with) { + str_list vars; + for (auto exp : with->valueList->exprs.objects()) { + auto var = singleVariableFrom(exp); + vars.push_back(var.empty() ? Empty : var); + } + return vars; + } + + str_list getAssignDefs(ExpList_t* expList) { + str_list preDefs; + for (auto exp_ : expList->exprs.objects()) { + auto exp = static_cast(exp_); + if (auto value = singleValueFrom(exp)) { + if (auto chain = value->item.as()) { + BLOCK_START + BREAK_IF(chain->items.size() != 1); + auto callable = ast_cast(chain->items.front()); + BREAK_IF(!callable); + std::string name; + if (auto var = callable->item.as()) { + name = _parser.toString(var); + } else if (auto self = callable->item.as()) { + if (self->name.is()) name = "self"sv; + } + BREAK_IF(name.empty()); + if (!isDefined(name)) { + preDefs.push_back(name); + } + BLOCK_END + } + } else { + throw std::logic_error(_info.errorMessage("left hand expression is not assignable"sv, exp)); + } + } + return preDefs; + } + + str_list transformAssignDefs(ExpList_t* expList, bool markDefined = true) { + str_list defs; + for (auto exp_ : expList->exprs.objects()) { + auto exp = static_cast(exp_); + if (auto value = singleValueFrom(exp)) { + if (auto chain = value->item.as()) { + BLOCK_START + BREAK_IF(chain->items.size() != 1); + auto callable = ast_cast(chain->items.front()); + BREAK_IF(!callable); + std::string name; + if (auto var = callable->item.as()) { + name = _parser.toString(var); + } else if (auto self = callable->item.as()) { + if (self->name.is()) name = "self"sv; + } + BREAK_IF(name.empty()); + if (!markDefined || addToScope(name)) { + defs.push_back(name); + } + BLOCK_END + } + } else { + throw std::logic_error(_info.errorMessage("left hand expression is not assignable"sv, exp)); + } + } + return defs; + } + + std::string getPredefine(const str_list& defs) { + if (defs.empty()) return Empty; + return indent() + s("local "sv) + join(defs, ", "sv); + } + + std::string getDestrucureDefine(ExpListAssign_t* assignment) { + auto info = extractDestructureInfo(assignment, true); + if (!info.first.empty()) { + for (const auto& destruct : info.first) { + str_list defs; + for (const auto& item : destruct.items) { + if (item.isVariable && addToScope(item.name)) { + defs.push_back(item.name); + } + } + if (!defs.empty()) _buf << indent() << "local "sv << join(defs,", "sv); + } + } + return clearBuf(); + } + + std::string getPredefine(ExpListAssign_t* assignment) { + auto preDefine = getDestrucureDefine(assignment); + if (preDefine.empty()) { + preDefine = getPredefine(transformAssignDefs(assignment->expList)); + } + return preDefine; + } + + ExpList_t* expListFrom(Statement_t* statement) { + if (auto expListAssign = statement->content.as()) { + if (!expListAssign->action) { + return expListAssign->expList.get(); + } + } + return nullptr; + } + + ExpListAssign_t* assignmentFrom(Statement_t* statement) { + if (auto expListAssign = statement->content.as()) { + if (expListAssign->action) { + return expListAssign; + } + } + return nullptr; + } + + void transformAssignment(ExpListAssign_t* assignment, str_list& out) { + checkAssignable(assignment->expList); + BLOCK_START + auto assign = ast_cast(assignment->action); + BREAK_IF(!assign); + BREAK_IF(assign->values.objects().size() != 1); + auto value = assign->values.objects().back(); + if (ast_is(value)) { + if (auto val = simpleSingleValueFrom(value)) { + value = val->value.get(); + } + } + switch (value->getId()) { + case id(): + case id(): { + auto expList = assignment->expList.get(); + str_list temp; + auto defs = transformAssignDefs(expList); + if (!defs.empty()) temp.push_back(getPredefine(defs) + nll(expList)); + switch (value->getId()) { + case id(): transformIf(static_cast(value), temp, ExpUsage::Assignment, expList); break; + case id(): transformUnless(static_cast(value), temp, ExpUsage::Assignment, expList); break; + } + out.push_back(join(temp)); + return; + } + case id(): { + auto switchNode = static_cast(value); + auto assignList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformSwitch(switchNode, out, ExpUsage::Assignment, assignList); + out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); + return; + } + case id(): { + auto withNode = static_cast(value); + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformWith(withNode, out, expList); + out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); + return; + } + case id(): { + auto expList = assignment->expList.get(); + auto doNode = static_cast(value); + std::string preDefine = getPredefine(assignment); + transformDo(doNode, out, ExpUsage::Assignment, expList); + out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); + return; + } + case id(): { + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformComprehension(static_cast(value), out, ExpUsage::Assignment, expList); + out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); + return; + } + case id(): { + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformTblComprehension(static_cast(value), out, ExpUsage::Assignment, expList); + out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); + return; + } + case id(): { + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformForInPlace(static_cast(value), out, expList); + out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); + return; + } + case id(): { + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformForEachInPlace(static_cast(value), out, expList); + out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); + return; + } + case id(): { + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformClassDecl(static_cast(value), out, ExpUsage::Assignment, expList); + out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); + return; + } + case id(): { + auto expList = assignment->expList.get(); + std::string preDefine = getPredefine(assignment); + transformWhileInPlace(static_cast(value), out, expList); + out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); + return; + } + } + auto exp = ast_cast(value); + BREAK_IF(!exp); + if (isPureBackcall(exp)) { + auto expList = assignment->expList.get(); + transformExp(exp, out, ExpUsage::Assignment, expList); + return; + } + auto singleVal = singleValueFrom(exp); + BREAK_IF(!singleVal); + if (auto chainValue = singleVal->item.as()) { + auto type = specialChainValue(chainValue); + auto expList = assignment->expList.get(); + switch (type) { + case ChainType::HasEOP: + case ChainType::EndWithColon: { + std::string preDefine = getPredefine(assignment); + transformChainValue(chainValue, out, ExpUsage::Assignment, expList); + out.back().insert(0, preDefine.empty() ? Empty : preDefine + nll(assignment)); + return; + } + case ChainType::HasKeyword: + case ChainType::Macro: + transformChainValue(chainValue, out, ExpUsage::Assignment, expList); + return; + case ChainType::Common: + case ChainType::EndWithEOP: + break; + } + } + BLOCK_END + auto info = extractDestructureInfo(assignment); + if (info.first.empty()) { + transformAssignmentCommon(assignment, out); + } else { + str_list temp; + for (const auto& destruct : info.first) { + if (destruct.items.size() == 1) { + auto& pair = destruct.items.front(); + _buf << indent(); + if (pair.isVariable && !isDefined(pair.name)) { + _buf << s("local "sv); + } + _buf << pair.name << " = "sv << info.first.front().value << pair.structure << nll(assignment); + addToScope(pair.name); + temp.push_back(clearBuf()); + } else if (_parser.match(destruct.value)) { + str_list defs, names, values; + for (const auto& item : destruct.items) { + if (item.isVariable && addToScope(item.name)) { + defs.push_back(item.name); + } + names.push_back(item.name); + values.push_back(item.structure); + } + for (auto& v : values) v.insert(0, destruct.value); + if (defs.empty()) { + _buf << indent() << join(names, ", "sv) << " = "sv << join(values, ", "sv) << nll(assignment); + } else { + _buf << indent() << "local "sv; + if (defs.size() != names.size()) { + _buf << join(defs,", "sv) << nll(assignment) << indent(); + } + _buf << join(names, ", "sv) << " = "sv << join(values, ", "sv) << nll(assignment); + } + temp.push_back(clearBuf()); + } else { + str_list defs, names, values; + for (const auto& item : destruct.items) { + if (item.isVariable && addToScope(item.name)) { + defs.push_back(item.name); + } + names.push_back(item.name); + values.push_back(item.structure); + } + if (!defs.empty()) _buf << indent() << "local "sv << join(defs,", "sv) << nll(assignment); + _buf << indent() << "do"sv << nll(assignment); + pushScope(); + auto objVar = getUnusedName("_obj_"sv); + for (auto& v : values) v.insert(0, objVar); + _buf << indent() << "local "sv << objVar << " = "sv << destruct.value << nll(assignment); + _buf << indent() << join(names, ", "sv) << " = "sv << join(values, ", "sv) << nll(assignment); + popScope(); + _buf << indent() << "end"sv << nll(assignment); + temp.push_back(clearBuf()); + } + } + if (info.second) { + transformAssignmentCommon(info.second, temp); + } + out.push_back(join(temp)); + } + } + + void transformAssignItem(ast_node* value, str_list& out) { + switch (value->getId()) { + case id(): transformWithClosure(static_cast(value), out); break; + case id(): transformIf(static_cast(value), out, ExpUsage::Closure); break; + case id(): transformSwitch(static_cast(value), out, ExpUsage::Closure); break; + case id(): transformTableBlock(static_cast(value), out); break; + case id(): transformExp(static_cast(value), out, ExpUsage::Closure); break; + default: YUEE("AST node mismatch", value); break; + } + } + + std::list destructFromExp(ast_node* node) { + const node_container* tableItems = nullptr; + if (ast_is(node)) { + auto item = singleValueFrom(node)->item.get(); + if (!item) throw std::logic_error(_info.errorMessage("invalid destructure value"sv, node)); + auto tbA = item->getByPath(); + if (tbA) { + tableItems = &tbA->values.objects(); + } else { + auto tbB = ast_cast(item); + if (tbB) tableItems = &tbB->pairs.objects(); + } + } else if (auto table = ast_cast(node)) { + tableItems = &table->values.objects(); + } + std::list pairs; + int index = 0; + for (auto pair : *tableItems) { + switch (pair->getId()) { + case id(): { + ++index; + if (!isAssignable(static_cast(pair))) { + throw std::logic_error(_info.errorMessage("can't destructure value"sv, pair)); + } + auto value = singleValueFrom(pair); + auto item = value->item.get(); + if (ast_is(item) || + item->getByPath()) { + auto subPairs = destructFromExp(pair); + for (auto& p : subPairs) { + pairs.push_back({p.isVariable, p.name, + s("["sv) + std::to_string(index) + s("]"sv) + p.structure}); + } + } else { + bool lintGlobal = _config.lintGlobalVariable; + _config.lintGlobalVariable = false; + auto exp = static_cast(pair); + auto varName = singleVariableFrom(exp); + bool isVariable = !varName.empty(); + if (!isVariable) { + str_list temp; + transformExp(exp, temp, ExpUsage::Closure); + varName = std::move(temp.back()); + } + _config.lintGlobalVariable = lintGlobal; + pairs.push_back({ + isVariable, + varName, + s("["sv) + std::to_string(index) + s("]"sv) + }); + } + break; + } + case id(): { + auto vp = static_cast(pair); + auto name = _parser.toString(vp->name); + if (Keywords.find(name) != Keywords.end()) { + pairs.push_back({true, name, s("[\""sv) + name + s("\"]"sv)}); + } else { + pairs.push_back({true, name, s("."sv) + name}); + } + break; + } + case id(): { + auto np = static_cast(pair); + auto key = np->key->getByPath(); + if (!key) throw std::logic_error(_info.errorMessage("invalid key for destructure"sv, np)); + if (auto exp = np->value.as()) { + if (!isAssignable(exp)) throw std::logic_error(_info.errorMessage("can't destructure value"sv, exp)); + auto item = singleValueFrom(exp)->item.get(); + if (ast_is(item) || + item->getByPath()) { + auto subPairs = destructFromExp(exp); + auto name = _parser.toString(key); + for (auto& p : subPairs) { + if (Keywords.find(name) != Keywords.end()) { + pairs.push_back({p.isVariable, p.name, + s("[\""sv) + name + s("\"]"sv) + p.structure}); + } else { + pairs.push_back({p.isVariable, p.name, + s("."sv) + name + p.structure}); + } + } + } else { + bool lintGlobal = _config.lintGlobalVariable; + _config.lintGlobalVariable = false; + auto varName = singleVariableFrom(exp); + bool isVariable = !varName.empty(); + if (!isVariable) { + str_list temp; + transformExp(exp, temp, ExpUsage::Closure); + varName = std::move(temp.back()); + } + _config.lintGlobalVariable = lintGlobal; + auto name = _parser.toString(key); + if (Keywords.find(name) != Keywords.end()) { + pairs.push_back({ + isVariable, + varName, + s("[\""sv) + name + s("\"]"sv) + }); + } else { + pairs.push_back({ + isVariable, + varName, + s("."sv) + name + }); + } + } + break; + } + if (np->value.is()) { + auto subPairs = destructFromExp(pair); + for (auto& p : subPairs) { + pairs.push_back({p.isVariable, p.name, + s("."sv) + _parser.toString(key) + p.structure}); + } + } + break; + } + default: YUEE("AST node mismatch", pair); break; + } + } + return pairs; + } + + std::pair, ast_ptr> + extractDestructureInfo(ExpListAssign_t* assignment, bool varDefOnly = false) { + auto x = assignment; + std::list destructs; + if (!assignment->action.is()) return { destructs, nullptr }; + auto exprs = assignment->expList->exprs.objects(); + auto values = assignment->action.to()->values.objects(); + size_t size = std::max(exprs.size(),values.size()); + ast_ptr var; + if (exprs.size() < size) { + var = toAst("_"sv, x); + while (exprs.size() < size) exprs.emplace_back(var); + } + ast_ptr nullNode; + if (values.size() < size) { + nullNode = toAst("nil"sv, x); + while (values.size() < size) values.emplace_back(nullNode); + } + using iter = node_container::iterator; + std::vector> destructPairs; + str_list temp; + for (auto i = exprs.begin(), j = values.begin(); i != exprs.end(); ++i, ++j) { + auto expr = *i; + auto value = singleValueFrom(expr); + ast_node* destructNode = value->getByPath(); + if (destructNode || (destructNode = value->item.as())) { + destructPairs.push_back({i,j}); + auto& destruct = destructs.emplace_back(); + if (!varDefOnly) { + pushScope(); + transformAssignItem(*j, temp); + destruct.value = temp.back(); + temp.pop_back(); + popScope(); + } + auto pairs = destructFromExp(expr); + destruct.items = std::move(pairs); + if (destruct.items.size() == 1 && !singleValueFrom(*j)) { + destruct.value.insert(0, "("sv); + destruct.value.append(")"sv); + } + } + } + for (const auto& p : destructPairs) { + exprs.erase(p.first); + values.erase(p.second); + } + ast_ptr newAssignment; + if (!destructPairs.empty() && !exprs.empty()) { + auto x = assignment; + auto expList = x->new_ptr(); + auto newAssign = x->new_ptr(); + newAssign->expList.set(expList); + for (auto expr : exprs) expList->exprs.push_back(expr); + auto assign = x->new_ptr(); + for (auto value : values) assign->values.push_back(value); + newAssign->action.set(assign); + newAssignment = newAssign; + } + return {std::move(destructs), newAssignment}; + } + + void transformAssignmentCommon(ExpListAssign_t* assignment, str_list& out) { + auto x = assignment; + str_list temp; + auto expList = assignment->expList.get(); + auto action = assignment->action.get(); + switch (action->getId()) { + case id(): { + if (expList->exprs.size() > 1) throw std::logic_error(_info.errorMessage("can not apply update to multiple values"sv, expList)); + auto update = static_cast(action); + auto leftExp = static_cast(expList->exprs.objects().front()); + auto leftValue = singleValueFrom(leftExp); + if (!leftValue) throw std::logic_error(_info.errorMessage("left hand expression is not assignable"sv, leftExp)); + if (auto chain = leftValue->item.as()) { + auto tmpChain = x->new_ptr(); + for (auto item : chain->items.objects()) { + bool itemAdded = false; + BLOCK_START + auto exp = ast_cast(item); + BREAK_IF(!exp); + auto var = singleVariableFrom(exp); + BREAK_IF(!var.empty()); + auto upVar = getUnusedName("_update_"sv); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst(upVar, x)); + auto assign = x->new_ptr(); + assign->values.push_back(exp); + assignment->action.set(assign); + transformAssignment(assignment, temp); + tmpChain->items.push_back(toAst(upVar, x)); + itemAdded = true; + BLOCK_END + if (!itemAdded) tmpChain->items.push_back(item); + } + chain->items.clear(); + chain->items.dup(tmpChain->items); + } + transformValue(leftValue, temp); + auto left = std::move(temp.back()); + temp.pop_back(); + transformExp(update->value, temp, ExpUsage::Closure); + auto right = std::move(temp.back()); + temp.pop_back(); + if (!singleValueFrom(update->value)) { + right = s("("sv) + right + s(")"sv); + } + _buf << join(temp) << indent() << left << " = "sv << left << + " "sv << _parser.toString(update->op) << " "sv << right << nll(assignment); + out.push_back(clearBuf()); + break; + } + case id(): { + bool oneLined = true; + auto assign = static_cast(action); + for (auto val : assign->values.objects()) { + if (auto value = singleValueFrom(val)) { + if (auto spValue = value->item.as()) { + if (spValue->value.is()) { + oneLined = false; + break; + } + } + } + } + auto defs = getAssignDefs(expList); + if (oneLined && defs.size() == expList->exprs.objects().size()) { + for (auto value : assign->values.objects()) { + transformAssignItem(value, temp); + } + std::string preDefine = getPredefine(defs); + for (const auto& def : defs) { + addToScope(def); + } + if (preDefine.empty()) { + transformExpList(expList, temp); + std::string left = std::move(temp.back()); + temp.pop_back(); + out.push_back(indent() + left + s(" = "sv) + join(temp, ", "sv) + nll(assignment)); + } else { + out.push_back(preDefine + s(" = "sv) + join(temp, ", "sv) + nll(assignment)); + } + } else { + std::string preDefine = getPredefine(defs); + for (const auto& def : defs) { + addToScope(def); + } + transformExpList(expList, temp); + std::string left = temp.back(); + temp.pop_back(); + for (auto value : assign->values.objects()) { + transformAssignItem(value, temp); + } + out.push_back((preDefine.empty() ? Empty : preDefine + nll(assignment)) + indent() + left + s(" = "sv) + join(temp, ", "sv) + nll(assignment)); + } + break; + } + default: YUEE("AST node mismatch", action); break; + } + } + + void transformCond(const node_container& nodes, str_list& out, ExpUsage usage, bool unless, ExpList_t* assignList) { + std::vector> ns(false); + for (auto it = nodes.rbegin(); it != nodes.rend(); ++it) { + ns.push_back(*it); + if (auto cond = ast_cast(*it)) { + if (*it != nodes.front() && cond->assign) { + auto x = *it; + auto newIf = x->new_ptr(); + for (auto j = ns.rbegin(); j != ns.rend(); ++j) { + newIf->nodes.push_back(*j); + } + ns.clear(); + auto simpleValue = x->new_ptr(); + simpleValue->value.set(newIf); + auto value = x->new_ptr(); + value->item.set(simpleValue); + auto exp = newExp(value, x); + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(expList); + auto stmt = x->new_ptr(); + stmt->content.set(expListAssign); + ns.push_back(stmt.get()); + } + } + } + if (nodes.size() != ns.size()) { + auto x = ns.back(); + auto newIf = x->new_ptr(); + for (auto j = ns.rbegin(); j != ns.rend(); ++j) { + newIf->nodes.push_back(*j); + } + transformCond(newIf->nodes.objects(), out, usage, unless, assignList); + return; + } + str_list temp; + if (usage == ExpUsage::Closure) { + temp.push_back(s("(function()"sv) + nll(nodes.front())); + pushScope(); + _enableReturn.push(true); + } + std::list> ifCondPairs; + ifCondPairs.emplace_back(); + for (auto node : nodes) { + switch (node->getId()) { + case id(): + ifCondPairs.back().first = static_cast(node); + break; + case id(): + case id(): + ifCondPairs.back().second = node; + ifCondPairs.emplace_back(); + break; + default: YUEE("AST node mismatch", node); break; + } + } + auto assign = ifCondPairs.front().first->assign.get(); + bool storingValue = false; + ast_ptr extraAssignment; + if (assign) { + auto exp = ifCondPairs.front().first->condition.get(); + auto x = exp; + bool lintGlobal = _config.lintGlobalVariable; + _config.lintGlobalVariable = false; + auto var = singleVariableFrom(exp); + _config.lintGlobalVariable = lintGlobal; + if (var.empty()) { + storingValue = true; + auto desVar = getUnusedName("_des_"sv); + if (assign->values.objects().size() == 1) { + auto var = singleVariableFrom(assign->values.objects().front()); + if (!var.empty()) { + desVar = var; + storingValue = false; + } + } + if (storingValue) { + if (usage != ExpUsage::Closure) { + temp.push_back(indent() + s("do"sv) + nll(assign)); + pushScope(); + } + auto expList = toAst(desVar, x); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + { + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto assignOne = x->new_ptr(); + auto valExp = toAst(desVar, x); + assignOne->values.push_back(valExp); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assignOne); + extraAssignment.set(assignment); + ifCondPairs.front().first->condition.set(valExp); + } + } else { + if (!isDefined(var)) { + storingValue = true; + if (usage != ExpUsage::Closure) { + temp.push_back(indent() + s("do"sv) + nll(assign)); + pushScope(); + } + } + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + } + for (const auto& pair : ifCondPairs) { + if (pair.first) { + str_list tmp; + auto condition = pair.first->condition.get(); + if (unless) { + if (auto value = singleValueFrom(condition)) { + transformValue(value, tmp); + } else { + transformExp(condition, tmp, ExpUsage::Closure); + tmp.back() = s("("sv) + tmp.back() + s(")"sv); + } + tmp.back().insert(0, s("not "sv)); + unless = false; + } else { + transformExp(condition, tmp, ExpUsage::Closure); + } + _buf << indent(); + if (pair != ifCondPairs.front()) { + _buf << "else"sv; + } + _buf << "if "sv << tmp.back() << " then"sv << nll(condition); + temp.push_back(clearBuf()); + } + if (pair.second) { + if (!pair.first) { + temp.push_back(indent() + s("else"sv) + nll(pair.second)); + } + pushScope(); + if (pair == ifCondPairs.front() && extraAssignment) { + transformAssignment(extraAssignment, temp); + } + transform_plain_body(pair.second, temp, usage, assignList); + popScope(); + } + if (!pair.first) { + temp.push_back(indent() + s("end"sv) + nll(nodes.front())); + break; + } + } + if (storingValue && usage != ExpUsage::Closure) { + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(nodes.front())); + } + if (usage == ExpUsage::Closure) { + _enableReturn.pop(); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + } + out.push_back(join(temp)); + } + + void transformIf(If_t* ifNode, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { + transformCond(ifNode->nodes.objects(), out, usage, false, assignList); + } + + void transformUnless(Unless_t* unless, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { + transformCond(unless->nodes.objects(), out, usage, true, assignList); + } + + void transformExpList(ExpList_t* expList, str_list& out) { + str_list temp; + for (auto exp : expList->exprs.objects()) { + transformExp(static_cast(exp), temp, ExpUsage::Closure); + } + out.push_back(join(temp, ", "sv)); + } + + void transformExpListLow(ExpListLow_t* expListLow, str_list& out) { + str_list temp; + for (auto exp : expListLow->exprs.objects()) { + transformExp(static_cast(exp), temp, ExpUsage::Closure); + } + out.push_back(join(temp, ", "sv)); + } + + void transform_backcall_exp(const node_container& values, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { + if (values.size() == 1 && usage == ExpUsage::Closure) { + transform_unary_exp(static_cast(values.front()), out); + } else { + auto x = values.front(); + auto arg = newExp(static_cast(x), x); + auto begin = values.begin(); begin++; + for (auto it = begin; it != values.end(); ++it) { + auto unary = static_cast(*it); + auto value = static_cast(singleUnaryExpFrom(unary) ? unary->expos.back() : nullptr); + if (values.back() == *it && !unary->ops.empty() && usage == ExpUsage::Common) { + throw std::logic_error(_info.errorMessage("expression list is not supported here"sv, x)); + } + if (!value) throw std::logic_error(_info.errorMessage("backcall operator must be followed by chain value"sv, *it)); + if (auto chainValue = value->item.as()) { + if (isChainValueCall(chainValue)) { + auto last = chainValue->items.back(); + _ast_list* args = nullptr; + if (auto invoke = ast_cast(last)) { + args = &invoke->args; + } else { + args = &(ast_to(last)->args); + } + bool findPlaceHolder = false; + for (auto a : args->objects()) { + bool lintGlobal = _config.lintGlobalVariable; + _config.lintGlobalVariable = false; + auto name = singleVariableFrom(a); + _config.lintGlobalVariable = lintGlobal; + if (name == "_"sv) { + if (!findPlaceHolder) { + args->swap(a, arg); + findPlaceHolder = true; + } else { + throw std::logic_error(_info.errorMessage("backcall placeholder can be used only in one place"sv, a)); + } + } + } + if (!findPlaceHolder) { + args->push_front(arg); + } + } else { + auto invoke = x->new_ptr(); + invoke->args.push_front(arg); + chainValue->items.push_back(invoke); + } + arg.set(newExp(unary, x)); + } else { + throw std::logic_error(_info.errorMessage("backcall operator must be followed by chain value"sv, value)); + } + } + switch (usage) { + case ExpUsage::Assignment: { + auto assignment = x->new_ptr(); + auto assign = x->new_ptr(); + assign->values.push_back(arg); + assignment->action.set(assign); + assignment->expList.set(assignList); + transformAssignment(assignment, out); + return; + } + case ExpUsage::Common: { + auto value = singleValueFrom(arg); + if (value && value->item.is()) { + transformChainValue(value->item.to(), out, ExpUsage::Common); + } else { + transformExp(arg, out, ExpUsage::Closure); + out.back().insert(0, indent()); + out.back().append(nlr(x)); + } + return; + } + case ExpUsage::Return: { + auto ret = x->new_ptr(); + auto expListLow = x->new_ptr(); + expListLow->exprs.push_back(arg); + ret->valueList.set(expListLow); + transformReturn(ret, out); + return; + } + case ExpUsage::Closure: { + transformExp(arg, out, ExpUsage::Closure); + return; + } + default: YUEE("invalid expression usage", x); return; + } + } + } + + void transformExp(Exp_t* exp, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { + if (exp->opValues.empty()) { + transform_backcall_exp(exp->backcalls.objects(), out, usage, assignList); + return; + } + if (usage != ExpUsage::Closure) { + YUEE("invalid expression usage", exp); + } + str_list temp; + transform_backcall_exp(exp->backcalls.objects(), temp, ExpUsage::Closure); + for (auto _opValue : exp->opValues.objects()) { + auto opValue = static_cast(_opValue); + transformBinaryOperator(opValue->op, temp); + transform_backcall_exp(opValue->backcalls.objects(), temp, ExpUsage::Closure); + } + out.push_back(join(temp, " "sv)); + } + + void transformValue(Value_t* value, str_list& out) { + auto item = value->item.get(); + switch (item->getId()) { + case id(): transformSimpleValue(static_cast(item), out); break; + case id(): transform_simple_table(static_cast(item), out); break; + case id(): transformChainValue(static_cast(item), out, ExpUsage::Closure); break; + case id(): transformString(static_cast(item), out); break; + default: YUEE("AST node mismatch", value); break; + } + } + + void transformCallable(Callable_t* callable, str_list& out, const ast_sel& invoke = {}) { + auto item = callable->item.get(); + switch (item->getId()) { + case id(): { + transformVariable(static_cast(item), out); + if (_config.lintGlobalVariable && !isDefined(out.back())) { + if (_globals.find(out.back()) == _globals.end()) { + _globals[out.back()] = {item->m_begin.m_line, item->m_begin.m_col}; + } + } + break; + } + case id(): { + transformSelfName(static_cast(item), out, invoke); + if (_config.lintGlobalVariable) { + std::string self("self"sv); + if (!isDefined(self)) { + if (_globals.find(self) == _globals.end()) { + _globals[self] = {item->m_begin.m_line, item->m_begin.m_col}; + } + } + } + break; + } + case id(): + if (_varArgs.empty() || !_varArgs.top()) { + throw std::logic_error(_info.errorMessage("cannot use '...' outside a vararg function near '...'"sv, item)); + } + out.push_back(s("..."sv)); + break; + case id(): transformParens(static_cast(item), out); break; + default: YUEE("AST node mismatch", item); break; + } + } + + void transformParens(Parens_t* parans, str_list& out) { + str_list temp; + transformExp(parans->expr, temp, ExpUsage::Closure); + out.push_back(s("("sv) + temp.front() + s(")"sv)); + } + + void transformSimpleValue(SimpleValue_t* simpleValue, str_list& out) { + auto value = simpleValue->value.get(); + switch (value->getId()) { + case id(): transform_const_value(static_cast(value), out); break; + case id(): transformIf(static_cast(value), out, ExpUsage::Closure); break; + case id(): transformUnless(static_cast(value), out, ExpUsage::Closure); break; + case id(): transformSwitch(static_cast(value), out, ExpUsage::Closure); break; + case id(): transformWithClosure(static_cast(value), out); break; + case id(): transformClassDeclClosure(static_cast(value), out); break; + case id(): transformForEachClosure(static_cast(value), out); break; + case id(): transformForClosure(static_cast(value), out); break; + case id(): transformWhileClosure(static_cast(value), out); break; + case id(): transformDo(static_cast(value), out, ExpUsage::Closure); break; + case id(): transform_unary_value(static_cast(value), out); break; + case id(): transformTblComprehension(static_cast(value), out, ExpUsage::Closure); break; + case id(): transformTableLit(static_cast(value), out); break; + case id(): transformComprehension(static_cast(value), out, ExpUsage::Closure); break; + case id(): transformFunLit(static_cast(value), out); break; + case id(): transformNum(static_cast(value), out); break; + default: YUEE("AST node mismatch", value); break; + } + } + + void transformFunLit(FunLit_t* funLit, str_list& out) { + _enableReturn.push(true); + _varArgs.push(false); + str_list temp; + bool isFatArrow = _parser.toString(funLit->arrow) == "=>"sv; + pushScope(); + if (isFatArrow) { + forceAddToScope(s("self"sv)); + } + if (auto argsDef = funLit->argsDef.get()) { + transformFnArgsDef(argsDef, temp); + if (funLit->body) { + transformBody(funLit->body, temp, ExpUsage::Return); + } else { + temp.push_back(Empty); + } + auto it = temp.begin(); + auto& args = *it; + auto& initArgs = *(++it); + auto& bodyCodes = *(++it); + _buf << "function("sv; + if (isFatArrow) { + _buf << "self"sv; + if (!args.empty()) _buf << ", "sv; + } + _buf << args << ')'; + if (!initArgs.empty() || !bodyCodes.empty()) { + _buf << nlr(argsDef) << initArgs << bodyCodes; + popScope(); + _buf << indent() << "end"sv; + } else { + popScope(); + _buf << " end"sv; + } + } else { + if (funLit->body) { + transformBody(funLit->body, temp, ExpUsage::Return); + } else { + temp.push_back(Empty); + } + auto& bodyCodes = temp.back(); + _buf << "function("sv << + (isFatArrow ? s("self"sv) : Empty) << + ')'; + if (!bodyCodes.empty()) { + _buf << nll(funLit) << bodyCodes; + popScope(); + _buf << indent() << "end"sv; + } else { + popScope(); + _buf << " end"sv; + } + } + out.push_back(clearBuf()); + _enableReturn.pop(); + _varArgs.pop(); + } + + void transformBody(Body_t* body, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { + auto x = body; + if (auto stmt = body->content.as()) { + auto block = x->new_ptr(); + block->statements.push_back(stmt); + transformBlock(block, out, usage, assignList); + } else { + transformBlock(body->content.to(), out, usage, assignList); + } + } + + void transformBlock(Block_t* block, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr, bool isRoot = false) { + const auto& nodes = block->statements.objects(); + LocalMode mode = LocalMode::None; + Local_t* any = nullptr, *capital = nullptr; + for (auto it = nodes.begin(); it != nodes.end(); ++it) { + auto node = *it; + auto stmt = static_cast(node); + if (auto backcallBody = stmt->content.as()) { + auto x = stmt; + bool cond = false; + BLOCK_START + BREAK_IF(it == nodes.begin()); + auto last = it; --last; + auto lst = static_cast(*last); + if (lst->appendix) { + throw std::logic_error(_info.errorMessage("statement decorator must be placed at the end of backcall chain"sv, lst->appendix.get())); + } + lst->appendix.set(stmt->appendix); + stmt->appendix.set(nullptr); + lst->needSep.set(stmt->needSep); + stmt->needSep.set(nullptr); + auto exp = lastExpFromStatement(lst); + BREAK_IF(!exp); + for (auto val : backcallBody->values.objects()) { + exp->backcalls.push_back(val); + } + cond = true; + BLOCK_END + if (!cond) throw std::logic_error(_info.errorMessage("backcall chain must be following a value"sv, x)); + stmt->content.set(nullptr); + auto next = it; ++next; + BLOCK_START + BREAK_IF(next == nodes.end()); + BREAK_IF(!static_cast(*next)->content.as()); + throw std::logic_error(_info.errorMessage("indent mismatch in backcall chain"sv, *next)); + BLOCK_END + } else if (auto backcall = stmt->content.as()) { + auto x = *nodes.begin(); + auto newBlock = x->new_ptr(); + if (it != nodes.begin()) { + for (auto i = nodes.begin(); i != it; ++i) { + newBlock->statements.push_back(*i); + } + } + x = backcall; + ast_ptr arg; + { + auto block = x->new_ptr(); + auto next = it; ++next; + if (next != nodes.end()) { + for (auto i = next; i != nodes.end(); ++i) { + block->statements.push_back(*i); + } + } + auto body = x->new_ptr(); + body->content.set(block); + auto funLit = x->new_ptr(); + funLit->argsDef.set(backcall->argsDef); + auto arrow = _parser.toString(backcall->arrow); + funLit->arrow.set(toAst(arrow == "<-"sv ? "->"sv : "=>"sv, x)); + funLit->body.set(body); + auto simpleValue = x->new_ptr(); + simpleValue->value.set(funLit); + auto value = x->new_ptr(); + value->item.set(simpleValue); + arg = newExp(value, x); + } + if (isChainValueCall(backcall->value)) { + auto last = backcall->value->items.back(); + _ast_list* args = nullptr; + if (auto invoke = ast_cast(last)) { + args = &invoke->args; + } else { + args = &(ast_to(last)->args); + } + bool findPlaceHolder = false; + for (auto a : args->objects()) { + bool lintGlobal = _config.lintGlobalVariable; + _config.lintGlobalVariable = false; + auto name = singleVariableFrom(a); + _config.lintGlobalVariable = lintGlobal; + if (name == "_"sv) { + if (!findPlaceHolder) { + args->swap(a, arg); + findPlaceHolder = true; + } else { + throw std::logic_error(_info.errorMessage("backcall placeholder can be used only in one place"sv, a)); + } + } + } + if (!findPlaceHolder) { + args->push_back(arg); + } + } else { + auto invoke = x->new_ptr(); + invoke->args.push_back(arg); + backcall->value->items.push_back(invoke); + } + auto newStmt = x->new_ptr(); + { + auto chainValue = backcall->value.get(); + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = newExp(value, x); + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(expList); + newStmt->content.set(expListAssign); + newStmt->appendix.set(stmt->appendix); + newBlock->statements.push_back(newStmt); + } + transformBlock(newBlock, out, usage, assignList, isRoot); + return; + } + if (auto local = stmt->content.as()) { + if (!local->collected) { + local->collected = true; + switch (local->item->getId()) { + case id(): { + auto flag = local->item.to(); + LocalMode newMode = _parser.toString(flag) == "*"sv ? LocalMode::Any : LocalMode::Capital; + if (int(newMode) > int(mode)) { + mode = newMode; + } + if (mode == LocalMode::Any) { + if (!any) any = local; + if (!capital) capital = local; + } else { + if (!capital) capital = local; + } + break; + } + case id(): { + auto values = local->item.to(); + for (auto name : values->nameList->names.objects()) { + local->forceDecls.push_back(_parser.toString(name)); + } + break; + } + } + } + } else if (mode != LocalMode::None) { + ClassDecl_t* classDecl = nullptr; + if (auto assignment = assignmentFrom(stmt)) { + auto vars = getAssignVars(assignment); + for (const auto& var : vars) { + if (var.empty()) continue; + if (std::isupper(var[0]) && capital) { + capital->decls.push_back(var); + } else if (any) { + any->decls.push_back(var); + } + } + auto info = extractDestructureInfo(assignment, true); + if (!info.first.empty()) { + for (const auto& destruct : info.first) + for (const auto& item : destruct.items) + if (item.isVariable) { + if (std::isupper(item.name[0]) && capital) { capital->decls.push_back(item.name); + } else if (any) { + any->decls.push_back(item.name); + } + } + } + if (info.second) { + auto defs = transformAssignDefs(info.second->expList, false); + for (const auto& def : defs) { + if (std::isupper(def[0]) && capital) { capital->decls.push_back(def); + } else if (any) { + any->decls.push_back(def); + } + } + } + BLOCK_START + auto assign = assignment->action.as(); + BREAK_IF(!assign); + BREAK_IF(assign->values.objects().size() != 1); + auto exp = ast_cast(assign->values.objects().front()); + BREAK_IF(!exp); + auto value = singleValueFrom(exp); + classDecl = value->getByPath(); + BLOCK_END + } else if (auto expList = expListFrom(stmt)) { + auto value = singleValueFrom(expList); + classDecl = value->getByPath(); + } + if (classDecl) { + if (auto variable = classDecl->name->item.as()) { + auto className = _parser.toString(variable); + if (!className.empty()) { + if (std::isupper(className[0]) && capital) { + capital->decls.push_back(className); + } else if (any) { + any->decls.push_back(className); + } + } + } + } + } + } + if (isRoot && !_info.moduleName.empty()) { + block->statements.push_front(toAst(_info.moduleName + s(_info.exportDefault ? "=nil"sv : "={}"sv), block)); + } + switch (usage) { + case ExpUsage::Closure: + case ExpUsage::Return: { + BLOCK_START + BREAK_IF(isRoot && !_info.moduleName.empty()); + auto last = lastStatementFrom(nodes); + BREAK_IF(!last); + auto x = last; + auto expList = expListFrom(last); + BREAK_IF(!expList || + (last->appendix && + last->appendix->item.is())); + auto expListLow = x->new_ptr(); + expListLow->exprs.dup(expList->exprs); + auto returnNode = x->new_ptr(); + returnNode->valueList.set(expListLow); + returnNode->allowBlockMacroReturn = true; + last->content.set(returnNode); + last->needSep.set(nullptr); + auto bLast = ++nodes.rbegin(); + if (bLast != nodes.rend()) { + bool isMacro = false; + BLOCK_START + BREAK_IF(expListLow->exprs.size() != 1); + auto exp = static_cast(expListLow->exprs.back()); + BREAK_IF(!exp->opValues.empty()); + auto chainValue = exp->getByPath(); + BREAK_IF(!chainValue); + isMacro = isMacroChain(chainValue); + BLOCK_END + if (!isMacro) { + ast_to(*bLast)->needSep.set(nullptr); + } + } + BLOCK_END + break; + } + case ExpUsage::Assignment: { + auto last = lastStatementFrom(block); + if (!last) return; + bool lastAssignable = expListFrom(last) || ast_is(last->content); + if (lastAssignable) { + auto x = last; + auto newAssignment = x->new_ptr(); + newAssignment->expList.set(assignList); + auto assign = x->new_ptr(); + if (auto valueList = last->content.as()) { + assign->values.dup(valueList->expList->exprs); + } else { + auto simpleValue = x->new_ptr(); + simpleValue->value.set(last->content); + auto value = x->new_ptr(); + value->item.set(simpleValue); + auto exp = newExp(value, x); + assign->values.push_back(exp); + } + newAssignment->action.set(assign); + last->content.set(newAssignment); + last->needSep.set(nullptr); + auto bLast = ++nodes.rbegin(); + if (bLast != nodes.rend()) { + static_cast(*bLast)->needSep.set(nullptr); + } + } + break; + } + default: break; + } + if (!nodes.empty()) { + str_list temp; + for (auto node : nodes) { + transformStatement(static_cast(node), temp); + } + out.push_back(join(temp)); + } else { + out.push_back(Empty); + } + if (isRoot && !_info.moduleName.empty()) { + out.back().append(indent() + s("return "sv) + _info.moduleName + nlr(block)); + } + } + +#ifndef YUE_NO_MACRO + void passOptions() { + if (!_config.options.empty()) { + pushYue("options"sv); // options + for (const auto& option : _config.options) { + lua_pushlstring(L, option.second.c_str(), option.second.size()); + lua_setfield(L, -2, option.first.c_str()); + } + lua_pop(L, 1); + } + } + + void pushCurrentModule() { + if (_useModule) { + lua_pushliteral(L, YUE_MODULE); // YUE_MODULE + lua_rawget(L, LUA_REGISTRYINDEX); // reg[YUE_MODULE], tb + int idx = static_cast(lua_objlen(L, -1)); // idx = #tb, tb + lua_rawgeti(L, -1, idx); // tb[idx], tb cur + lua_remove(L, -2); // cur + return; + } + _useModule = true; + if (!L) { + L = luaL_newstate(); + if (_luaOpen) { + _luaOpen(static_cast(L)); + } + passOptions(); + _stateOwner = true; + } + lua_pushliteral(L, YUE_MODULE); // YUE_MODULE + lua_rawget(L, LUA_REGISTRYINDEX); // reg[YUE_MODULE], tb + if (lua_isnil(L, -1) != 0) { // tb == nil + lua_pop(L, 1); + lua_newtable(L); // tb + lua_pushliteral(L, YUE_MODULE); // tb YUE_MODULE + lua_pushvalue(L, -2); // tb YUE_MODULE tb + lua_rawset(L, LUA_REGISTRYINDEX); // reg[YUE_MODULE] = tb, tb + } // tb + int idx = static_cast(lua_objlen(L, -1)); // idx = #tb, tb + lua_newtable(L); // tb cur + lua_pushvalue(L, -1); // tb cur cur + lua_rawseti(L, -3, idx + 1); // tb[idx + 1] = cur, tb cur + lua_remove(L, -2); // cur + } + + void pushYue(std::string_view name) { + lua_getglobal(L, "package"); // package + lua_getfield(L, -1, "loaded"); // package loaded + lua_getfield(L, -1, "yue"); // package loaded yue + lua_pushlstring(L, &name.front(), name.size()); // package loaded yue name + lua_gettable(L, -2); // loaded[name], package loaded yue item + lua_insert(L, -4); // item package loaded yue + lua_pop(L, 3); // item + } + + bool isModuleLoaded(std::string_view name) { + int top = lua_gettop(L); + DEFER(lua_settop(L, top)); + lua_pushliteral(L, YUE_MODULE); // YUE_MODULE + lua_rawget(L, LUA_REGISTRYINDEX); // modules + lua_pushlstring(L, &name.front(), name.size()); + lua_rawget(L, -2); // modules module + if (lua_isnil(L, -1) != 0) { + return false; + } + return true; + } + + void pushModuleTable(std::string_view name) { + lua_pushliteral(L, YUE_MODULE); // YUE_MODULE + lua_rawget(L, LUA_REGISTRYINDEX); // modules + lua_pushlstring(L, &name.front(), name.size()); + lua_rawget(L, -2); // modules module + if (lua_isnil(L, -1) != 0) { + lua_pop(L, 1); + lua_newtable(L); // modules module + lua_pushlstring(L, &name.front(), name.size()); + lua_pushvalue(L, -2); // modules module name module + lua_rawset(L, -4); // modules[name] = module, modules module + } + lua_remove(L, -2); // module + } + + void pushOptions(int lineOffset) { + lua_newtable(L); + lua_pushliteral(L, "lint_global"); + lua_pushboolean(L, 0); + lua_rawset(L, -3); + lua_pushliteral(L, "implicit_return_root"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); + lua_pushliteral(L, "reserve_line_number"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); + lua_pushliteral(L, "same_module"); + lua_pushboolean(L, 1); + lua_rawset(L, -3); + lua_pushliteral(L, "line_offset"); + lua_pushinteger(L, lineOffset); + lua_rawset(L, -3); + } + + void transformMacro(Macro_t* macro, str_list& out, bool exporting) { + if (_scopes.size() > 1) { + throw std::logic_error(_info.errorMessage("can not define macro outside the root block"sv, macro)); + } + auto macroName = _parser.toString(macro->name); + auto argsDef = macro->macroLit->argsDef.get(); + str_list newArgs; + if (argsDef) { + for (auto def_ : argsDef->definitions.objects()) { + auto def = static_cast(def_); + if (def->name.is()) { + throw std::logic_error(_info.errorMessage("self name is not supported for macro function argument"sv, def->name)); + } else { + std::string defVal; + if (def->defaultValue) { + defVal = _parser.toString(def->defaultValue); + Utils::trim(defVal); + defVal.insert(0, "=[==========["sv); + defVal.append("]==========]"sv); + } + newArgs.emplace_back(_parser.toString(def->name) + defVal); + } + } + if (argsDef->varArg) { + newArgs.emplace_back(_parser.toString(argsDef->varArg)); + } + } + _buf << "("sv << join(newArgs, ","sv) << ")->"sv; + _buf << _parser.toString(macro->macroLit->body); + auto macroCodes = clearBuf(); + _buf << "=(macro "sv << macroName << ")"; + auto chunkName = clearBuf(); + pushCurrentModule(); // cur + int top = lua_gettop(L) - 1; + DEFER(lua_settop(L, top)); + pushYue("loadstring"sv); // cur loadstring + lua_pushlstring(L, macroCodes.c_str(), macroCodes.size()); // cur loadstring codes + lua_pushlstring(L, chunkName.c_str(), chunkName.size()); // cur loadstring codes chunk + pushOptions(macro->m_begin.m_line - 1); // cur loadstring codes chunk options + if (lua_pcall(L, 3, 2, 0) != 0) { // loadstring(codes,chunk,options), cur f err + std::string err = lua_tostring(L, -1); + throw std::logic_error(_info.errorMessage(s("failed to load macro codes\n"sv) + err, macro->macroLit)); + } // cur f err + if (lua_isnil(L, -2) != 0) { // f == nil, cur f err + std::string err = lua_tostring(L, -1); + throw std::logic_error(_info.errorMessage(s("failed to load macro codes, at (macro "sv) + macroName + s("): "sv) + err, macro->macroLit)); + } + lua_pop(L, 1); // cur f + pushYue("pcall"sv); // cur f pcall + lua_insert(L, -2); // cur pcall f + if (lua_pcall(L, 1, 2, 0) != 0) { // f(), cur success macro + std::string err = lua_tostring(L, -1); + throw std::logic_error(_info.errorMessage(s("failed to generate macro function\n"sv) + err, macro->macroLit)); + } // cur success res + if (lua_toboolean(L, -2) == 0) { + std::string err = lua_tostring(L, -1); + throw std::logic_error(_info.errorMessage(s("failed to generate macro function\n"sv) + err, macro->macroLit)); + } // cur true macro + lua_remove(L, -2); // cur macro + if (exporting && !_moduleName.empty()) { + pushModuleTable(_moduleName); // cur macro module + lua_pushlstring(L, macroName.c_str(), macroName.size()); // cur macro module name + lua_pushvalue(L, -3); // cur macro module name macro + lua_rawset(L, -3); // cur macro module + lua_pop(L, 1); + } // cur macro + lua_pushlstring(L, macroName.c_str(), macroName.size()); // cur macro name + lua_insert(L, -2); // cur name macro + lua_rawset(L, -3); // cur[name] = macro, cur + out.push_back(Empty); + } +#else + void transformMacro(Macro_t* macro, str_list&, bool) { + throw std::logic_error(_info.errorMessage("macro feature not supported"sv, macro)); + } +#endif // YUE_NO_MACRO + + void transformReturn(Return_t* returnNode, str_list& out) { + if (!_enableReturn.top()) { + ast_node* target = returnNode->valueList.get(); + if (!target) target = returnNode; + throw std::logic_error(_info.errorMessage("illegal return statement here"sv, target)); + } + if (auto valueList = returnNode->valueList.get()) { + if (valueList->exprs.size() == 1) { + auto exp = static_cast(valueList->exprs.back()); + if (isPureBackcall(exp)) { + transformExp(exp, out, ExpUsage::Return); + return; + } + } + if (auto singleValue = singleValueFrom(valueList)) { + if (auto simpleValue = singleValue->item.as()) { + auto value = simpleValue->value.get(); + switch (value->getId()) { + case id(): + transformComprehension(static_cast(value), out, ExpUsage::Return); + return; + case id(): + transformTblComprehension(static_cast(value), out, ExpUsage::Return); + return; + case id(): + transformWith(static_cast(value), out, nullptr, true); + return; + case id(): + transformClassDecl(static_cast(value), out, ExpUsage::Return); + return; + case id(): + transformDo(static_cast(value), out, ExpUsage::Return); + return; + case id(): + transformSwitch(static_cast(value), out, ExpUsage::Return); + return; + case id(): + transformWhileInPlace(static_cast(value), out); + return; + case id(): + transformForInPlace(static_cast(value), out); + return; + case id(): + transformForEachInPlace(static_cast(value), out); + return; + case id(): + transformIf(static_cast(value), out, ExpUsage::Return); + return; + case id(): + transformUnless(static_cast(value), out, ExpUsage::Return); + return; + } + } else if (auto chainValue = singleValue->item.as()) { + if (specialChainValue(chainValue) != ChainType::Common) { + transformChainValue(chainValue, out, ExpUsage::Return, nullptr, returnNode->allowBlockMacroReturn); + return; + } + } + transformValue(singleValue, out); + out.back() = indent() + s("return "sv) + out.back() + nlr(returnNode); + return; + } else { + str_list temp; + transformExpListLow(valueList, temp); + out.push_back(indent() + s("return "sv) + temp.back() + nlr(returnNode)); + } + } else { + out.push_back(indent() + s("return"sv) + nll(returnNode)); + } + } + + void transformFnArgsDef(FnArgsDef_t* argsDef, str_list& out) { + if (!argsDef->defList) { + out.push_back(Empty); + out.push_back(Empty); + } else { + transformFnArgDefList(argsDef->defList, out); + } + if (argsDef->shadowOption) { + transform_outer_var_shadow(argsDef->shadowOption); + } + } + + void transform_outer_var_shadow(outer_var_shadow_t* shadow) { + markVarShadowed(); + if (shadow->varList) { + for (auto name : shadow->varList->names.objects()) { + addToAllowList(_parser.toString(name)); + } + } + } + + void transformFnArgDefList(FnArgDefList_t* argDefList, str_list& out) { + auto x = argDefList; + struct ArgItem { + std::string name; + std::string assignSelf; + }; + std::list argItems; + str_list temp; + std::string varNames; + bool assignSelf = false; + for (auto _def : argDefList->definitions.objects()) { + auto def = static_cast(_def); + auto& arg = argItems.emplace_back(); + switch (def->name->getId()) { + case id(): arg.name = _parser.toString(def->name); break; + case id(): { + assignSelf = true; + auto selfName = static_cast(def->name.get()); + switch (selfName->name->getId()) { + case id(): { + auto clsName = static_cast(selfName->name.get()); + arg.name = _parser.toString(clsName->name); + arg.assignSelf = s("self.__class."sv) + arg.name; + break; + } + case id(): + arg.name = "self.__class"sv; + break; + case id(): { + auto sfName = static_cast(selfName->name.get()); + arg.name = _parser.toString(sfName->name); + arg.assignSelf = s("self."sv) + arg.name; + break; + } + case id(): + arg.name = "self"sv; + break; + default: YUEE("AST node mismatch", selfName->name.get()); break; + } + break; + } + default: YUEE("AST node mismatch", def->name.get()); break; + } + forceAddToScope(arg.name); + if (def->defaultValue) { + pushScope(); + auto expList = toAst(arg.name, x); + auto assign = x->new_ptr(); + assign->values.push_back(def->defaultValue.get()); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + popScope(); + _buf << indent() << "if "sv << arg.name << " == nil then"sv << nll(def); + _buf << temp.back(); + _buf << indent() << "end"sv << nll(def); + temp.back() = clearBuf(); + } + if (varNames.empty()) varNames = arg.name; + else varNames.append(s(", "sv) + arg.name); + } + if (argDefList->varArg) { + auto& arg = argItems.emplace_back(); + arg.name = "..."sv; + if (varNames.empty()) varNames = arg.name; + else varNames.append(s(", "sv) + arg.name); + _varArgs.top() = true; + } + std::string initCodes = join(temp); + if (assignSelf) { + auto sjoin = [](const decltype(argItems)& items, int index) { + std::string result; + for (auto it = items.begin(); it != items.end(); ++it) { + if (it->assignSelf.empty()) continue; + if (result.empty()) result = (&it->name)[index]; + else result.append(s(", "sv) + (&it->name)[index]); + } + return result; + }; + std::string sleft = sjoin(argItems, 1); + std::string sright = sjoin(argItems, 0); + initCodes.append(indent() + sleft + s(" = "sv) + sright + nll(argDefList)); + } + out.push_back(varNames); + out.push_back(initCodes); + } + + void transformSelfName(SelfName_t* selfName, str_list& out, const ast_sel& invoke = {}) { + auto x = selfName; + auto name = selfName->name.get(); + switch (name->getId()) { + case id(): { + auto clsName = static_cast(name); + auto nameStr = _parser.toString(clsName->name); + if (LuaKeywords.find(nameStr) != LuaKeywords.end()) { + out.push_back(s("self.__class[\""sv) + nameStr + s("\"]"sv)); + if (invoke) { + if (auto invokePtr = invoke.as()) { + invokePtr->args.push_front(toAst("self.__class"sv, x)); + } else { + auto invokeArgsPtr = invoke.as(); + invokeArgsPtr->args.push_front(toAst("self.__class"sv, x)); + } + } + } else { + out.push_back(s("self.__class"sv) + s(invoke ? ":"sv : "."sv) + nameStr); + } + break; + } + case id(): + out.push_back(s("self.__class"sv)); + break; + case id(): { + auto sfName = static_cast(name); + auto nameStr = _parser.toString(sfName->name); + if (LuaKeywords.find(nameStr) != LuaKeywords.end()) { + out.push_back(s("self[\""sv) + nameStr + s("\"]"sv)); + if (invoke) { + if (auto invokePtr = invoke.as()) { + invokePtr->args.push_front(toAst("self"sv, x)); + } else { + auto invokeArgsPtr = invoke.as(); + invokeArgsPtr->args.push_front(toAst("self"sv, x)); + } + } + } else { + out.push_back(s("self"sv) + s(invoke ? ":"sv : "."sv) + nameStr); + } + break; + } + case id(): + out.push_back(s("self"sv)); + break; + default: YUEE("AST node mismatch", name); break; + } + } + + bool transformChainEndWithEOP(const node_container& chainList, str_list& out, ExpUsage usage, ExpList_t* assignList) { + auto x = chainList.front(); + if (ast_is(chainList.back())) { + auto parens = x->new_ptr(); + { + auto chainValue = x->new_ptr(); + for (auto item : chainList) { + chainValue->items.push_back(item); + } + chainValue->items.pop_back(); + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = newExp(value, toAst("!="sv, x), toAst("nil"sv, x), x); + parens->expr.set(exp); + } + switch (usage) { + case ExpUsage::Assignment: { + auto callable = x->new_ptr(); + callable->item.set(parens); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = newExp(value, x); + auto assignment = x->new_ptr(); + assignment->expList.set(assignList); + auto assign = x->new_ptr(); + assign->values.push_back(exp); + assignment->action.set(assign); + transformAssignment(assignment, out); + break; + } + case ExpUsage::Return: + transformParens(parens, out); + out.back().insert(0, indent() + s("return "sv)); + out.back().append(nlr(x)); + break; + default: + transformParens(parens, out); + break; + } + return true; + } + return false; + } + + bool transformChainWithEOP(const node_container& chainList, str_list& out, ExpUsage usage, ExpList_t* assignList) { + auto opIt = std::find_if(chainList.begin(), chainList.end(), [](ast_node* node) { return ast_is(node); }); + if (opIt != chainList.end()) { + auto x = chainList.front(); + str_list temp; + if (usage == ExpUsage::Closure) { + temp.push_back(s("(function()"sv) + nll(x)); + pushScope(); + _enableReturn.push(true); + } + auto partOne = x->new_ptr(); + for (auto it = chainList.begin();it != opIt;++it) { + partOne->items.push_back(*it); + } + BLOCK_START + auto back = ast_cast(partOne->items.back()); + BREAK_IF(!back); + auto selfName = ast_cast(back->item); + BREAK_IF(!selfName); + if (auto sname = ast_cast(selfName->name)) { + auto colonItem = x->new_ptr(); + colonItem->name.set(sname->name); + partOne->items.pop_back(); + partOne->items.push_back(toAst("@"sv, x)); + partOne->items.push_back(colonItem); + break; + } + if (auto cname = ast_cast(selfName->name)) { + auto colonItem = x->new_ptr(); + colonItem->name.set(cname->name); + partOne->items.pop_back(); + partOne->items.push_back(toAst("@@"sv, x)); + partOne->items.push_back(colonItem); + break; + } + BLOCK_END + auto objVar = singleVariableFrom(partOne); + if (objVar.empty()) { + objVar = getUnusedName("_obj_"sv); + if (auto colonItem = ast_cast(partOne->items.back())) { + auto chainValue = x->new_ptr(); + chainValue->items.dup(partOne->items); + chainValue->items.pop_back(); + if (chainValue->items.empty()) { + if (_withVars.empty()) { + throw std::logic_error(_info.errorMessage("short dot/colon syntax must be called within a with block"sv, x)); + } + chainValue->items.push_back(toAst(_withVars.top(), x)); + } + auto newObj = singleVariableFrom(chainValue); + if (!newObj.empty()) { + objVar = newObj; + } else { + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = newExp(value, x); + auto assign = x->new_ptr(); + assign->values.push_back(exp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(toAst(objVar, x)); + expListAssign->action.set(assign); + transformAssignment(expListAssign, temp); + } + auto dotItem = x->new_ptr(); + auto name = colonItem->name.get(); + if (auto keyword = ast_cast(name)) { + name = keyword->name.get(); + } + dotItem->name.set(name); + partOne->items.clear(); + partOne->items.push_back(toAst(objVar, x)); + partOne->items.push_back(dotItem); + auto it = opIt; ++it; + if (it != chainList.end() && ast_is(*it)) { + + if (auto invoke = ast_cast(*it)) { + invoke->args.push_front(toAst(objVar, x)); + } else { + auto invokeArgs = static_cast(*it); + invokeArgs->args.push_front(toAst(objVar, x)); + } + } + objVar = getUnusedName("_obj_"sv); + } + auto value = x->new_ptr(); + value->item.set(partOne); + auto exp = newExp(value, x); + auto assign = x->new_ptr(); + assign->values.push_back(exp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(toAst(objVar, x)); + expListAssign->action.set(assign); + transformAssignment(expListAssign, temp); + } + _buf << indent() << "if "sv << objVar << " ~= nil then"sv << nll(x); + temp.push_back(clearBuf()); + pushScope(); + auto partTwo = x->new_ptr(); + partTwo->items.push_back(toAst(objVar, x)); + for (auto it = ++opIt;it != chainList.end();++it) { + partTwo->items.push_back(*it); + } + switch (usage) { + case ExpUsage::Common: + transformChainValue(partTwo, temp, ExpUsage::Common); + break; + case ExpUsage::Assignment: { + auto value = x->new_ptr(); + value->item.set(partTwo); + auto exp = newExp(value, x); + auto assign = x->new_ptr(); + assign->values.push_back(exp); + auto assignment = x->new_ptr(); + assignment->expList.set(assignList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + break; + } + case ExpUsage::Return: + case ExpUsage::Closure: { + auto value = x->new_ptr(); + value->item.set(partTwo); + auto exp = newExp(value, x); + auto ret = x->new_ptr(); + auto expListLow = x->new_ptr(); + expListLow->exprs.push_back(exp); + ret->valueList.set(expListLow); + transformReturn(ret, temp); + break; + } + } + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(x)); + switch (usage) { + case ExpUsage::Return: + temp.push_back(indent() + s("return nil"sv) + nlr(x)); + break; + case ExpUsage::Closure: + temp.push_back(indent() + s("return nil"sv) + nlr(x)); + _enableReturn.pop(); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + break; + default: + break; + } + out.push_back(join(temp)); + return true; + } + return false; + } + + bool transformChainEndWithColonItem(const node_container& chainList, str_list& out, ExpUsage usage, ExpList_t* assignList) { + if (ast_is(chainList.back())) { + auto x = chainList.front(); + str_list temp; + switch (usage) { + case ExpUsage::Assignment: + temp.push_back(indent() + s("do"sv) + nll(x)); + pushScope(); + break; + case ExpUsage::Closure: + temp.push_back(s("(function()"sv) + nll(x)); + pushScope(); + _enableReturn.push(true); + break; + default: + break; + } + auto baseChain = x->new_ptr(); + switch (chainList.front()->getId()) { + case id(): + case id(): + if (_withVars.empty()) { + throw std::logic_error(_info.errorMessage("short dot/colon syntax must be called within a with block"sv, chainList.front())); + } else { + baseChain->items.push_back(toAst(_withVars.top(), x)); + } + break; + } + auto end = --chainList.end(); + for (auto it = chainList.begin(); it != end; ++it) { + baseChain->items.push_back(*it); + } + auto colonChainItem = static_cast(chainList.back()); + auto funcName = _parser.toString(colonChainItem->name); + auto baseVar = getUnusedName("_base_"sv); + auto fnVar = getUnusedName("_fn_"sv); + { + auto value = x->new_ptr(); + value->item.set(baseChain); + auto exp = newExp(value, x); + auto assign = x->new_ptr(); + assign->values.push_back(exp); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst(baseVar, x)); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + { + auto assign = x->new_ptr(); + assign->values.push_back(toAst(baseVar + "." + funcName, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst(fnVar, x)); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + auto funLit = toAst(fnVar + s(" and (...)-> "sv) + fnVar + s(" "sv) + baseVar + s(", ..."sv), x); + switch (usage) { + case ExpUsage::Closure: + case ExpUsage::Return: { + auto returnNode = x->new_ptr(); + auto expListLow = x->new_ptr(); + expListLow->exprs.push_back(funLit); + returnNode->valueList.set(expListLow); + transformReturn(returnNode, temp); + break; + } + case ExpUsage::Assignment: { + auto assign = x->new_ptr(); + assign->values.push_back(funLit); + auto assignment = x->new_ptr(); + assignment->expList.set(assignList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + break; + } + default: + break; + } + switch (usage) { + case ExpUsage::Assignment: + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(x)); + break; + case ExpUsage::Closure: + _enableReturn.pop(); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + break; + default: + break; + } + out.push_back(join(temp)); + return true; + } + return false; + } + + void transformChainList(const node_container& chainList, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { + auto x = chainList.front(); + str_list temp; + switch (x->getId()) { + case id(): + case id(): + if (_withVars.empty()) { + throw std::logic_error(_info.errorMessage("short dot/colon syntax must be called within a with block"sv, x)); + } else { + temp.push_back(_withVars.top()); + } + break; + } + for (auto it = chainList.begin(); it != chainList.end(); ++it) { + auto item = *it; + switch (item->getId()) { + case id(): + transformInvoke(static_cast(item), temp); + break; + case id(): + transformDotChainItem(static_cast(item), temp); + break; + case id(): { + auto colonItem = static_cast(item); + auto current = it; + auto next = current; ++next; + auto followItem = next != chainList.end() ? *next : nullptr; + if (current != chainList.begin()) { + --current; + if (!ast_is(*current)) { + ++current; + } + } + if (ast_is(followItem)) { + ++next; + followItem = next != chainList.end() ? *next : nullptr; + --next; + } + if (!ast_is(followItem)) { + throw std::logic_error(_info.errorMessage("colon chain item must be followed by invoke arguments"sv, colonItem)); + } + if (colonItem->name.is()) { + std::string callVar; + auto block = x->new_ptr(); + { + auto chainValue = x->new_ptr(); + switch (chainList.front()->getId()) { + case id(): + case id(): + chainValue->items.push_back(toAst(_withVars.top(), x)); + break; + } + for (auto i = chainList.begin(); i != current; ++i) { + chainValue->items.push_back(*i); + } + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = newExp(value, x); + callVar = singleVariableFrom(exp); + if (callVar.empty()) { + callVar = getUnusedName(s("_call_"sv)); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst(callVar, x)); + auto assign = x->new_ptr(); + assign->values.push_back(exp); + assignment->action.set(assign); + auto stmt = x->new_ptr(); + stmt->content.set(assignment); + block->statements.push_back(stmt); + } + } + ast_ptr nexp; + { + auto name = _parser.toString(colonItem->name); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(toAst(callVar, x)); + if (ast_is(*current)) { + chainValue->items.push_back(x->new_ptr()); + } + chainValue->items.push_back(toAst(s("\""sv) + name + s("\""sv), x)); + if (auto invoke = ast_cast(followItem)) { + invoke->args.push_front(toAst(callVar, x)); + } else { + auto invokeArgs = static_cast(followItem); + invokeArgs->args.push_front(toAst(callVar, x)); + } + for (auto i = next; i != chainList.end(); ++i) { + chainValue->items.push_back(*i); + } + auto value = x->new_ptr(); + value->item.set(chainValue); + nexp = newExp(value, x); + auto expList = x->new_ptr(); + expList->exprs.push_back(nexp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(expList); + auto stmt = x->new_ptr(); + stmt->content.set(expListAssign); + block->statements.push_back(stmt); + } + switch (usage) { + case ExpUsage::Common: + case ExpUsage::Return: + transformBlock(block, out, usage); + return; + case ExpUsage::Assignment: { + transformBlock(block, out, ExpUsage::Assignment, assignList); + return; + } + default: + break; + } + if (block->statements.size() == 1) { + transformExp(nexp, out, usage, assignList); + } else { + auto body = x->new_ptr(); + body->content.set(block); + auto funLit = toAst("->"sv, x); + funLit->body.set(body); + auto simpleValue = x->new_ptr(); + simpleValue->value.set(funLit); + auto value = x->new_ptr(); + value->item.set(simpleValue); + auto exp = newExp(value, x); + auto paren = x->new_ptr(); + paren->expr.set(exp); + auto callable = x->new_ptr(); + callable->item.set(paren); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + auto invoke = x->new_ptr(); + chainValue->items.push_back(invoke); + transformChainValue(chainValue, out, ExpUsage::Closure); + } + return; + } + transformColonChainItem(colonItem, temp); + break; + } + case id(): + transformSlice(static_cast(item), temp); + break; + case id(): { + auto next = it; ++next; + auto followItem = next != chainList.end() ? *next : nullptr; + ast_sel invoke; + if (ast_is(followItem)) { + invoke.set(followItem); + } + transformCallable(static_cast(item), temp, invoke); + break; + } + case id(): + transformString(static_cast(item), temp); + temp.back() = s("("sv) + temp.back() + s(")"sv); + break; + case id(): + transformExp(static_cast(item), temp, ExpUsage::Closure); + temp.back() = s(temp.back().front() == '[' ? "[ "sv : "["sv) + temp.back() + s("]"sv); + break; + case id(): transformInvokeArgs(static_cast(item), temp); break; + default: YUEE("AST node mismatch", item); break; + } + } + switch (usage) { + case ExpUsage::Common: + out.push_back(indent() + join(temp) + nll(x)); + break; + case ExpUsage::Return: + out.push_back(indent() + s("return "sv) + join(temp) + nll(x)); + break; + case ExpUsage::Assignment: YUEE("invalid expression usage", x); break; + default: + out.push_back(join(temp)); + break; + } + } + +#ifndef YUE_NO_MACRO + std::tuple expandMacroStr(ChainValue_t* chainValue) { + const auto& chainList = chainValue->items.objects(); + auto x = ast_to(chainList.front())->item.to(); + auto macroName = x->name ? _parser.toString(x->name) : Empty; + if (!macroName.empty() && !_useModule) { + throw std::logic_error(_info.errorMessage("can not resolve macro"sv, x)); + } + pushCurrentModule(); // cur + int top = lua_gettop(L) - 1; + DEFER(lua_settop(L, top)); + if (macroName.empty()) { + lua_pop(L, 1); // empty + auto item = *(++chainList.begin()); + const node_container* args = nullptr; + if (auto invoke = ast_cast(item)) { + args = &invoke->args.objects(); + } else { + args = &ast_to(item)->args.objects(); + } + if (args->size() != 1) { + throw std::logic_error(_info.errorMessage("in-place macro must be followed by a compile time function"sv, x)); + } + auto fcodes = _parser.toString(args->back()); + Utils::trim(fcodes); + pushYue("loadstring"sv); // loadstring + lua_pushlstring(L, fcodes.c_str(), fcodes.size()); // loadstring codes + lua_pushliteral(L, "=(macro in-place)"); // loadstring codes chunk + pushOptions(args->back()->m_begin.m_line - 1); // loadstring codes chunk options + if (lua_pcall(L, 3, 2, 0) != 0) { // loadstring(codes,chunk,options), f err + std::string err = lua_tostring(L, -1); + throw std::logic_error(_info.errorMessage(s("failed to load macro codes\n"sv) + err, x)); + } // f err + if (lua_isnil(L, -2) != 0) { // f == nil, f err + std::string err = lua_tostring(L, -1); + throw std::logic_error(_info.errorMessage(s("failed to load macro codes, at (macro in-place): "sv) + err, x)); + } + lua_pop(L, 1); // f + pushYue("pcall"sv); // f pcall + lua_insert(L, -2); // pcall f + if (lua_pcall(L, 1, 2, 0) != 0) { // f(), success macroFunc + std::string err = lua_tostring(L, -1); + throw std::logic_error(_info.errorMessage(s("failed to generate macro function\n"sv) + err, x)); + } // success res + if (lua_toboolean(L, -2) == 0) { + std::string err = lua_tostring(L, -1); + throw std::logic_error(_info.errorMessage(s("failed to generate macro function\n"sv) + err, x)); + } // true macroFunc + lua_remove(L, -2); // macroFunc + pushYue("pcall"sv); // macroFunc pcall + lua_insert(L, -2); // pcall macroFunc + bool success = lua_pcall(L, 1, 2, 0) == 0; + if (!success) { // err + std::string err = lua_tostring(L, -1); + throw std::logic_error(_info.errorMessage(s("failed to expand macro: "sv) + err, x)); + } // success err + if (lua_toboolean(L, -2) == 0) { + std::string err = lua_tostring(L, -1); + throw std::logic_error(_info.errorMessage(s("failed to expand macro: "sv) + err, x)); + } + return {Empty, Empty, {}}; + } + lua_pushlstring(L, macroName.c_str(), macroName.size()); // cur macroName + lua_rawget(L, -2); // cur[macroName], cur macroFunc + if (lua_isfunction(L, -1) == 0) { + throw std::logic_error(_info.errorMessage("can not resolve macro"sv, x)); + } // cur macroFunc + pushYue("pcall"sv); // cur macroFunc pcall + lua_insert(L, -2); // cur pcall macroFunc + auto item = *(++chainList.begin()); + const node_container* args = nullptr; + if (auto invoke = ast_cast(item)) { + args = &invoke->args.objects(); + } else { + args = &ast_to(item)->args.objects(); + } + for (auto arg : *args) { + std::string str; + // check whether arg is reassembled + // do some workaround for backcall expression + if (ast_is(arg) && arg->m_begin.m_it == arg->m_end.m_it) { + auto exp = static_cast(arg); + BLOCK_START + BREAK_IF(!exp->opValues.empty()); + auto chainValue = exp->getByPath(); + BREAK_IF(!chainValue); + BREAK_IF(!isMacroChain(chainValue)); + BREAK_IF(chainValue->items.size() != 2); + str = std::get<1>(expandMacroStr(chainValue)); + BLOCK_END + if (str.empty()) { + // exp is reassembled due to backcall expressions + // in transform stage, toString(exp) won't be able + // to convert its whole text content + str = _parser.toString(exp->backcalls.front()); + } + } else if (auto lstr = ast_cast(arg)) { + str = _parser.toString(lstr->content); + } else { + bool multiLineStr = false; + BLOCK_START + auto exp = ast_cast(arg); + BREAK_IF(!exp); + auto value = singleValueFrom(exp); + BREAK_IF(!value); + auto lstr = value->getByPath(); + BREAK_IF(!lstr); + str = _parser.toString(lstr->content); + multiLineStr = true; + BLOCK_END + if (!multiLineStr) { + str = _parser.toString(arg); + } + } + Utils::trim(str); + Utils::replace(str, "\r\n"sv, "\n"sv); + lua_pushlstring(L, str.c_str(), str.size()); + } // cur pcall macroFunc args... + bool success = lua_pcall(L, static_cast(args->size()) + 1, 2, 0) == 0; + if (!success) { // cur err + std::string err = lua_tostring(L, -1); + throw std::logic_error(_info.errorMessage(s("failed to expand macro: "sv) + err, x)); + } // cur success res + if (lua_toboolean(L, -2) == 0) { + std::string err = lua_tostring(L, -2); + throw std::logic_error(_info.errorMessage(s("failed to expand macro: "sv) + err, x)); + } + lua_remove(L, -2); // cur res + if (lua_isstring(L, -1) == 0 && lua_istable(L, -1) == 0) { + throw std::logic_error(_info.errorMessage(s("macro function must return string or table"sv), x)); + } // cur res + std::string codes; + std::string type; + str_list localVars; + if (lua_istable(L, -1) != 0) { + lua_getfield(L, -1, "codes"); // cur res codes + if (lua_isstring(L, -1) != 0) { + codes = lua_tostring(L, -1); + } else { + throw std::logic_error(_info.errorMessage(s("macro table must contain field \"codes\" of string"sv), x)); + } + lua_pop(L, 1); // cur res + lua_getfield(L, -1, "type"); // cur res type + if (lua_isstring(L, -1) != 0) { + type = lua_tostring(L, -1); + } + if (type != "lua"sv && type != "text"sv) { + throw std::logic_error(_info.errorMessage(s("macro table must contain field \"type\" of value \"lua\" or \"text\""sv), x)); + } + lua_pop(L, 1); // cur res + lua_getfield(L, -1, "locals"); // cur res locals + if (lua_istable(L, -1) != 0) { + for (int i = 0; i < static_cast(lua_objlen(L, -1)); i++) { + lua_rawgeti(L, -1, i + 1); // cur res locals item + size_t len = 0; + if (lua_isstring(L, -1) == 0) { + throw std::logic_error(_info.errorMessage(s("macro table field \"locals\" must be a table of strings"sv), x)); + } + auto name = lua_tolstring(L, -1, &len); + if (_parser.match({name, len})) { + localVars.push_back(std::string(name, len)); + } else { + throw std::logic_error(_info.errorMessage(s("macro table field \"locals\" must contain names for local variables, got \""sv) + std::string(name, len) + '"', x)); + } + lua_pop(L, 1); + } + } + lua_pop(L, 1); // cur res + } else { + codes = lua_tostring(L, -1); + } + return {type, codes, std::move(localVars)}; + } + + std::tuple, std::unique_ptr, std::string, str_list> expandMacro(ChainValue_t* chainValue, ExpUsage usage, bool allowBlockMacroReturn) { + auto x = ast_to(chainValue->items.front())->item.to(); + const auto& chainList = chainValue->items.objects(); + std::string type, codes; + str_list localVars; + std::tie(type, codes, localVars) = expandMacroStr(chainValue); + bool isBlock = (usage == ExpUsage::Common) && (chainList.size() <= 2); + ParseInfo info; + if (type == "lua"sv) { + if (!isBlock) { + throw std::logic_error(_info.errorMessage("lua macro can only be placed where block macro is allowed"sv, x)); + } + auto macroChunk = s("=(macro "sv) + _parser.toString(x->name) + ')'; + int top = lua_gettop(L); + DEFER(lua_settop(L, top)); + if (luaL_loadbuffer(L, codes.c_str(), codes.size(), macroChunk.c_str()) != 0) { + std::string err = lua_tostring(L, -1); + throw std::logic_error(_info.errorMessage(err, x)); + } + return {nullptr, nullptr, std::move(codes), std::move(localVars)}; + } else if (type == "text"sv) { + if (!isBlock) { + throw std::logic_error(_info.errorMessage("text macro can only be placed where block macro is allowed"sv, x)); + } + return {nullptr, nullptr, std::move(codes), std::move(localVars)}; + } else { + if (!codes.empty()) { + if (isBlock) { + info = _parser.parse(codes); + if (!info.node) { + info.error = info.error.substr(info.error.find(':') + 2); + throw std::logic_error(_info.errorMessage(s("failed to expanded macro as block: "sv) + info.error, x)); + } + } else { + info = _parser.parse(codes); + if (!info.node && allowBlockMacroReturn) { + info = _parser.parse(codes); + if (!info.node) { + info.error = info.error.substr(info.error.find(':') + 2); + throw std::logic_error(_info.errorMessage(s("failed to expanded macro as expr or block: "sv) + info.error, x)); + } + isBlock = true; + } else if (!info.node) { + info.error = info.error.substr(info.error.find(':') + 2); + throw std::logic_error(_info.errorMessage(s("failed to expanded macro as expr: "sv) + info.error, x)); + } + } + int line = x->m_begin.m_line; + int col = x->m_begin.m_col; + info.node->traverse([&](ast_node* node) { + node->m_begin.m_line = line; + node->m_end.m_line = line; + node->m_begin.m_col = col; + node->m_end.m_col = col; + return traversal::Continue; + }); + if (!isBlock) { + ast_ptr exp; + exp.set(info.node); + if (!exp->opValues.empty() || chainList.size() > 2) { + auto paren = x->new_ptr(); + paren->expr.set(exp); + auto callable = x->new_ptr(); + callable->item.set(paren); + auto newChain = x->new_ptr(); + newChain->items.push_back(callable); + auto it = chainList.begin(); + it++; it++; + for (; it != chainList.end(); ++it) { + newChain->items.push_back(*it); + } + auto value = x->new_ptr(); + value->item.set(newChain); + exp = newExp(value, x); + } + if (usage == ExpUsage::Common) { + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto exps = x->new_ptr(); + exps->expList.set(expList); + auto stmt = x->new_ptr(); + stmt->content.set(exps); + auto block = x->new_ptr(); + block->statements.push_back(stmt); + info.node.set(block); + } else { + info.node.set(exp); + } + } + return {info.node, std::move(info.codes), Empty, std::move(localVars)}; + } else { + if (!isBlock) throw std::logic_error(_info.errorMessage(s("failed to expanded empty macro as expr"sv), x)); + return {x->new_ptr().get(), std::move(info.codes), Empty, std::move(localVars)}; + } + } + } +#endif // YUE_NO_MACRO + + void transformChainValue(ChainValue_t* chainValue, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr, bool allowBlockMacroReturn = false) { + if (isMacroChain(chainValue)) { +#ifndef YUE_NO_MACRO + ast_ptr node; + std::unique_ptr codes; + std::string luaCodes; + str_list localVars; + std::tie(node, codes, luaCodes, localVars) = expandMacro(chainValue, usage, allowBlockMacroReturn); + Utils::replace(luaCodes, "\r\n"sv, "\n"sv); + Utils::trim(luaCodes); + if (!node) { + if (!luaCodes.empty()) { + if (_config.reserveLineNumber) { + luaCodes.insert(0, nll(chainValue).substr(1)); + } + luaCodes.append(nlr(chainValue)); + } + out.push_back(luaCodes); + if (!localVars.empty()) { + for (const auto& var : localVars) { + addToScope(var); + } + } + return; + } + if (usage == ExpUsage::Common || (usage == ExpUsage::Return && node.is())) { + transformBlock(node.to(), out, usage, assignList); + } else { + auto x = chainValue; + switch (usage) { + case ExpUsage::Assignment: { + auto assign = x->new_ptr(); + assign->values.push_back(node); + auto assignment = x->new_ptr(); + assignment->expList.set(assignList); + assignment->action.set(assign); + transformAssignment(assignment, out); + break; + } + case ExpUsage::Return: { + auto expListLow = x->new_ptr(); + expListLow->exprs.push_back(node); + auto returnNode = x->new_ptr(); + returnNode->valueList.set(expListLow); + transformReturn(returnNode, out); + break; + } + default: + transformExp(node.to(), out, usage); + break; + } + } + return; +#else + (void)allowBlockMacroReturn; + throw std::logic_error(_info.errorMessage("macro feature not supported"sv, chainValue)); +#endif // YUE_NO_MACRO + } + const auto& chainList = chainValue->items.objects(); + if (transformChainEndWithEOP(chainList, out, usage, assignList)) { + return; + } + if (transformChainWithEOP(chainList, out, usage, assignList)) { + return; + } + if (transformChainEndWithColonItem(chainList, out, usage, assignList)) { + return; + } + transformChainList(chainList, out, usage, assignList); + } + + void transformAssignableChain(AssignableChain_t* chain, str_list& out) { + transformChainList(chain->items.objects(), out, ExpUsage::Closure); + } + + void transformDotChainItem(DotChainItem_t* dotChainItem, str_list& out) { + auto name = _parser.toString(dotChainItem->name); + if (Keywords.find(name) != Keywords.end()) { + out.push_back(s("[\""sv) + name + s("\"]"sv)); + } else { + out.push_back(s("."sv) + name); + } + } + + void transformColonChainItem(ColonChainItem_t* colonChainItem, str_list& out) { + auto name = _parser.toString(colonChainItem->name); + out.push_back(s(colonChainItem->switchToDot ? "."sv : ":"sv) + name); + } + + void transformSlice(Slice_t* slice, str_list&) { + throw std::logic_error(_info.errorMessage("slice syntax not supported here"sv, slice)); + } + + void transformInvoke(Invoke_t* invoke, str_list& out) { + str_list temp; + for (auto arg : invoke->args.objects()) { + switch (arg->getId()) { + case id(): transformExp(static_cast(arg), temp, ExpUsage::Closure); break; + case id(): transformSingleString(static_cast(arg), temp); break; + case id(): transformDoubleString(static_cast(arg), temp); break; + case id(): transformLuaString(static_cast(arg), temp); break; + case id(): transformTableLit(static_cast(arg), temp); break; + default: YUEE("AST node mismatch", arg); break; + } + } + out.push_back(s("("sv) + join(temp, ", "sv) + s(")"sv)); + } + + void transform_unary_value(unary_value_t* unary_value, str_list& out) { + str_list temp; + for (auto _op : unary_value->ops.objects()) { + std::string op = _parser.toString(_op); + temp.push_back(op + (op == "not"sv ? s(" "sv) : Empty)); + } + transformValue(unary_value->value, temp); + out.push_back(join(temp)); + } + + void transform_unary_exp(unary_exp_t* unary_exp, str_list& out) { + if (unary_exp->ops.empty() && unary_exp->expos.size() == 1) { + transformValue(static_cast(unary_exp->expos.back()), out); + return; + } + std::string unary_op; + for (auto _op : unary_exp->ops.objects()) { + std::string op = _parser.toString(_op); + unary_op.append(op + (op == "not"sv ? s(" "sv) : Empty)); + } + str_list temp; + for (auto _value : unary_exp->expos.objects()) { + auto value = static_cast(_value); + transformValue(value, temp); + } + out.push_back(unary_op + join(temp, " ^ "sv)); + } + + void transformVariable(Variable_t* name, str_list& out) { + out.push_back(_parser.toString(name)); + } + + void transformNum(Num_t* num, str_list& out) { + out.push_back(_parser.toString(num)); + } + + void transformTableLit(TableLit_t* table, str_list& out) { + transformTable(table, table->values.objects(), out); + } + + void transformCompCommon(Comprehension_t* comp, str_list& out) { + str_list temp; + auto x = comp; + auto compInner = comp->forLoop.get(); + for (auto item : compInner->items.objects()) { + switch (item->getId()) { + case id(): + transformCompForEach(static_cast(item), temp); + break; + case id(): + transformCompFor(static_cast(item), temp); + break; + case id(): + transformExp(static_cast(item), temp, ExpUsage::Closure); + temp.back() = indent() + s("if "sv) + temp.back() + s(" then"sv) + nll(item); + pushScope(); + break; + default: YUEE("AST node mismatch", item); break; + } + } + if (auto stmt = comp->value.as()) { + transformStatement(stmt, temp); + } else if (auto exp = comp->value.as()) { + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto expListAssign = x->new_ptr(); + expListAssign->expList.set(expList); + auto statement = x->new_ptr(); + statement->content.set(expListAssign); + transformStatement(statement, temp); + } + auto value = temp.back(); + temp.pop_back(); + _buf << join(temp) << value; + for (size_t i = 0; i < compInner->items.objects().size(); ++i) { + popScope(); + _buf << indent() << "end"sv << nll(comp); + } + out.push_back(clearBuf()); + } + + void transformComprehension(Comprehension_t* comp, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { + auto x = comp; + switch (usage) { + case ExpUsage::Closure: + _enableReturn.push(true); + pushScope(); + break; + case ExpUsage::Assignment: + pushScope(); + break; + default: + break; + } + str_list temp; + std::string accumVar = getUnusedName("_accum_"sv); + std::string lenVar = getUnusedName("_len_"sv); + addToScope(accumVar); + addToScope(lenVar); + auto compInner = comp->forLoop.get(); + for (auto item : compInner->items.objects()) { + switch (item->getId()) { + case id(): + transformCompForEach(static_cast(item), temp); + break; + case id(): + transformCompFor(static_cast(item), temp); + break; + case id(): + transformExp(static_cast(item), temp, ExpUsage::Closure); + temp.back() = indent() + s("if "sv) + temp.back() + s(" then"sv) + nll(item); + pushScope(); + break; + default: YUEE("AST node mismatch", item); break; + } + } + { + auto assignLeft = toAst(accumVar + s("["sv) + lenVar + s("]"sv), x); + auto assign = x->new_ptr(); + assign->values.push_back(comp->value); + auto assignment = x->new_ptr(); + assignment->expList.set(assignLeft); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + auto assignStr = temp.back(); + temp.pop_back(); + for (size_t i = 0; i < compInner->items.objects().size(); ++i) { + popScope(); + } + _buf << indent() << "local "sv << accumVar << " = { }"sv << nll(comp); + _buf << indent() << "local "sv << lenVar << " = 1"sv << nll(comp); + _buf << join(temp); + _buf << assignStr; + _buf << indent(int(temp.size())) << lenVar << " = "sv << lenVar << " + 1"sv << nll(comp); + for (int ind = int(temp.size()) - 1; ind > -1; --ind) { + _buf << indent(ind) << "end"sv << nll(comp); + } + switch (usage) { + case ExpUsage::Common: + break; + case ExpUsage::Closure: { + _enableReturn.pop(); + out.push_back(clearBuf()); + out.back().append(indent() + s("return "sv) + accumVar + nlr(comp)); + popScope(); + out.back().insert(0, s("(function()"sv) + nll(comp)); + out.back().append(indent() + s("end)()"sv)); + break; + } + case ExpUsage::Assignment: { + out.push_back(clearBuf()); + auto assign = x->new_ptr(); + assign->values.push_back(toAst(accumVar, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(assignList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + popScope(); + out.back() = indent() + s("do"sv) + nll(comp) + + out.back() + temp.back() + + indent() + s("end"sv) + nlr(comp); + break; + } + case ExpUsage::Return: + out.push_back(clearBuf()); + out.back().append(indent() + s("return "sv) + accumVar + nlr(comp)); + break; + default: + break; + } + } + + void transformForEachHead(AssignableNameList_t* nameList, ast_node* loopTarget, str_list& out) { + auto x = nameList; + str_list temp; + str_list vars; + str_list varBefore, varAfter; + std::list>> destructPairs; + for (auto _item : nameList->items.objects()) { + auto item = static_cast(_item)->item.get(); + switch (item->getId()) { + case id(): + transformVariable(static_cast(item), vars); + varAfter.push_back(vars.back()); + break; + case id(): { + auto desVar = getUnusedName("_des_"sv); + destructPairs.emplace_back(item, toAst(desVar, x)); + vars.push_back(desVar); + varAfter.push_back(desVar); + break; + } + default: YUEE("AST node mismatch", item); break; + } + } + switch (loopTarget->getId()) { + case id(): { + auto star_exp = static_cast(loopTarget); + std::string listVar; + auto indexVar = getUnusedName("_index_"sv); + varAfter.push_back(indexVar); + auto value = singleValueFrom(star_exp->value); + if (!value) throw std::logic_error(_info.errorMessage("invalid star syntax"sv, star_exp)); + bool endWithSlice = false; + BLOCK_START + auto chainValue = value->item.as(); + BREAK_IF(!chainValue); + auto chainList = chainValue->items.objects(); + auto slice = ast_cast(chainList.back()); + BREAK_IF(!slice); + endWithSlice = true; + chainList.pop_back(); + auto chain = x->new_ptr(); + for (auto item : chainList) { + chain->items.push_back(item); + } + std::string startValue("1"sv); + if (auto exp = slice->startValue.as()) { + transformExp(exp, temp, ExpUsage::Closure); + startValue = temp.back(); + temp.pop_back(); + } + std::string stopValue; + if (auto exp = slice->stopValue.as()) { + transformExp(exp, temp, ExpUsage::Closure); + stopValue = temp.back(); + temp.pop_back(); + } + std::string stepValue; + if (auto exp = slice->stepValue.as()) { + transformExp(exp, temp, ExpUsage::Closure); + stepValue = temp.back(); + temp.pop_back(); + } + listVar = getUnusedName("_list_"sv); + varBefore.push_back(listVar); + transformChainValue(chain, temp, ExpUsage::Closure); + _buf << indent() << "local "sv << listVar << " = "sv << temp.back() << nll(nameList); + std::string maxVar; + if (!stopValue.empty()) { + maxVar = getUnusedName("_max_"sv); + varBefore.push_back(maxVar); + _buf << indent() << "local "sv << maxVar << " = "sv << stopValue << nll(nameList); + } + _buf << indent() << "for "sv << indexVar << " = "sv; + _buf << startValue << ", "sv; + if (stopValue.empty()) { + _buf << "#"sv << listVar; + } else { + _buf << maxVar << " < 0 and #"sv << listVar << " + "sv << maxVar << " or "sv << maxVar; + } + if (!stepValue.empty()) { + _buf << ", "sv << stepValue; + } + _buf << " do"sv << nlr(loopTarget); + _buf << indent(1) << "local "sv << join(vars, ", "sv) << " = "sv << listVar << "["sv << indexVar << "]"sv << nll(nameList); + out.push_back(clearBuf()); + BLOCK_END + bool newListVal = false; + if (listVar.empty()) { + newListVal = true; + listVar = getUnusedName("_list_"sv); + varBefore.push_back(listVar); + } + if (!endWithSlice) { + transformExp(star_exp->value, temp, ExpUsage::Closure); + if (newListVal) _buf << indent() << "local "sv << listVar << " = "sv << temp.back() << nll(nameList); + _buf << indent() << "for "sv << indexVar << " = 1, #"sv << listVar << " do"sv << nlr(loopTarget); + _buf << indent(1) << "local "sv << join(vars) << " = "sv << listVar << "["sv << indexVar << "]"sv << nll(nameList); + out.push_back(clearBuf()); + } + break; + } + case id(): + transformExp(static_cast(loopTarget), temp, ExpUsage::Closure); + _buf << indent() << "for "sv << join(vars, ", "sv) << " in "sv << temp.back() << " do"sv << nlr(loopTarget); + out.push_back(clearBuf()); + break; + case id(): + transformExpList(static_cast(loopTarget), temp); + _buf << indent() << "for "sv << join(vars, ", "sv) << " in "sv << temp.back() << " do"sv << nlr(loopTarget); + out.push_back(clearBuf()); + break; + default: YUEE("AST node mismatch", loopTarget); break; + } + for (auto& var : varBefore) addToScope(var); + pushScope(); + for (auto& var : varAfter) addToScope(var); + if (!destructPairs.empty()) { + temp.clear(); + for (auto& pair : destructPairs) { + auto sValue = x->new_ptr(); + sValue->value.set(pair.first); + auto value = x->new_ptr(); + value->item.set(sValue); + auto exp = newExp(value, x); + auto expList = x->new_ptr(); + expList->exprs.push_back(exp); + auto assign = x->new_ptr(); + assign->values.push_back(pair.second); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + out.back().append(join(temp)); + } + } + + void transformCompForEach(CompForEach_t* comp, str_list& out) { + transformForEachHead(comp->nameList, comp->loopValue, out); + } + + void transformInvokeArgs(InvokeArgs_t* invokeArgs, str_list& out) { + if (invokeArgs->args.size() > 1) { + /* merge all the key-value pairs into one table + from arguments in the end */ + auto lastArg = invokeArgs->args.back(); + _ast_list* lastTable = nullptr; + if (auto tableBlock = ast_cast(lastArg)) { + lastTable = &tableBlock->values; + } else if (auto value = singleValueFrom(lastArg)) { + if (auto simpleTable = ast_cast(value->item)) { + lastTable = &simpleTable->pairs; + } + } + if (lastTable) { + ast_ptr ref(lastArg); + invokeArgs->args.pop_back(); + while (!invokeArgs->args.empty()) { + if (Value_t* value = singleValueFrom(invokeArgs->args.back())) { + if (auto tb = value->item.as()) { + const auto& ps = tb->pairs.objects(); + for (auto it = ps.rbegin(); it != ps.rend(); ++it) { + lastTable->push_front(*it); + } + invokeArgs->args.pop_back(); + continue; + } + } + break; + } + invokeArgs->args.push_back(lastArg); + } + } + str_list temp; + for (auto arg : invokeArgs->args.objects()) { + switch (arg->getId()) { + case id(): transformExp(static_cast(arg), temp, ExpUsage::Closure); break; + case id(): transformTableBlock(static_cast(arg), temp); break; + default: YUEE("AST node mismatch", arg); break; + } + } + out.push_back(s("("sv) + join(temp, ", "sv) + s(")"sv)); + } + + void transformForHead(For_t* forNode, str_list& out) { + str_list temp; + std::string varName = _parser.toString(forNode->varName); + transformExp(forNode->startValue, temp, ExpUsage::Closure); + transformExp(forNode->stopValue, temp, ExpUsage::Closure); + if (forNode->stepValue) { + transformExp(forNode->stepValue->value, temp, ExpUsage::Closure); + } else { + temp.emplace_back(); + } + auto it = temp.begin(); + const auto& start = *it; + const auto& stop = *(++it); + const auto& step = *(++it); + _buf << indent() << "for "sv << varName << " = "sv << start << ", "sv << stop << (step.empty() ? Empty : s(", "sv) + step) << " do"sv << nll(forNode); + pushScope(); + addToScope(varName); + out.push_back(clearBuf()); + } + + void transform_plain_body(ast_node* body, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { + switch (body->getId()) { + case id(): + transformBlock(static_cast(body), out, usage, assignList); + break; + case id(): { + auto newBlock = body->new_ptr(); + newBlock->statements.push_back(body); + transformBlock(newBlock, out, usage, assignList); + break; + } + default: YUEE("AST node mismatch", body); break; + } + } + + void transformLoopBody(ast_node* body, str_list& out, const std::string& appendContent, ExpUsage usage, ExpList_t* assignList = nullptr) { + str_list temp; + bool withContinue = traversal::Stop == body->traverse([&](ast_node* node) { + if (auto stmt = ast_cast(node)) { + if (stmt->content.is()) { + return _parser.toString(stmt->content) == "continue"sv ? + traversal::Stop : traversal::Return; + } else if (expListFrom(stmt)) { + return traversal::Continue; + } + return traversal::Return; + } + return traversal::Continue; + }); + if (withContinue) { + auto continueVar = getUnusedName("_continue_"sv); + addToScope(continueVar); + _buf << indent() << "local "sv << continueVar << " = false"sv << nll(body); + _buf << indent() << "repeat"sv << nll(body); + temp.push_back(clearBuf()); + _continueVars.push(continueVar); + pushScope(); + } + transform_plain_body(body, temp, usage, assignList); + if (withContinue) { + if (!appendContent.empty()) { + _buf << indent() << appendContent; + } + _buf << indent() << _continueVars.top() << " = true"sv << nll(body); + popScope(); + _buf << indent() << "until true"sv << nlr(body); + _buf << indent() << "if not "sv << _continueVars.top() << " then"sv << nlr(body); + _buf << indent(1) << "break"sv << nlr(body); + _buf << indent() << "end"sv << nlr(body); + temp.push_back(clearBuf()); + _continueVars.pop(); + } else if (!appendContent.empty()) { + temp.back().append(indent() + appendContent); + } + out.push_back(join(temp)); + } + + void transformFor(For_t* forNode, str_list& out) { + str_list temp; + transformForHead(forNode, temp); + transformLoopBody(forNode->body, temp, Empty, ExpUsage::Common); + popScope(); + out.push_back(join(temp) + indent() + s("end"sv) + nlr(forNode)); + } + + std::string transformForInner(For_t* forNode, str_list& out) { + auto x = forNode; + std::string accum = getUnusedName("_accum_"sv); + addToScope(accum); + std::string len = getUnusedName("_len_"sv); + addToScope(len); + _buf << indent() << "local "sv << accum << " = { }"sv << nll(forNode); + _buf << indent() << "local "sv << len << " = 1"sv << nll(forNode); + out.push_back(clearBuf()); + transformForHead(forNode, out); + auto expList = toAst(accum + s("["sv) + len + s("]"sv), x); + auto lenLine = len + s(" = "sv) + len + s(" + 1"sv) + nlr(forNode->body); + transformLoopBody(forNode->body, out, lenLine, ExpUsage::Assignment, expList); + popScope(); + out.push_back(indent() + s("end"sv) + nlr(forNode)); + return accum; + } + + void transformForClosure(For_t* forNode, str_list& out) { + str_list temp; + _buf << "(function()"sv << nll(forNode); + pushScope(); + _enableReturn.push(true); + auto accum = transformForInner(forNode, temp); + temp.push_back(indent() + s("return "sv) + accum + nlr(forNode)); + _enableReturn.pop(); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformForInPlace(For_t* forNode, str_list& out, ExpList_t* assignExpList = nullptr) { + auto x = forNode; + str_list temp; + if (assignExpList) { + _buf << indent() << "do"sv << nll(forNode); + pushScope(); + auto accum = transformForInner(forNode, temp); + auto assign = x->new_ptr(); + assign->values.push_back(toAst(accum, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(assignExpList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(forNode)); + } else { + auto accum = transformForInner(forNode, temp); + auto returnNode = x->new_ptr(); + auto expListLow = toAst(accum, x); + returnNode->valueList.set(expListLow); + transformReturn(returnNode, temp); + } + out.push_back(join(temp)); + } + + void transformBinaryOperator(BinaryOperator_t* node, str_list& out) { + auto op = _parser.toString(node); + out.push_back(op == "!="sv ? s("~="sv) : op); + } + + void transformForEach(ForEach_t* forEach, str_list& out) { + str_list temp; + transformForEachHead(forEach->nameList, forEach->loopValue, temp); + transformLoopBody(forEach->body, temp, Empty, ExpUsage::Common); + popScope(); + out.push_back(temp.front() + temp.back() + indent() + s("end"sv) + nlr(forEach)); + } + + std::string transformForEachInner(ForEach_t* forEach, str_list& out) { + auto x = forEach; + std::string accum = getUnusedName("_accum_"sv); + addToScope(accum); + std::string len = getUnusedName("_len_"sv); + addToScope(len); + _buf << indent() << "local "sv << accum << " = { }"sv << nll(forEach); + _buf << indent() << "local "sv << len << " = 1"sv << nll(forEach); + out.push_back(clearBuf()); + transformForEachHead(forEach->nameList, forEach->loopValue, out); + auto expList = toAst(accum + s("["sv) + len + s("]"sv), x); + auto lenLine = len + s(" = "sv) + len + s(" + 1"sv) + nlr(forEach->body); + transformLoopBody(forEach->body, out, lenLine, ExpUsage::Assignment, expList); + popScope(); + out.push_back(indent() + s("end"sv) + nlr(forEach)); + return accum; + } + + void transformForEachClosure(ForEach_t* forEach, str_list& out) { + str_list temp; + _buf << "(function()"sv << nll(forEach); + pushScope(); + _enableReturn.push(true); + auto accum = transformForEachInner(forEach, temp); + temp.push_back(indent() + s("return "sv) + accum + nlr(forEach)); + _enableReturn.pop(); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformForEachInPlace(ForEach_t* forEach, str_list& out, ExpList_t* assignExpList = nullptr) { + auto x = forEach; + str_list temp; + if (assignExpList) { + _buf << indent() << "do"sv << nll(forEach); + pushScope(); + auto accum = transformForEachInner(forEach, temp); + auto assign = x->new_ptr(); + assign->values.push_back(toAst(accum, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(assignExpList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(forEach)); + } else { + auto accum = transformForEachInner(forEach, temp); + auto returnNode = x->new_ptr(); + auto expListLow = toAst(accum, x); + returnNode->valueList.set(expListLow); + transformReturn(returnNode, temp); + } + out.push_back(join(temp)); + } + + void transform_variable_pair(variable_pair_t* pair, str_list& out) { + auto name = _parser.toString(pair->name); + out.push_back(name + s(" = "sv) + name); + } + + void transform_normal_pair(normal_pair_t* pair, str_list& out) { + auto key = pair->key.get(); + str_list temp; + switch (key->getId()) { + case id(): { + transformKeyName(static_cast(key), temp); + if (LuaKeywords.find(temp.back()) != LuaKeywords.end()) { + temp.back() = s("[\""sv) + temp.back() + s("\"]"); + } + break; + } + case id(): + transformExp(static_cast(key), temp, ExpUsage::Closure); + temp.back() = s(temp.back().front() == '[' ? "[ "sv : "["sv) + temp.back() + s("]"sv); + break; + case id(): + transformDoubleString(static_cast(key), temp); + temp.back() = s("["sv) + temp.back() + s("]"sv); + break; + case id(): transformSingleString(static_cast(key), temp); + temp.back() = s("["sv) + temp.back() + s("]"sv); + break; + case id(): transformLuaString(static_cast(key), temp); + temp.back() = s("[ "sv) + temp.back() + s("]"sv); + break; + default: YUEE("AST node mismatch", key); break; + } + auto value = pair->value.get(); + switch (value->getId()) { + case id(): transformExp(static_cast(value), temp, ExpUsage::Closure); break; + case id(): transformTableBlock(static_cast(value), temp); break; + default: YUEE("AST node mismatch", value); break; + } + out.push_back(temp.front() + s(" = "sv) + temp.back()); + } + + void transformKeyName(KeyName_t* keyName, str_list& out) { + auto name = keyName->name.get(); + switch (name->getId()) { + case id(): transformSelfName(static_cast(name), out); break; + case id(): out.push_back(_parser.toString(name)); break; + default: YUEE("AST node mismatch", name); break; + } + } + + void transformLuaString(LuaString_t* luaString, str_list& out) { + auto content = _parser.toString(luaString->content); + Utils::replace(content, "\r\n"sv, "\n"); + if (content[0] == '\n') content.erase(content.begin()); + out.push_back(_parser.toString(luaString->open) + content + _parser.toString(luaString->close)); + } + + void transformSingleString(SingleString_t* singleString, str_list& out) { + auto str = _parser.toString(singleString); + Utils::replace(str, "\r\n"sv, "\n"); + Utils::replace(str, "\n"sv, "\\n"sv); + out.push_back(str); + } + + void transformDoubleString(DoubleString_t* doubleString, str_list& out) { + str_list temp; + for (auto _seg : doubleString->segments.objects()) { + auto seg = static_cast(_seg); + auto content = seg->content.get(); + switch (content->getId()) { + case id(): { + auto str = _parser.toString(content); + Utils::replace(str, "\r\n"sv, "\n"); + Utils::replace(str, "\n"sv, "\\n"sv); + temp.push_back(s("\""sv) + str + s("\""sv)); + break; + } + case id(): { + transformExp(static_cast(content), temp, ExpUsage::Closure); + std::string tostr("tostring"sv); + temp.back() = tostr + '(' + temp.back() + s(")"sv); + if (_config.lintGlobalVariable) { + if (!isDefined(tostr)) { + if (_globals.find(tostr) == _globals.end()) { + _globals[tostr] = {content->m_begin.m_line, content->m_begin.m_col}; + } + } + } + break; + } + default: YUEE("AST node mismatch", content); break; + } + } + out.push_back(temp.empty() ? s("\"\""sv) : join(temp, " .. "sv)); + } + + void transformString(String_t* string, str_list& out) { + auto str = string->str.get(); + switch (str->getId()) { + case id(): transformSingleString(static_cast(str), out); break; + case id(): transformDoubleString(static_cast(str), out); break; + case id(): transformLuaString(static_cast(str), out); break; + default: YUEE("AST node mismatch", str); break; + } + } + + std::pair defineClassVariable(Assignable_t* assignable) { + if (auto variable = assignable->item.as()) { + auto name = _parser.toString(variable); + if (addToScope(name)) { + return {name, true}; + } else { + return {name, false}; + } + } + return {Empty, false}; + } + + void transformClassDeclClosure(ClassDecl_t* classDecl, str_list& out) { + str_list temp; + temp.push_back(s("(function()"sv) + nll(classDecl)); + pushScope(); + _enableReturn.push(true); + transformClassDecl(classDecl, temp, ExpUsage::Return); + _enableReturn.pop(); + popScope(); + temp.push_back(s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformClassDecl(ClassDecl_t* classDecl, str_list& out, ExpUsage usage, ExpList_t* expList = nullptr) { + str_list temp; + auto x = classDecl; + auto body = classDecl->body.get(); + auto assignable = classDecl->name.get(); + auto extend = classDecl->extend.get(); + std::string className; + std::string assignItem; + if (assignable) { + if (!isAssignable(assignable)) { + throw std::logic_error(_info.errorMessage("left hand expression is not assignable"sv, assignable)); + } + bool newDefined = false; + std::tie(className, newDefined) = defineClassVariable(assignable); + if (newDefined) { + temp.push_back(indent() + s("local "sv) + className + nll(classDecl)); + } + if (className.empty()) { + if (auto chain = ast_cast(assignable->item)) { + if (auto dotChain = ast_cast(chain->items.back())) { + className = s("\""sv) + _parser.toString(dotChain->name) + s("\""sv); + } else if (auto index = ast_cast(chain->items.back())) { + if (auto name = index->getByPath()) { + transformString(name, temp); + className = temp.back(); + temp.pop_back(); + } + } + } + } else { + className = s("\""sv) + className + s("\""sv); + } + pushScope(); + transformAssignable(assignable, temp); + popScope(); + assignItem = temp.back(); + temp.pop_back(); + } else if (expList) { + auto name = singleVariableFrom(expList); + if (!name.empty()) { + className = s("\""sv) + name + s("\""sv); + } + } + temp.push_back(indent() + s("do"sv) + nll(classDecl)); + pushScope(); + auto classVar = getUnusedName("_class_"sv); + addToScope(classVar); + temp.push_back(indent() + s("local "sv) + classVar + nll(classDecl)); + if (body) { + str_list varDefs; + for (auto item : body->contents.objects()) { + if (auto statement = ast_cast(item)) { + ClassDecl_t* clsDecl = nullptr; + if (auto assignment = assignmentFrom(statement)) { + auto names = transformAssignDefs(assignment->expList.get()); + varDefs.insert(varDefs.end(), names.begin(), names.end()); + auto info = extractDestructureInfo(assignment, true); + if (!info.first.empty()) { + for (const auto& destruct : info.first) + for (const auto& item : destruct.items) + if (item.isVariable && addToScope(item.name)) + varDefs.push_back(item.name); + } + BLOCK_START + auto assign = assignment->action.as(); + BREAK_IF(!assign); + BREAK_IF(assign->values.objects().size() != 1); + auto exp = ast_cast(assign->values.objects().front()); + BREAK_IF(!exp); + auto value = singleValueFrom(exp); + clsDecl = value->getByPath(); + BLOCK_END + } else if (auto expList = expListFrom(statement)) { + auto value = singleValueFrom(expList); + clsDecl = value->getByPath(); + } + if (clsDecl) { + std::string clsName; + bool newDefined = false; + std::tie(clsName,newDefined) = defineClassVariable(clsDecl->name); + if (newDefined) varDefs.push_back(clsName); + } + } + } + if (!varDefs.empty()) { + temp.push_back(indent() + s("local "sv) + join(varDefs, ", "sv) + nll(body)); + } + } + std::string parent, parentVar; + if (extend) { + parentVar = getUnusedName("_parent_"sv); + addToScope(parentVar); + transformExp(extend, temp, ExpUsage::Closure); + parent = temp.back(); + temp.pop_back(); + temp.push_back(indent() + s("local "sv) + parentVar + s(" = "sv) + parent + nll(classDecl)); + } + auto baseVar = getUnusedName("_base_"sv); + addToScope(baseVar); + temp.push_back(indent() + s("local "sv) + baseVar + s(" = "sv)); + str_list builtins; + str_list commons; + str_list statements; + if (body) { + std::list members; + for (auto content : classDecl->body->contents.objects()) { + switch (content->getId()) { + case id(): { + size_t inc = transform_class_member_list(static_cast(content), members, classVar); + auto it = members.end(); + for (size_t i = 0; i < inc; ++i, --it); + for (; it != members.end(); ++it) { + auto& member = *it; + if (member.type == MemType::Property) { + statements.push_back(indent() + member.item + nll(content)); + } else { + member.item = indent(1) + member.item; + } + } + break; + } + case id(): + transformStatement(static_cast(content), statements); + break; + default:YUEE("AST node mismatch", content); break; + } + } + for (auto& member : members) { + switch (member.type) { + case MemType::Common: + commons.push_back((commons.empty() ? Empty : s(","sv) + nll(member.node)) + member.item); + break; + case MemType::Builtin: + builtins.push_back((builtins.empty() ? Empty : s(","sv) + nll(member.node)) + member.item); + break; + default: break; + } + } + if (!commons.empty()) { + temp.back() += s("{"sv) + nll(body); + temp.push_back(join(commons) + nll(body)); + temp.push_back(indent() + s("}"sv) + nll(body)); + } else { + temp.back() += s("{ }"sv) + nll(body); + } + } else { + temp.back() += s("{ }"sv) + nll(classDecl); + } + temp.push_back(indent() + baseVar + s(".__index = "sv) + baseVar + nll(classDecl)); + str_list tmp; + if (usage == ExpUsage::Assignment) { + auto assign = x->new_ptr(); + assign->values.push_back(toAst(classVar, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, tmp); + } + if (extend) { + _buf << indent() << "setmetatable("sv << baseVar << ", "sv << parentVar << ".__base)"sv << nll(classDecl); + } + _buf << indent() << classVar << " = setmetatable({"sv << nll(classDecl); + if (!builtins.empty()) { + _buf << join(builtins) << ","sv << nll(classDecl); + } else { + if (extend) { + _buf << indent(1) << "__init = function(self, ...)"sv << nll(classDecl); + _buf << indent(2) << "return _class_0.__parent.__init(self, ...)"sv << nll(classDecl); + _buf << indent(1) << "end,"sv << nll(classDecl); + } else { + _buf << indent(1) << "__init = function() end,"sv << nll(classDecl); + } + } + _buf << indent(1) << "__base = "sv << baseVar; + if (!className.empty()) { + _buf << ","sv << nll(classDecl); + _buf << indent(1) << "__name = "sv << className; + } + if (extend) { + _buf << ","sv << nll(classDecl); + _buf << indent(1) << "__parent = "sv << parentVar; + } + _buf << nll(classDecl); + _buf << indent() << "}, {"sv << nll(classDecl); + if (extend) { + _buf << indent(1) << "__index = function(cls, name)"sv << nll(classDecl); + _buf << indent(2) << "local val = rawget("sv << baseVar << ", name)"sv << nll(classDecl); + _buf << indent(2) << "if val == nil then"sv << nll(classDecl); + _buf << indent(3) << "local parent = rawget(cls, \"__parent\")"sv << nll(classDecl); + _buf << indent(3) << "if parent then"sv << nll(classDecl); + _buf << indent(4) << "return parent[name]"sv << nll(classDecl); + _buf << indent(3) << "end"sv << nll(classDecl); + _buf << indent(2) << "else"sv << nll(classDecl); + _buf << indent(3) << "return val"sv << nll(classDecl); + _buf << indent(2) << "end"sv << nll(classDecl); + _buf << indent(1) << "end,"sv << nll(classDecl); + } else { + _buf << indent(1) << "__index = "sv << baseVar << ","sv << nll(classDecl); + } + _buf << indent(1) << "__call = function(cls, ...)"sv << nll(classDecl); + pushScope(); + auto selfVar = getUnusedName("_self_"sv); + addToScope(selfVar); + _buf << indent(1) << "local "sv << selfVar << " = setmetatable({}, "sv << baseVar << ")"sv << nll(classDecl); + _buf << indent(1) << "cls.__init("sv << selfVar << ", ...)"sv << nll(classDecl); + _buf << indent(1) << "return "sv << selfVar << nll(classDecl); + popScope(); + _buf << indent(1) << "end"sv << nll(classDecl); + _buf << indent() << "})"sv << nll(classDecl); + _buf << indent() << baseVar << ".__class = "sv << classVar << nll(classDecl); + if (!statements.empty()) { + _buf << indent() << "local self = "sv << classVar << ';' << nll(classDecl); + } + _buf << join(statements); + if (extend) { + _buf << indent() << "if "sv << parentVar << ".__inherited then"sv << nll(classDecl); + _buf << indent(1) << parentVar << ".__inherited("sv << parentVar << ", "sv << classVar << ")"sv << nll(classDecl); + _buf << indent() << "end"sv << nll(classDecl); + } + if (!assignItem.empty()) { + _buf << indent() << assignItem << " = "sv << classVar << nll(classDecl); + } + switch (usage) { + case ExpUsage::Return: { + _buf << indent() << "return "sv << classVar << nlr(classDecl); + break; + } + case ExpUsage::Assignment: { + _buf << tmp.back(); + break; + } + default: break; + } + temp.push_back(clearBuf()); + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(classDecl)); + out.push_back(join(temp)); + } + + size_t transform_class_member_list(class_member_list_t* class_member_list, std::list& out, const std::string& classVar) { + str_list temp; + size_t count = 0; + for (auto keyValue : class_member_list->values.objects()) { + MemType type = MemType::Common; + BLOCK_START + auto normal_pair = ast_cast(keyValue); + BREAK_IF(!normal_pair); + auto keyName = normal_pair->key.as(); + BREAK_IF(!keyName); + std::string newSuperCall; + auto selfName = keyName->name.as(); + if (selfName) { + type = MemType::Property; + auto name = ast_cast(selfName->name); + if (!name) throw std::logic_error(_info.errorMessage("invalid class poperty name"sv, selfName->name)); + newSuperCall = classVar + s(".__parent."sv) + _parser.toString(name->name); + } else { + auto x = keyName; + auto nameNode = keyName->name.as(); + if (!nameNode) break; + auto name = _parser.toString(nameNode); + if (name == "new"sv) { + type = MemType::Builtin; + keyName->name.set(toAst("__init"sv, x)); + newSuperCall = classVar + s(".__parent.__init"sv); + } else { + newSuperCall = classVar + s(".__parent.__base."sv) + name; + } + } + normal_pair->value->traverse([&](ast_node* node) { + if (node->getId() == id()) return traversal::Return; + if (auto chainValue = ast_cast(node)) { + if (auto callable = ast_cast(chainValue->items.front())) { + auto var = callable->item.get(); + if (_parser.toString(var) == "super"sv) { + auto insertSelfToArguments = [&](ast_node* item) { + auto x = item; + switch (item->getId()) { + case id(): { + auto invoke = static_cast(item); + invoke->args.push_front(toAst("self"sv, x)); + return true; + } + case id(): { + auto invoke = static_cast(item); + invoke->args.push_front(toAst("self"sv, x)); + return true; + } + default: + return false; + } + }; + const auto& chainList = chainValue->items.objects(); + if (chainList.size() >= 2) { + auto it = chainList.begin(); + auto secondItem = *(++it); + if (!insertSelfToArguments(secondItem)) { + if (auto colonChainItem = ast_cast(secondItem)) { + if (chainList.size() > 2 && insertSelfToArguments(*(++it))) { + colonChainItem->switchToDot = true; + } + } + newSuperCall = classVar + s(".__parent"sv); + } + } else { + newSuperCall = classVar + s(".__parent"sv); + } + auto newChain = toAst(newSuperCall, chainValue); + chainValue->items.pop_front(); + const auto& items = newChain->items.objects(); + for (auto it = items.rbegin(); it != items.rend(); ++it) { + chainValue->items.push_front(*it); + } + } + } + } + return traversal::Continue; + }); + BLOCK_END + pushScope(); + if (type == MemType::Property) { + decIndentOffset(); + } + switch (keyValue->getId()) { + case id(): + transform_variable_pair(static_cast(keyValue), temp); + break; + case id(): + transform_normal_pair(static_cast(keyValue), temp); + break; + default: YUEE("AST node mismatch", keyValue); break; + } + if (type == MemType::Property) { + incIndentOffset(); + } + popScope(); + out.push_back({temp.back(), type, keyValue}); + temp.clear(); + ++count; + } + return count; + } + + void transformAssignable(Assignable_t* assignable, str_list& out) { + auto item = assignable->item.get(); + switch (item->getId()) { + case id(): transformAssignableChain(static_cast(item), out); break; + case id(): transformVariable(static_cast(item), out); break; + case id(): transformSelfName(static_cast(item), out); break; + default: YUEE("AST node mismatch", item); break; + } + } + + void transformWithClosure(With_t* with, str_list& out) { + str_list temp; + temp.push_back(s("(function()"sv) + nll(with)); + pushScope(); + _enableReturn.push(true); + transformWith(with, temp, nullptr, true); + _enableReturn.pop(); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformWith(With_t* with, str_list& out, ExpList_t* assignList = nullptr, bool returnValue = false) { + auto x = with; + str_list temp; + std::string withVar; + bool scoped = false; + if (with->assigns) { + checkAssignable(with->valueList); + auto vars = getAssignVars(with); + if (vars.front().empty()) { + withVar = getUnusedName("_with_"sv); + { + auto assignment = x->new_ptr(); + assignment->expList.set(toAst(withVar, x)); + auto assign = x->new_ptr(); + assign->values.push_back(with->assigns->values.objects().front()); + assignment->action.set(assign); + if (!returnValue) { + scoped = true; + temp.push_back(indent() + s("do"sv) + nll(with)); + pushScope(); + } + transformAssignment(assignment, temp); + } + auto assignment = x->new_ptr(); + assignment->expList.set(with->valueList); + auto assign = x->new_ptr(); + assign->values.push_back(toAst(withVar, x)); + bool skipFirst = true; + for (auto value : with->assigns->values.objects()) { + if (skipFirst) { + skipFirst = false; + continue; + } + assign->values.push_back(value); + } + assignment->action.set(assign); + transformAssignment(assignment, temp); + } else { + withVar = vars.front(); + auto assignment = x->new_ptr(); + assignment->expList.set(with->valueList); + assignment->action.set(with->assigns); + if (!returnValue) { + scoped = true; + temp.push_back(indent() + s("do"sv) + nll(with)); + pushScope(); + } + transformAssignment(assignment, temp); + } + } else { + withVar = getUnusedName("_with_"sv); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst(withVar, x)); + auto assign = x->new_ptr(); + assign->values.dup(with->valueList->exprs); + assignment->action.set(assign); + if (!returnValue) { + scoped = true; + temp.push_back(indent() + s("do"sv) + nll(with)); + pushScope(); + } + transformAssignment(assignment, temp); + } + if (!with->eop && !scoped && !returnValue) { + pushScope(); + scoped = traversal::Stop == with->body->traverse([&](ast_node* node) { + if (auto statement = ast_cast(node)) { + ClassDecl_t* clsDecl = nullptr; + if (auto assignment = assignmentFrom(statement)) { + auto names = getAssignDefs(assignment->expList.get()); + if (!names.empty()) { + return traversal::Stop; + } + auto info = extractDestructureInfo(assignment, true); + if (!info.first.empty()) { + for (const auto& destruct : info.first) + for (const auto& item : destruct.items) + if (item.isVariable && !isDefined(item.name)) + return traversal::Stop; + } + BLOCK_START + auto assign = assignment->action.as(); + BREAK_IF(!assign); + BREAK_IF(assign->values.objects().size() != 1); + auto exp = ast_cast(assign->values.objects().front()); + BREAK_IF(!exp); + if (auto value = singleValueFrom(exp)) { + clsDecl = value->getByPath(); + } + BLOCK_END + } else if (auto expList = expListFrom(statement)) { + auto value = singleValueFrom(expList); + clsDecl = value->getByPath(); + } + if (clsDecl) { + auto variable = clsDecl->name.as(); + if (!isDefined(_parser.toString(variable))) return traversal::Stop; + } + return traversal::Return; + } + return traversal::Continue; + }); + popScope(); + if (scoped) { + temp.push_back(indent() + s("do"sv) + nll(with)); + pushScope(); + } + } + _withVars.push(withVar); + if (with->eop) { + auto ifNode = x->new_ptr(); + ifNode->nodes.push_back(toAst(withVar + s("~=nil"sv), x)); + ifNode->nodes.push_back(with->body); + transformIf(ifNode, temp, ExpUsage::Common); + } else { + transform_plain_body(with->body, temp, ExpUsage::Common); + } + _withVars.pop(); + if (assignList) { + auto assignment = x->new_ptr(); + assignment->expList.set(assignList); + auto assign = x->new_ptr(); + assign->values.push_back(toAst(withVar, x)); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + if (returnValue) { + auto last = lastStatementFrom(with->body); + if (last && !last->content.is()) { + temp.push_back(indent() + s("return "sv) + withVar + nll(with)); + } + } + if (scoped) { + popScope(); + temp.push_back(indent() + s("end"sv) + nll(with)); + } + out.push_back(join(temp)); + } + + void transform_const_value(const_value_t* const_value, str_list& out) { + out.push_back(_parser.toString(const_value)); + } + + void transformGlobal(Global_t* global, str_list& out) { + auto x = global; + auto item = global->item.get(); + switch (item->getId()) { + case id(): { + auto classDecl = static_cast(item); + if (classDecl->name && classDecl->name->item->getId() == id()) { + markVarGlobal(GlobalMode::Any, true); + addGlobalVar(_parser.toString(classDecl->name->item)); + } + transformClassDecl(classDecl, out, ExpUsage::Common); + break; + } + case id(): + if (_parser.toString(item) == "*"sv) { + markVarGlobal(GlobalMode::Any, false); + } else { + markVarGlobal(GlobalMode::Capital, false); + } + break; + case id(): { + markVarGlobal(GlobalMode::Any, true); + auto values = global->item.to(); + if (values->valueList) { + auto expList = x->new_ptr(); + for (auto name : values->nameList->names.objects()) { + addGlobalVar(_parser.toString(name)); + auto callable = x->new_ptr(); + callable->item.set(name); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = newExp(value, x); + expList->exprs.push_back(exp); + } + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + auto assign = x->new_ptr(); + if (auto expListLow = values->valueList.as()) { + assign->values.dup(expListLow->exprs); + } else { + auto tableBlock = values->valueList.to(); + assign->values.push_back(tableBlock); + } + assignment->action.set(assign); + transformAssignment(assignment, out); + } else { + for (auto name : values->nameList->names.objects()) { + addGlobalVar(_parser.toString(name)); + } + } + break; + } + default: YUEE("AST node mismatch", item); break; + } + } + + void transformExport(Export_t* exportNode, str_list& out) { + auto x = exportNode; + if (_scopes.size() > 1) { + throw std::logic_error(_info.errorMessage("can not do module export outside the root block"sv, exportNode)); + } + if (exportNode->assign) { + auto expList = exportNode->target.to(); + if (expList->exprs.size() != exportNode->assign->values.size()) { + throw std::logic_error(_info.errorMessage("left and right expressions must be matched in export statement"sv, x)); + } + for (auto _exp : expList->exprs.objects()) { + auto exp = static_cast(_exp); + if (!variableFrom(exp) && + !exp->getByPath() && + !exp->getByPath()) { + throw std::logic_error(_info.errorMessage("left hand expressions must be variables in export statement"sv, x)); + } + } + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(exportNode->assign); + transformAssignment(assignment, out); + str_list names = transformAssignDefs(expList, false); + auto info = extractDestructureInfo(assignment, true); + if (!info.first.empty()) { + for (const auto& destruct : info.first) + for (const auto& item : destruct.items) + if (item.isVariable) + names.push_back(item.name); + } + if (_info.exportDefault) { + out.back().append(indent() + _info.moduleName + s(" = "sv) + names.back() + nlr(exportNode)); + } else { + str_list lefts, rights; + for (const auto& name : names) { + lefts.push_back(_info.moduleName + s("[\""sv) + name + s("\"]"sv)); + rights.push_back(name); + } + out.back().append(indent() + join(lefts,", "sv) + s(" = "sv) + join(rights, ", "sv) + nlr(exportNode)); + } + } else { + if (auto macro = exportNode->target.as()) { + transformMacro(macro, out, true); + } else if (_info.exportDefault) { + auto exp = exportNode->target.to(); + auto assignment = x->new_ptr(); + assignment->expList.set(toAst(_info.moduleName, x)); + auto assign = x->new_ptr(); + assign->values.push_back(exp); + assignment->action.set(assign); + transformAssignment(assignment, out); + } else { + str_list temp; + auto expList = exportNode->target.to(); + auto assignment = x->new_ptr(); + auto assignList = toAst(_info.moduleName + s("[#"sv) + _info.moduleName + s("+1]"sv), x); + assignment->expList.set(assignList); + for (auto exp : expList->exprs.objects()) { + if (auto classDecl = exp->getByPath()) { + if (classDecl->name && classDecl->name->item->getId() == id()) { + transformClassDecl(classDecl, temp, ExpUsage::Common); + auto name = _parser.toString(classDecl->name->item); + assignment->expList.set(toAst(_info.moduleName + s("[\""sv) + name + s("\"]"sv), x)); + auto assign = x->new_ptr(); + assign->values.push_back(toAst(name, x)); + assignment->action.set(assign); + transformAssignment(assignment, temp); + assignment->expList.set(assignList); + continue; + } + } + auto assign = x->new_ptr(); + assign->values.push_back(exp); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + out.push_back(join(temp)); + } + } + } + + void transformTable(ast_node* table, const node_container& pairs, str_list& out) { + if (pairs.empty()) { + out.push_back(s("{ }"sv)); + return; + } + str_list temp; + incIndentOffset(); + for (auto pair : pairs) { + switch (pair->getId()) { + case id(): transformExp(static_cast(pair), temp, ExpUsage::Closure); break; + case id(): transform_variable_pair(static_cast(pair), temp); break; + case id(): transform_normal_pair(static_cast(pair), temp); break; + case id(): transformTableBlockIndent(static_cast(pair), temp); break; + case id(): transformTableBlock(static_cast(pair), temp); break; + default: YUEE("AST node mismatch", pair); break; + } + temp.back() = indent() + temp.back() + (pair == pairs.back() ? Empty : s(","sv)) + nll(pair); + } + out.push_back(s("{"sv) + nll(table) + join(temp)); + decIndentOffset(); + out.back() += (indent() + s("}"sv)); + } + + void transform_simple_table(simple_table_t* table, str_list& out) { + transformTable(table, table->pairs.objects(), out); + } + + void transformTblComprehension(TblComprehension_t* comp, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { + switch (usage) { + case ExpUsage::Closure: + pushScope(); + _enableReturn.push(true); + break; + case ExpUsage::Assignment: + pushScope(); + break; + default: + break; + } + auto x = comp; + str_list kv; + std::string tbl = getUnusedName("_tbl_"sv); + addToScope(tbl); + str_list temp; + auto compInner = comp->forLoop.get(); + for (auto item : compInner->items.objects()) { + switch (item->getId()) { + case id(): + transformCompForEach(static_cast(item), temp); + break; + case id(): + transformCompFor(static_cast(item), temp); + break; + case id(): + transformExp(static_cast(item), temp, ExpUsage::Closure); + temp.back() = indent() + s("if "sv) + temp.back() + s(" then"sv) + nll(item); + pushScope(); + break; + default: YUEE("AST node mismatch", item); break; + } + } + transformExp(comp->key, kv, ExpUsage::Closure); + if (comp->value) { + transformExp(comp->value->value, kv, ExpUsage::Closure); + } + for (size_t i = 0; i < compInner->items.objects().size(); ++i) { + popScope(); + } + _buf << indent() << "local "sv << tbl << " = { }"sv << nll(comp); + _buf << join(temp); + pushScope(); + if (!comp->value) { + auto keyVar = getUnusedName("_key_"sv); + auto valVar = getUnusedName("_val_"sv); + _buf << indent(int(temp.size()) - 1) << "local "sv << keyVar << ", "sv << valVar << " = "sv << kv.front() << nll(comp); + kv.front() = keyVar; + kv.push_back(valVar); + } + _buf << indent(int(temp.size()) - 1) << tbl << "["sv << kv.front() << "] = "sv << kv.back() << nll(comp); + for (int ind = int(temp.size()) - 2; ind > -1 ; --ind) { + _buf << indent(ind) << "end"sv << nll(comp); + } + popScope(); + _buf << indent() << "end"sv << nll(comp); + switch (usage) { + case ExpUsage::Closure: + out.push_back(clearBuf() + indent() + s("return "sv) + tbl + nlr(comp)); + popScope(); + _enableReturn.pop(); + out.back().insert(0, s("(function()"sv) + nll(comp)); + out.back().append(indent() + s("end)()"sv)); + break; + case ExpUsage::Assignment: { + out.push_back(clearBuf()); + auto assign = x->new_ptr(); + assign->values.push_back(toAst(tbl, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(assignList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + out.back().append(temp.back()); + popScope(); + out.back().insert(0, indent() + s("do"sv) + nll(comp)); + out.back().append(indent() + s("end"sv) + nlr(comp)); + break; + } + case ExpUsage::Return: + out.push_back(clearBuf() + indent() + s("return "sv) + tbl + nlr(comp)); + break; + default: + break; + } + } + + void transformCompFor(CompFor_t* comp, str_list& out) { + str_list temp; + std::string varName = _parser.toString(comp->varName); + transformExp(comp->startValue, temp, ExpUsage::Closure); + transformExp(comp->stopValue, temp, ExpUsage::Closure); + if (comp->stepValue) { + transformExp(comp->stepValue->value, temp, ExpUsage::Closure); + } else { + temp.emplace_back(); + } + auto it = temp.begin(); + const auto& start = *it; + const auto& stop = *(++it); + const auto& step = *(++it); + _buf << indent() << "for "sv << varName << " = "sv << start << ", "sv << stop << (step.empty() ? Empty : s(", "sv) + step) << " do"sv << nll(comp); + out.push_back(clearBuf()); + pushScope(); + addToScope(varName); + } + + void transformTableBlockIndent(TableBlockIndent_t* table, str_list& out) { + transformTable(table, table->values.objects(), out); + } + + void transformTableBlock(TableBlock_t* table, str_list& out) { + transformTable(table, table->values.objects(), out); + } + + void transformDo(Do_t* doNode, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { + str_list temp; + if (usage == ExpUsage::Closure) { + temp.push_back(s("(function()"sv) + nll(doNode)); + _enableReturn.push(true); + } else { + temp.push_back(indent() + s("do"sv) + nll(doNode)); + } + pushScope(); + transformBody(doNode->body, temp, usage, assignList); + popScope(); + if (usage == ExpUsage::Closure) { + _enableReturn.pop(); + temp.push_back(indent() + s("end)()"sv)); + } else { + temp.push_back(indent() + s("end"sv) + nlr(doNode)); + } + out.push_back(join(temp)); + } + + void transformImportFrom(ImportFrom_t* import, str_list& out) { + str_list temp; + auto x = import; + auto objVar = singleVariableFrom(import->exp); + ast_ptr objAssign; + if (objVar.empty()) { + objVar = getUnusedName("_obj_"sv); + auto expList = toAst(objVar, x); + auto assign = x->new_ptr(); + assign->values.push_back(import->exp); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + objAssign.set(assignment); + } + auto expList = x->new_ptr(); + auto assign = x->new_ptr(); + for (auto name : import->names.objects()) { + switch (name->getId()) { + case id(): { + auto var = ast_to(name); + { + auto callable = toAst(objVar, x); + auto dotChainItem = x->new_ptr(); + dotChainItem->name.set(var->name); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + chainValue->items.push_back(dotChainItem); + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = newExp(value, x); + assign->values.push_back(exp); + } + auto callable = x->new_ptr(); + callable->item.set(var); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = newExp(value, x); + expList->exprs.push_back(exp); + break; + } + case id(): { + auto var = static_cast(name)->name.get(); + { + auto nameNode = var->name.get(); + auto callable = toAst(objVar, x); + auto colonChain = x->new_ptr(); + colonChain->name.set(nameNode); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + chainValue->items.push_back(colonChain); + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = newExp(value, x); + assign->values.push_back(exp); + } + auto callable = x->new_ptr(); + callable->item.set(var); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = newExp(value, x); + expList->exprs.push_back(exp); + break; + } + default: YUEE("AST node mismatch", name); break; + } + } + if (objAssign) { + auto preDef = getPredefine(transformAssignDefs(expList)); + if (!preDef.empty()) { + temp.push_back(preDef + nll(import)); + } + temp.push_back(indent() + s("do"sv) + nll(import)); + pushScope(); + transformAssignment(objAssign, temp); + } + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + if (objAssign) { + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(import)); + } + out.push_back(join(temp)); + } + + std::string moduleNameFrom(ImportLiteral_t* literal) { + auto name = _parser.toString(literal->inners.back()); + Utils::replace(name, "-"sv, "_"sv); + Utils::replace(name, " "sv, "_"sv); + return name; + } + + void transformImportAs(ImportAs_t* import, str_list& out) { + auto x = import; + if (!import->target) { + auto name = moduleNameFrom(import->literal); + import->target.set(toAst(name, x)); + } + if (auto tabLit = import->target.as()) { + auto newTab = x->new_ptr(); +#ifndef YUE_NO_MACRO + bool importAllMacro = false; + std::list> macroPairs; + for (auto item : tabLit->items.objects()) { + switch (item->getId()) { + case id(): { + auto macroName = static_cast(item); + auto name = _parser.toString(macroName->name); + macroPairs.emplace_back(name, name); + break; + } + case id(): { + auto pair = static_cast(item); + macroPairs.emplace_back(_parser.toString(pair->key->name), _parser.toString(pair->value->name)); + break; + } + case id(): + if (importAllMacro) throw std::logic_error(_info.errorMessage(s("import all macro symbol duplicated"sv), item)); + importAllMacro = true; + break; + case id(): + case id(): + newTab->items.push_back(item); + break; + default: YUEE("AST node mismatch", item); break; + } + } + if (importAllMacro || !macroPairs.empty()) { + auto moduleName = _parser.toString(import->literal); + Utils::replace(moduleName, "'"sv, ""sv); + Utils::replace(moduleName, "\""sv, ""sv); + Utils::trim(moduleName); + pushCurrentModule(); // cur + int top = lua_gettop(L) - 1; // Lua state may be setup by pushCurrentModule() + DEFER(lua_settop(L, top)); + pushYue("find_modulepath"sv); // cur find_modulepath + lua_pushlstring(L, moduleName.c_str(), moduleName.size()); // cur find_modulepath moduleName + if (lua_pcall(L, 1, 1, 0) != 0) { + std::string err = lua_tostring(L, -1); + throw std::logic_error(_info.errorMessage(s("failed to resolve module path\n"sv) + err, x)); + } + if (lua_isnil(L, -1) != 0) { + throw std::logic_error(_info.errorMessage(s("failed to find module '"sv) + moduleName + '\'', x)); + } + std::string moduleFullName = lua_tostring(L, -1); + lua_pop(L, 1); // cur + if (!isModuleLoaded(moduleFullName)) { + pushYue("read_file"sv); // cur read_file + lua_pushlstring(L, moduleFullName.c_str(), moduleFullName.size()); // cur load_text moduleFullName + if (lua_pcall(L, 1, 1, 0) != 0) { + std::string err = lua_tostring(L, -1); + throw std::logic_error(_info.errorMessage(s("failed to read module file\n"sv) + err, x)); + } // cur text + if (lua_isnil(L, -1) != 0) { + throw std::logic_error(_info.errorMessage("failed to get module text"sv, x)); + } // cur text + std::string text = lua_tostring(L, -1); + auto compiler = YueCompilerImpl(L, _luaOpen, false, moduleFullName); + YueConfig config; + config.lineOffset = 0; + config.lintGlobalVariable = false; + config.reserveLineNumber = false; + config.implicitReturnRoot = _config.implicitReturnRoot; + auto result = compiler.compile(text, config); + if (result.codes.empty() && !result.error.empty()) { + throw std::logic_error(_info.errorMessage(s("failed to compile module '"sv) + moduleName + s("\': "sv) + result.error, x)); + } + lua_pop(L, 1); // cur + } + pushModuleTable(moduleFullName); // cur mod + if (importAllMacro) { + lua_pushnil(L); // cur mod startKey + while (lua_next(L, -2) != 0) { // cur mod key value + lua_pushvalue(L, -2); // cur mod key value key + lua_insert(L, -2); // cur mod key key value + lua_rawset(L, -5); // cur[key] = value, cur mod key + } + } + for (const auto& pair : macroPairs) { + lua_getfield(L, -1, pair.first.c_str()); // mod[first], cur mod val + lua_setfield(L, -3, pair.second.c_str()); // cur[second] = val, cur mod + } + } +#else // YUE_NO_MACRO + for (auto item : tabLit->items.objects()) { + switch (item->getId()) { + case id(): + case id(): + case id(): { + throw std::logic_error(_info.errorMessage("macro feature not supported"sv, item)); + break; + } + case id(): + case id(): + newTab->items.push_back(item); + break; + default: YUEE("AST node mismatch", item); break; + } + } +#endif // YUE_NO_MACRO + if (newTab->items.empty()) { + out.push_back(Empty); + return; + } else { + import->target.set(newTab); + } + } + auto target = import->target.get(); + auto value = x->new_ptr(); + if (auto var = ast_cast(target)) { + auto callable = x->new_ptr(); + callable->item.set(var); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + value->item.set(chainValue); + } else { + auto tabLit = ast_to(target); + auto simpleValue = x->new_ptr(); + auto tableLit = x->new_ptr(); + tableLit->values.dup(tabLit->items); + simpleValue->value.set(tableLit); + value->item.set(simpleValue); + } + auto exp = newExp(value, x); + auto assignList = x->new_ptr(); + assignList->exprs.push_back(exp); + auto assign = x->new_ptr(); + assign->values.push_back(toAst(s("require "sv) + _parser.toString(import->literal), x)); + auto assignment = x->new_ptr(); + assignment->expList.set(assignList); + assignment->action.set(assign); + transformAssignment(assignment, out); + } + + void transformImport(Import_t* import, str_list& out) { + auto content = import->content.get(); + switch (content->getId()) { + case id(): + transformImportAs(static_cast(content), out); + break; + case id(): + transformImportFrom(static_cast(content), out); + break; + default: YUEE("AST node mismatch", content); break; + } + } + + void transformWhileInPlace(While_t* whileNode, str_list& out, ExpList_t* expList = nullptr) { + auto x = whileNode; + str_list temp; + if (expList) { + temp.push_back(indent() + s("do"sv) + nll(whileNode)); + } + pushScope(); + auto accumVar = getUnusedName("_accum_"sv); + addToScope(accumVar); + auto lenVar = getUnusedName("_len_"sv); + addToScope(lenVar); + temp.push_back(indent() + s("local "sv) + accumVar + s(" = { }"sv) + nll(whileNode)); + temp.push_back(indent() + s("local "sv) + lenVar + s(" = 1"sv) + nll(whileNode)); + transformExp(whileNode->condition, temp, ExpUsage::Closure); + temp.back() = indent() + s("while "sv) + temp.back() + s(" do"sv) + nll(whileNode); + pushScope(); + auto assignLeft = toAst(accumVar + s("["sv) + lenVar + s("]"sv), x); + auto lenLine = lenVar + s(" = "sv) + lenVar + s(" + 1"sv) + nlr(whileNode); + transformLoopBody(whileNode->body, temp, lenLine, ExpUsage::Assignment, assignLeft); + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(whileNode)); + if (expList) { + auto assign = x->new_ptr(); + assign->values.push_back(toAst(accumVar, x)); + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + assignment->action.set(assign); + transformAssignment(assignment, temp); + } else { + temp.push_back(indent() + s("return "sv) + accumVar + nlr(whileNode)); + } + popScope(); + if (expList) { + temp.push_back(indent() + s("end"sv) + nlr(whileNode)); + } + out.push_back(join(temp)); + } + + void transformWhileClosure(While_t* whileNode, str_list& out) { + auto x = whileNode; + str_list temp; + temp.push_back(s("(function() "sv) + nll(whileNode)); + pushScope(); + _enableReturn.push(true); + auto accumVar = getUnusedName("_accum_"sv); + addToScope(accumVar); + auto lenVar = getUnusedName("_len_"sv); + addToScope(lenVar); + temp.push_back(indent() + s("local "sv) + accumVar + s(" = { }"sv) + nll(whileNode)); + temp.push_back(indent() + s("local "sv) + lenVar + s(" = 1"sv) + nll(whileNode)); + transformExp(whileNode->condition, temp, ExpUsage::Closure); + temp.back() = indent() + s("while "sv) + temp.back() + s(" do"sv) + nll(whileNode); + pushScope(); + auto assignLeft = toAst(accumVar + s("["sv) + lenVar + s("]"sv), x); + auto lenLine = lenVar + s(" = "sv) + lenVar + s(" + 1"sv) + nlr(whileNode); + transformLoopBody(whileNode->body, temp, lenLine, ExpUsage::Assignment, assignLeft); + popScope(); + temp.push_back(indent() + s("end"sv) + nlr(whileNode)); + temp.push_back(indent() + s("return "sv) + accumVar + nlr(whileNode)); + _enableReturn.pop(); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + out.push_back(join(temp)); + } + + void transformWhile(While_t* whileNode, str_list& out) { + str_list temp; + pushScope(); + transformExp(whileNode->condition, temp, ExpUsage::Closure); + transformLoopBody(whileNode->body, temp, Empty, ExpUsage::Common); + popScope(); + _buf << indent() << "while "sv << temp.front() << " do"sv << nll(whileNode); + _buf << temp.back(); + _buf << indent() << "end"sv << nlr(whileNode); + out.push_back(clearBuf()); + } + + void transformRepeat(Repeat_t* repeat, str_list& out) { + str_list temp; + pushScope(); + transformLoopBody(repeat->body->content, temp, Empty, ExpUsage::Common); + transformExp(repeat->condition, temp, ExpUsage::Closure); + popScope(); + _buf << indent() << "repeat"sv << nll(repeat); + _buf << temp.front(); + _buf << indent() << "until "sv << temp.back() << nlr(repeat); + out.push_back(clearBuf()); + } + + void transformSwitch(Switch_t* switchNode, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) { + str_list temp; + if (usage == ExpUsage::Closure) { + temp.push_back(s("(function()"sv) + nll(switchNode)); + pushScope(); + _enableReturn.push(true); + } + auto objVar = singleVariableFrom(switchNode->target); + if (objVar.empty()) { + objVar = getUnusedName("_exp_"sv); + addToScope(objVar); + transformExp(switchNode->target, temp, ExpUsage::Closure); + _buf << indent() << "local "sv << objVar << " = "sv << temp.back() << nll(switchNode); + temp.back() = clearBuf(); + } + const auto& branches = switchNode->branches.objects(); + for (auto branch_ : branches) { + auto branch = static_cast(branch_); + temp.push_back(indent() + s(branches.front() == branch ? "if"sv : "elseif"sv)); + str_list tmp; + const auto& exprs = branch->valueList->exprs.objects(); + for (auto exp_ : exprs) { + auto exp = static_cast(exp_); + transformExp(exp, tmp, ExpUsage::Closure); + if (!singleValueFrom(exp)) { + tmp.back() = s("("sv) + tmp.back() + s(")"sv); + } + temp.back().append(s(" "sv) + tmp.back() + s(" == "sv) + objVar + + s(exp == exprs.back() ? ""sv : " or"sv)); + } + temp.back().append(s(" then"sv) + nll(branch)); + pushScope(); + transform_plain_body(branch->body, temp, usage, assignList); + popScope(); + } + if (switchNode->lastBranch) { + temp.push_back(indent() + s("else"sv) + nll(switchNode->lastBranch)); + pushScope(); + transform_plain_body(switchNode->lastBranch, temp, usage, assignList); + popScope(); + } + temp.push_back(indent() + s("end"sv) + nlr(switchNode)); + if (usage == ExpUsage::Closure) { + _enableReturn.pop(); + popScope(); + temp.push_back(indent() + s("end)()"sv)); + } + out.push_back(join(temp)); + } + + void transformLocalDef(Local_t* local, str_list& out) { + if (!local->forceDecls.empty() || !local->decls.empty()) { + str_list defs; + for (const auto& decl : local->forceDecls) { + forceAddToScope(decl); + defs.push_back(decl); + } + for (const auto& decl : local->decls) { + if (addToScope(decl)) { + defs.push_back(decl); + } + } + auto preDefine = getPredefine(defs); + if (!preDefine.empty()) { + out.push_back(preDefine + nll(local)); + } + } + } + + void transformLocal(Local_t* local, str_list& out) { + str_list temp; + if (!local->defined) { + local->defined = true; + transformLocalDef(local, temp); + } + if (auto values = local->item.as()) { + if (values->valueList) { + auto x = local; + auto expList = x->new_ptr(); + for (auto name : values->nameList->names.objects()) { + auto callable = x->new_ptr(); + callable->item.set(name); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = newExp(value, x); + expList->exprs.push_back(exp); + } + auto assignment = x->new_ptr(); + assignment->expList.set(expList); + auto assign = x->new_ptr(); + if (auto expListLow = values->valueList.as()) { + assign->values.dup(expListLow->exprs); + } else { + auto tableBlock = values->valueList.to(); + assign->values.push_back(tableBlock); + } + assignment->action.set(assign); + transformAssignment(assignment, temp); + } + } + out.push_back(join(temp)); + } + + void transformLocalAttrib(LocalAttrib_t* localAttrib, str_list& out) { + auto x = localAttrib; + auto attrib = _parser.toString(localAttrib->attrib); + if (attrib != "close"sv && attrib != "const"sv) { + throw std::logic_error(_info.errorMessage(s("unknown attribute '"sv) + attrib + '\'', localAttrib->attrib)); + } + auto expList = x->new_ptr(); + str_list tmpVars; + str_list vars; + pushScope(); + for (auto name : localAttrib->nameList->names.objects()) { + auto callable = x->new_ptr(); + callable->item.set(name); + auto chainValue = x->new_ptr(); + chainValue->items.push_back(callable); + auto value = x->new_ptr(); + value->item.set(chainValue); + auto exp = newExp(value, x); + expList->exprs.push_back(exp); + tmpVars.push_back(getUnusedName("_var_"sv)); + addToScope(tmpVars.back()); + vars.push_back(_parser.toString(name)); + } + popScope(); + auto tmpVarStr = join(tmpVars, ", "sv); + auto tmpVarList = toAst(tmpVarStr, x); + auto assignment = x->new_ptr(); + assignment->expList.set(tmpVarList); + assignment->action.set(localAttrib->assign); + str_list temp; + transformAssignment(assignment, temp); + attrib = s(" <"sv) + attrib + '>'; + for (auto& var : vars) { + forceAddToScope(var); + var.append(attrib); + } + temp.push_back(indent() + s("local "sv) + join(vars, ", "sv) + s(" = "sv) + tmpVarStr + nll(x)); + out.push_back(join(temp)); + } + + void transformBreakLoop(BreakLoop_t* breakLoop, str_list& out) { + auto keyword = _parser.toString(breakLoop); + if (keyword == "break"sv) { + out.push_back(indent() + keyword + nll(breakLoop)); + return; + } + if (_continueVars.empty()) throw std::logic_error(_info.errorMessage("continue is not inside a loop"sv, breakLoop)); + _buf << indent() << _continueVars.top() << " = true"sv << nll(breakLoop); + _buf << indent() << "break"sv << nll(breakLoop); + out.push_back(clearBuf()); + } + + void transformLabel(Label_t* label, str_list& out) { + out.push_back(indent() + s("::"sv) + _parser.toString(label->label) + s("::"sv) + nll(label)); + } + + void transformGoto(Goto_t* gotoNode, str_list& out) { + out.push_back(indent() + s("goto "sv) + _parser.toString(gotoNode->label) + nll(gotoNode)); + } +}; + +const std::string YueCompilerImpl::Empty; + +YueCompiler::YueCompiler(void* sharedState, + const std::function& luaOpen, + bool sameModule): +#ifndef YUE_NO_MACRO +_compiler(std::make_unique(static_cast(sharedState), luaOpen, sameModule)) {} +#else +_compiler(std::make_unique()) { + (void)sharedState; + (void)luaOpen; + (void)sameModule; +} +#endif // YUE_NO_MACRO + +YueCompiler::~YueCompiler() {} + +CompileInfo YueCompiler::compile(std::string_view codes, const YueConfig& config) { + return _compiler->compile(codes, config); +} + +} // namespace yue diff --git a/src/yuescript/yue_compiler.h b/src/yuescript/yue_compiler.h new file mode 100644 index 0000000..2e8c86a --- /dev/null +++ b/src/yuescript/yue_compiler.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2021 Jin Li, http://www.luvfight.me + +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. */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace yue { + +extern const std::string_view version; +extern const std::string_view extension; + +using Options = std::unordered_map; + +struct YueConfig { + bool lintGlobalVariable = false; + bool implicitReturnRoot = true; + bool reserveLineNumber = true; + bool useSpaceOverTab = false; + int lineOffset = 0; + Options options; +}; + +struct GlobalVar { + std::string name; + int line; + int col; +}; + +using GlobalVars = std::list; + +struct CompileInfo { + std::string codes; + std::string error; + std::unique_ptr globals; + std::unique_ptr options; +}; + +class YueCompilerImpl; + +class YueCompiler { +public: + YueCompiler(void* luaState = nullptr, + const std::function& luaOpen = nullptr, + bool sameModule = false); + virtual ~YueCompiler(); + CompileInfo compile(std::string_view codes, const YueConfig& config = {}); +private: + std::unique_ptr _compiler; +}; + +} // namespace yue diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp new file mode 100644 index 0000000..cf3da01 --- /dev/null +++ b/src/yuescript/yue_parser.cpp @@ -0,0 +1,719 @@ +/* Copyright (c) 2021 Jin Li, http://www.luvfight.me + +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. */ + +#include "yuescript/yue_parser.h" + +namespace pl = parserlib; + +namespace yue { +using namespace std::string_view_literals; + +std::unordered_set LuaKeywords = { + "and", "break", "do", "else", "elseif", + "end", "false", "for", "function", "goto", + "if", "in", "local", "nil", "not", + "or", "repeat", "return", "then", "true", + "until", "while" +}; + +std::unordered_set Keywords = { + "and", "break", "do", "else", "elseif", + "end", "false", "for", "function", "goto", + "if", "in", "local", "nil", "not", + "or", "repeat", "return", "then", "true", + "until", "while", // Lua keywords + "as", "class", "continue", "export", "extends", + "from", "global", "import", "macro", "switch", + "unless", "using", "when", "with" // Yue keywords +}; + +YueParser::YueParser() { + plain_space = *set(" \t"); + Break = nl(-expr('\r') >> '\n'); + Any = Break | any(); + Stop = Break | eof(); + Indent = plain_space; + Comment = "--" >> *(not_(set("\r\n")) >> Any) >> and_(Stop); + multi_line_open = expr("--[["); + multi_line_close = expr("]]"); + multi_line_content = *(not_(multi_line_close) >> Any); + MultiLineComment = multi_line_open >> multi_line_content >> multi_line_close; + EscapeNewLine = expr('\\') >> *(set(" \t") | MultiLineComment) >> -Comment >> Break; + space_one = set(" \t") | and_(set("-\\")) >> (MultiLineComment | EscapeNewLine); + Space = *space_one >> -Comment; + SpaceBreak = Space >> Break; + White = Space >> *(Break >> Space); + EmptyLine = SpaceBreak; + AlphaNum = range('a', 'z') | range('A', 'Z') | range('0', '9') | '_'; + Name = (range('a', 'z') | range('A', 'Z') | '_') >> *AlphaNum; + Num = ( + "0x" >> + +(range('0', '9') | range('a', 'f') | range('A', 'F')) >> + -(-set("uU") >> set("lL") >> set("lL")) + ) | ( + +range('0', '9') >> -set("uU") >> set("lL") >> set("lL") + ) | ( + ( + +range('0', '9') >> -('.' >> +range('0', '9')) + ) | ( + '.' >> +range('0', '9') + ) + ) >> -(set("eE") >> -expr('-') >> +range('0', '9')); + + Cut = false_(); + Seperator = true_(); + + #define sym(str) (Space >> str) + #define symx(str) expr(str) + #define ensure(patt, finally) ((patt) >> (finally) | (finally) >> Cut) + #define key(str) (Space >> str >> not_(AlphaNum)) + #define disable_do(patt) (DisableDo >> ((patt) >> EnableDo | EnableDo >> Cut)) + #define disable_chain(patt) (DisableChain >> ((patt) >> EnableChain | EnableChain >> Cut)) + #define disable_do_chain(patt) (DisableDoChain >> ((patt) >> EnableDoChain | EnableDoChain >> Cut)) + #define plain_body_with(str) (-key(str) >> InBlock | key(str) >> Statement) + #define plain_body (InBlock | Statement) + + Variable = pl::user(Name, [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + for (auto it = item.begin; it != item.end; ++it) st->buffer += static_cast(*it); + auto isValid = Keywords.find(st->buffer) == Keywords.end(); + if (isValid) { + if (st->buffer == st->moduleName) { + st->moduleFix++; + st->moduleName = std::string("_module_"sv) + std::to_string(st->moduleFix); + } + } + st->buffer.clear(); + return isValid; + }); + + LabelName = pl::user(Name, [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + for (auto it = item.begin; it != item.end; ++it) st->buffer += static_cast(*it); + auto isValid = LuaKeywords.find(st->buffer) == LuaKeywords.end(); + st->buffer.clear(); + return isValid; + }); + + LuaKeyword = pl::user(Name, [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + for (auto it = item.begin; it != item.end; ++it) st->buffer += static_cast(*it); + auto it = LuaKeywords.find(st->buffer); + st->buffer.clear(); + return it != LuaKeywords.end(); + }); + + self = expr('@'); + self_name = '@' >> Name; + self_class = expr("@@"); + self_class_name = "@@" >> Name; + + SelfName = self_class_name | self_class | self_name | self; + KeyName = Space >> (SelfName | Name); + VarArg = expr("..."); + + check_indent = pl::user(Indent, [](const item_t& item) { + int indent = 0; + for (input_it i = item.begin; i != item.end; ++i) { + switch (*i) { + case ' ': indent++; break; + case '\t': indent += 4; break; + } + } + State* st = reinterpret_cast(item.user_data); + return st->indents.top() == indent; + }); + CheckIndent = and_(check_indent); + + advance = pl::user(Indent, [](const item_t& item) { + int indent = 0; + for (input_it i = item.begin; i != item.end; ++i) { + switch (*i) { + case ' ': indent++; break; + case '\t': indent += 4; break; + } + } + State* st = reinterpret_cast(item.user_data); + int top = st->indents.top(); + if (top != -1 && indent > top) { + st->indents.push(indent); + return true; + } + return false; + }); + Advance = and_(advance); + + push_indent = pl::user(Indent, [](const item_t& item) { + int indent = 0; + for (input_it i = item.begin; i != item.end; ++i) { + switch (*i) { + case ' ': indent++; break; + case '\t': indent += 4; break; + } + } + State* st = reinterpret_cast(item.user_data); + st->indents.push(indent); + return true; + }); + PushIndent = and_(push_indent); + + PreventIndent = pl::user(true_(), [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + st->indents.push(-1); + return true; + }); + + PopIndent = pl::user(true_(), [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + st->indents.pop(); + return true; + }); + + InBlock = +SpaceBreak >> Advance >> ensure(Block, PopIndent); + + local_flag = expr('*') | expr('^'); + local_values = NameList >> -(sym('=') >> (TableBlock | ExpListLow)); + Local = key("local") >> (Space >> local_flag | local_values); + + LocalAttrib = and_(key(pl::user(Name, [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + for (auto it = item.begin; it != item.end; ++it) st->buffer += static_cast(*it); + auto it = Keywords.find(st->buffer); + st->buffer.clear(); + return it == Keywords.end(); + })) >> NameList >> sym('=') >> not_('=')) >> Space >> Name >> NameList >> Assign; + + colon_import_name = sym('\\') >> Space >> Variable; + ImportName = colon_import_name | Space >> Variable; + ImportNameList = Seperator >> *SpaceBreak >> ImportName >> *((+SpaceBreak | sym(',') >> *SpaceBreak) >> ImportName); + ImportFrom = ImportNameList >> *SpaceBreak >> key("from") >> Exp; + + import_literal_inner = (range('a', 'z') | range('A', 'Z') | set("_-")) >> *(AlphaNum | '-'); + import_literal_chain = Seperator >> import_literal_inner >> *(expr('.') >> import_literal_inner); + ImportLiteral = sym('\'') >> import_literal_chain >> symx('\'') | sym('"') >> import_literal_chain >> symx('"'); + + macro_name_pair = Space >> MacroName >> Space >> symx(':') >> Space >> MacroName; + import_all_macro = expr('$'); + ImportTabItem = variable_pair | normal_pair | sym(':') >> MacroName | macro_name_pair | Space >> import_all_macro; + ImportTabList = ImportTabItem >> *(sym(',') >> ImportTabItem); + ImportTabLine = ( + PushIndent >> (ImportTabList >> PopIndent | PopIndent) + ) | Space; + import_tab_lines = SpaceBreak >> ImportTabLine >> *(-sym(',') >> SpaceBreak >> ImportTabLine) >> -sym(','); + ImportTabLit = + sym('{') >> Seperator >> + -ImportTabList >> + -sym(',') >> + -import_tab_lines >> + White >> sym('}'); + + ImportAs = ImportLiteral >> -(key("as") >> (Space >> Variable | ImportTabLit)); + + Import = key("import") >> (ImportAs | ImportFrom); + + Label = Space >> expr("::") >> LabelName >> expr("::"); + + Goto = key("goto") >> Space >> LabelName; + + BreakLoop = (expr("break") | expr("continue")) >> not_(AlphaNum); + + Return = key("return") >> -ExpListLow; + + WithExp = ExpList >> -Assign; + + With = key("with") >> -existential_op >> disable_do_chain(WithExp) >> plain_body_with("do"); + SwitchCase = key("when") >> disable_chain(ExpList) >> plain_body_with("then"); + SwitchElse = key("else") >> plain_body; + + SwitchBlock = *EmptyLine >> + Advance >> Seperator >> + SwitchCase >> + *(+SpaceBreak >> SwitchCase) >> + -(+SpaceBreak >> SwitchElse) >> + PopIndent; + + Switch = key("switch") >> disable_do(Exp) >> -key("do") + >> -Space >> Break >> SwitchBlock; + + IfCond = disable_chain(Exp >> -Assign); + IfElseIf = -(Break >> *EmptyLine >> CheckIndent) >> key("elseif") >> IfCond >> plain_body_with("then"); + IfElse = -(Break >> *EmptyLine >> CheckIndent) >> key("else") >> plain_body; + If = key("if") >> Seperator >> IfCond >> plain_body_with("then") >> *IfElseIf >> -IfElse; + Unless = key("unless") >> Seperator >> IfCond >> plain_body_with("then") >> *IfElseIf >> -IfElse; + + While = key("while") >> disable_do_chain(Exp) >> plain_body_with("do"); + Repeat = key("repeat") >> Body >> Break >> *EmptyLine >> CheckIndent >> key("until") >> Exp; + + for_step_value = sym(',') >> Exp; + for_args = Space >> Variable >> sym('=') >> Exp >> sym(',') >> Exp >> -for_step_value; + + For = key("for") >> disable_do_chain(for_args) >> plain_body_with("do"); + + for_in = star_exp | ExpList; + + ForEach = key("for") >> AssignableNameList >> key("in") >> + disable_do_chain(for_in) >> plain_body_with("do"); + + Do = pl::user(key("do"), [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + return st->doStack.empty() || st->doStack.top(); + }) >> Body; + + DisableDo = pl::user(true_(), [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + st->doStack.push(false); + return true; + }); + + EnableDo = pl::user(true_(), [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + st->doStack.pop(); + return true; + }); + + DisableDoChain = pl::user(true_(), [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + st->doStack.push(false); + st->chainBlockStack.push(false); + return true; + }); + + EnableDoChain = pl::user(true_(), [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + st->doStack.pop(); + st->chainBlockStack.pop(); + return true; + }); + + Comprehension = sym('[') >> Exp >> CompInner >> sym(']'); + comp_value = sym(',') >> Exp; + TblComprehension = sym('{') >> Exp >> -comp_value >> CompInner >> sym('}'); + + CompInner = Seperator >> (CompForEach | CompFor) >> *CompClause; + star_exp = sym('*') >> Exp; + CompForEach = key("for") >> AssignableNameList >> key("in") >> (star_exp | Exp); + CompFor = key("for") >> Space >> Variable >> sym('=') >> Exp >> sym(',') >> Exp >> -for_step_value; + CompClause = CompFor | CompForEach | key("when") >> Exp; + + Assign = sym('=') >> Seperator >> (With | If | Switch | TableBlock | Exp >> *(Space >> set(",;") >> Exp)); + + update_op = + expr("..") | + expr("+") | + expr("-") | + expr("*") | + expr("/") | + expr("%") | + expr("or") | + expr("and") | + expr("&") | + expr("|") | + expr(">>") | + expr("<<"); + + Update = Space >> update_op >> expr("=") >> Exp; + + Assignable = AssignableChain | Space >> Variable | Space >> SelfName; + + unary_value = unary_operator >> *(Space >> unary_operator) >> Value; + + ExponentialOperator = expr('^'); + expo_value = Space >> ExponentialOperator >> *SpaceBreak >> Value; + expo_exp = Value >> *expo_value; + + unary_operator = + expr('-') >> not_(set(">=") | space_one) | + expr('#') | + expr('~') >> not_(expr('=') | space_one) | + expr("not") >> not_(AlphaNum); + unary_exp = *(Space >> unary_operator) >> expo_exp; + + BackcallOperator = expr("|>"); + backcall_value = Space >> BackcallOperator >> *SpaceBreak >> unary_exp; + backcall_exp = unary_exp >> *backcall_value; + + BinaryOperator = + (expr("or") >> not_(AlphaNum)) | + (expr("and") >> not_(AlphaNum)) | + expr("<=") | + expr(">=") | + expr("~=") | + expr("!=") | + expr("==") | + expr("..") | + expr("<<") | + expr(">>") | + expr("//") | + set("+-*/%><|&~"); + exp_op_value = Space >> BinaryOperator >> *SpaceBreak >> backcall_exp; + Exp = Seperator >> backcall_exp >> *exp_op_value; + + DisableChain = pl::user(true_(), [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + st->chainBlockStack.push(false); + return true; + }); + + EnableChain = pl::user(true_(), [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + st->chainBlockStack.pop(); + return true; + }); + + chain_line = CheckIndent >> (chain_item | Space >> (chain_dot_chain | ColonChain)) >> -InvokeArgs; + chain_block = pl::user(true_(), [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + return st->chainBlockStack.empty() || st->chainBlockStack.top(); + }) >> +SpaceBreak >> Advance >> ensure( + chain_line >> *(+SpaceBreak >> chain_line), PopIndent); + ChainValue = Seperator >> (Chain | Callable) >> -existential_op >> -(InvokeArgs | chain_block); + + simple_table = Seperator >> KeyValue >> *(sym(',') >> KeyValue); + Value = SimpleValue | simple_table | ChainValue | String; + + single_string_inner = expr("\\'") | "\\\\" | not_(expr('\'')) >> Any; + SingleString = symx('\'') >> *single_string_inner >> symx('\''); + interp = symx("#{") >> Exp >> sym('}'); + double_string_plain = expr("\\\"") | "\\\\" | not_(expr('"')) >> Any; + double_string_inner = +(not_(interp) >> double_string_plain); + double_string_content = double_string_inner | interp; + DoubleString = symx('"') >> Seperator >> *double_string_content >> symx('"'); + String = Space >> (DoubleString | SingleString | LuaString); + + lua_string_open = '[' >> *expr('=') >> '['; + lua_string_close = ']' >> *expr('=') >> ']'; + + LuaStringOpen = pl::user(lua_string_open, [](const item_t& item) { + size_t count = std::distance(item.begin, item.end); + State* st = reinterpret_cast(item.user_data); + st->stringOpen = count; + return true; + }); + + LuaStringClose = pl::user(lua_string_close, [](const item_t& item) { + size_t count = std::distance(item.begin, item.end); + State* st = reinterpret_cast(item.user_data); + return st->stringOpen == count; + }); + + LuaStringContent = *(not_(LuaStringClose) >> Any); + + LuaString = LuaStringOpen >> -Break >> LuaStringContent >> LuaStringClose; + + Parens = symx('(') >> *SpaceBreak >> Exp >> *SpaceBreak >> sym(')'); + Callable = Space >> (Variable | SelfName | MacroName | VarArg | Parens); + FnArgsExpList = Exp >> *((Break | sym(',')) >> White >> Exp); + + FnArgs = (symx('(') >> *SpaceBreak >> -FnArgsExpList >> *SpaceBreak >> sym(')')) | + (sym('!') >> not_(expr('='))); + + existential_op = expr('?'); + chain_call = (Callable | String) >> -existential_op >> ChainItems; + chain_item = and_(set(".\\")) >> ChainItems; + chain_dot_chain = DotChainItem >> -existential_op >> -ChainItems; + + Chain = chain_call | chain_item | + Space >> (chain_dot_chain | ColonChain); + + AssignableChain = Seperator >> Chain; + + chain_with_colon = +ChainItem >> -ColonChain; + ChainItems = chain_with_colon | ColonChain; + + Index = symx('[') >> Exp >> sym(']'); + ChainItem = Invoke >> -existential_op | DotChainItem >> -existential_op | Slice | Index >> -existential_op; + DotChainItem = symx('.') >> Name; + ColonChainItem = symx('\\') >> (LuaKeyword | Name); + invoke_chain = Invoke >> -existential_op >> -ChainItems; + ColonChain = ColonChainItem >> -existential_op >> -invoke_chain; + + default_value = true_(); + Slice = + symx('[') >> + (Exp | default_value) >> + sym(',') >> + (Exp | default_value) >> + (sym(',') >> Exp | default_value) >> + sym(']'); + + Invoke = Seperator >> ( + FnArgs | + SingleString | + DoubleString | + and_(expr('[')) >> LuaString | + and_(expr('{')) >> TableLit); + + TableValue = KeyValue | Exp; + + table_lit_lines = SpaceBreak >> TableLitLine >> *(-sym(',') >> SpaceBreak >> TableLitLine) >> -sym(','); + + TableLit = + sym('{') >> Seperator >> + -TableValueList >> + -sym(',') >> + -table_lit_lines >> + White >> sym('}'); + + TableValueList = TableValue >> *(sym(',') >> TableValue); + + TableLitLine = ( + PushIndent >> (TableValueList >> PopIndent | PopIndent) + ) | ( + Space + ); + + TableBlockInner = Seperator >> KeyValueLine >> *(+SpaceBreak >> KeyValueLine); + TableBlock = +SpaceBreak >> Advance >> ensure(TableBlockInner, PopIndent); + TableBlockIndent = sym('*') >> Seperator >> KeyValueList >> -sym(',') >> + -(+SpaceBreak >> Advance >> ensure(KeyValueList >> -sym(',') >> *(+SpaceBreak >> KeyValueLine), PopIndent)); + + class_member_list = Seperator >> KeyValue >> *(sym(',') >> KeyValue); + ClassLine = CheckIndent >> (class_member_list | Statement) >> -sym(','); + ClassBlock = +SpaceBreak >> Advance >> Seperator >> ClassLine >> *(+SpaceBreak >> ClassLine) >> PopIndent; + + ClassDecl = + key("class") >> not_(expr(':')) >> + -Assignable >> + -(key("extends") >> PreventIndent >> ensure(Exp, PopIndent)) >> + -ClassBlock; + + global_values = NameList >> -(sym('=') >> (TableBlock | ExpListLow)); + global_op = expr('*') | expr('^'); + Global = key("global") >> (ClassDecl | (Space >> global_op) | global_values); + + export_default = key("default"); + + Export = pl::user(key("export"), [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + st->exportCount++; + return true; + }) >> ((pl::user(export_default, [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + bool isValid = !st->exportDefault && st->exportCount == 1; + st->exportDefault = true; + return isValid; + }) >> Exp) + | (pl::user(true_(), [](const item_t& item) { + State* st = reinterpret_cast(item.user_data); + if (st->exportDefault && st->exportCount > 1) { + return false; + } else { + return true; + } + }) >> ExpList >> -Assign) + | Macro) >> not_(Space >> statement_appendix); + + variable_pair = sym(':') >> Variable; + + normal_pair = ( + KeyName | + sym('[') >> Exp >> sym(']') | + Space >> DoubleString | + Space >> SingleString | + Space >> LuaString + ) >> + symx(':') >> + (Exp | TableBlock | +(SpaceBreak) >> Exp); + + KeyValue = variable_pair | normal_pair; + + KeyValueList = KeyValue >> *(sym(',') >> KeyValue); + KeyValueLine = CheckIndent >> (KeyValueList >> -sym(',') | TableBlockIndent | Space >> expr('*') >> (Exp | TableBlock)); + + FnArgDef = (Variable | SelfName) >> -(sym('=') >> Space >> Exp); + + FnArgDefList = Space >> Seperator >> ( + ( + FnArgDef >> + *((sym(',') | Break) >> White >> FnArgDef) >> + -((sym(',') | Break) >> White >> VarArg) + ) | ( + VarArg + ) + ); + + outer_var_shadow = key("using") >> (NameList | Space >> expr("nil")); + + FnArgsDef = sym('(') >> White >> -FnArgDefList >> -outer_var_shadow >> White >> sym(')'); + fn_arrow = expr("->") | expr("=>"); + FunLit = -FnArgsDef >> Space >> fn_arrow >> -Body; + + MacroName = expr('$') >> -Name; + macro_args_def = sym('(') >> White >> -FnArgDefList >> White >> sym(')'); + MacroLit = -macro_args_def >> Space >> expr("->") >> Body; + Macro = key("macro") >> Space >> Name >> sym('=') >> MacroLit; + + NameList = Seperator >> Space >> Variable >> *(sym(',') >> Space >> Variable); + NameOrDestructure = Space >> Variable | TableLit; + AssignableNameList = Seperator >> NameOrDestructure >> *(sym(',') >> NameOrDestructure); + + fn_arrow_back = expr('<') >> set("-="); + Backcall = -FnArgsDef >> Space >> fn_arrow_back >> Space >> ChainValue; + + BackcallBody = Seperator >> Space >> BackcallOperator >> unary_exp >> *(+SpaceBreak >> CheckIndent >> Space >> BackcallOperator >> unary_exp); + + ExpList = Seperator >> Exp >> *(sym(',') >> Exp); + ExpListLow = Seperator >> Exp >> *(Space >> set(",;") >> Exp); + + ArgLine = CheckIndent >> Exp >> *(sym(',') >> Exp); + ArgBlock = ArgLine >> *(sym(',') >> SpaceBreak >> ArgLine) >> PopIndent; + + invoke_args_with_table = + sym(',') >> + ( + TableBlock | + SpaceBreak >> Advance >> ArgBlock >> -TableBlock + ); + + InvokeArgs = + not_(set("-~")) >> Seperator >> + ( + Exp >> *(sym(',') >> Exp) >> -(invoke_args_with_table | TableBlock) | + TableBlock + ); + + const_value = (expr("nil") | expr("true") | expr("false")) >> not_(AlphaNum); + + SimpleValue = + (Space >> const_value) | + If | Unless | Switch | With | ClassDecl | ForEach | For | While | Do | + (Space >> unary_value) | + TblComprehension | TableLit | Comprehension | FunLit | + (Space >> Num); + + ExpListAssign = ExpList >> -(Update | Assign); + + if_line = key("if") >> Exp >> -Assign; + unless_line = key("unless") >> Exp; + + statement_appendix = (if_line | unless_line | CompInner) >> Space; + statement_sep = and_(*SpaceBreak >> CheckIndent >> Space >> (set("($'\"") | expr("[[") | expr("[="))); + Statement = ( + Import | While | Repeat | For | ForEach | + Return | Local | Global | Export | Macro | + Space >> BreakLoop | Label | Goto | Backcall | + LocalAttrib | BackcallBody | ExpListAssign + ) >> Space >> + -statement_appendix >> -statement_sep; + + Body = InBlock | Statement; + + empty_line_stop = Space >> and_(Stop); + Line = and_(check_indent >> Space >> not_(BackcallOperator)) >> Statement | Advance >> ensure(and_(Space >> BackcallOperator) >> Statement, PopIndent) | empty_line_stop; + Block = Seperator >> Line >> *(+Break >> Line); + + Shebang = expr("#!") >> *(not_(Stop) >> Any); + File = White >> -Shebang >> Block >> eof(); +} + +ParseInfo YueParser::parse(std::string_view codes, rule& r) { + ParseInfo res; + try { + res.codes = std::make_unique(); + *(res.codes) = _converter.from_bytes(&codes.front(), &codes.back() + 1); + } catch (const std::range_error&) { + res.error = "Invalid text encoding."sv; + return res; + } + error_list errors; + try { + State state; + res.node.set(pl::parse(*(res.codes), r, errors, &state)); + if (state.exportCount > 0) { + res.moduleName = std::move(state.moduleName); + res.exportDefault = state.exportDefault; + } + } catch (const std::logic_error& err) { + res.error = err.what(); + return res; + } + if (!errors.empty()) { + std::ostringstream buf; + for (error_list::iterator it = errors.begin(); it != errors.end(); ++it) { + const error& err = *it; + switch (err.m_type) { + case ERROR_TYPE::ERROR_SYNTAX_ERROR: + buf << res.errorMessage("syntax error"sv, &err); + break; + case ERROR_TYPE::ERROR_INVALID_EOF: + buf << res.errorMessage("invalid EOF"sv, &err); + break; + } + } + res.error = buf.str(); + } + return res; +} + +std::string YueParser::toString(ast_node* node) { + return _converter.to_bytes(std::wstring(node->m_begin.m_it, node->m_end.m_it)); +} + +std::string YueParser::toString(input::iterator begin, input::iterator end) { + return _converter.to_bytes(std::wstring(begin, end)); +} + +input YueParser::encode(std::string_view codes) { + return _converter.from_bytes(&codes.front(), &codes.back() + 1); +} + +std::string YueParser::decode(const input& codes) { + return _converter.to_bytes(codes); +} + +namespace Utils { + void replace(std::string& str, std::string_view from, std::string_view to) { + size_t start_pos = 0; + while((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.size(), to); + start_pos += to.size(); + } + } + + void trim(std::string& str) { + if (str.empty()) return; + str.erase(0, str.find_first_not_of(" \t\n")); + str.erase(str.find_last_not_of(" \t\n") + 1); + } +} + +std::string ParseInfo::errorMessage(std::string_view msg, const input_range* loc) const { + const int ASCII = 255; + int length = loc->m_begin.m_line; + auto begin = codes->begin(); + auto end = codes->end(); + int count = 0; + for (auto it = codes->begin(); it != codes->end(); ++it) { + if (*it == '\n') { + if (count + 1 == length) { + end = it; + break; + } else { + begin = it + 1; + } + count++; + } + } + int oldCol = loc->m_begin.m_col; + int col = std::max(0, oldCol - 1); + auto it = begin; + for (int i = 0; i < oldCol && it != end; ++i) { + if (*it > ASCII) { + ++col; + } + ++it; + } + auto line = Converter{}.to_bytes(std::wstring(begin, end)); + Utils::replace(line, "\t"sv, " "sv); + std::ostringstream buf; + buf << loc->m_begin.m_line << ": "sv << msg << + '\n' << line << '\n' << std::string(col, ' ') << "^"sv; + return buf.str(); +} + +} // namespace yue diff --git a/src/yuescript/yue_parser.h b/src/yuescript/yue_parser.h new file mode 100644 index 0000000..f9a0d54 --- /dev/null +++ b/src/yuescript/yue_parser.h @@ -0,0 +1,317 @@ +/* Copyright (c) 2021 Jin Li, http://www.luvfight.me + +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. */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "yuescript/ast.hpp" +#include "yuescript/yue_ast.h" + +namespace yue { +using namespace parserlib; + +struct ParseInfo { + ast_ptr node; + std::string error; + std::unique_ptr codes; + bool exportDefault = false; + std::string moduleName; + std::string errorMessage(std::string_view msg, const input_range* loc) const; +}; + +template +struct identity { typedef T type; }; + +#define AST_RULE(type) \ + rule type; \ + ast type##_impl = type; \ + inline rule& getRule(identity) { return type; } + +extern std::unordered_set LuaKeywords; +extern std::unordered_set Keywords; + +class YueParser { +public: + YueParser(); + + template + ParseInfo parse(std::string_view codes) { + return parse(codes, getRule()); + } + + template + bool match(std::string_view codes) { + auto rEnd = rule(getRule() >> eof()); + return parse(codes, rEnd).node; + } + + std::string toString(ast_node* node); + std::string toString(input::iterator begin, input::iterator end); + + input encode(std::string_view input); + std::string decode(const input& input); + +protected: + ParseInfo parse(std::string_view codes, rule& r); + + struct State { + State() { + indents.push(0); + } + bool exportDefault = false; + int exportCount = 0; + int moduleFix = 0; + size_t stringOpen = 0; + std::string moduleName = "_module_0"; + std::string buffer; + std::stack indents; + std::stack doStack; + std::stack chainBlockStack; + }; + + template + inline rule& getRule() { + return getRule(identity()); + } + +private: + Converter _converter; + + template + inline rule& getRule(identity) { + assert(false); + return Cut; + } + + rule plain_space; + rule Break; + rule Any; + rule White; + rule Stop; + rule Comment; + rule multi_line_open; + rule multi_line_close; + rule multi_line_content; + rule MultiLineComment; + rule Indent; + rule EscapeNewLine; + rule space_one; + rule Space; + rule SpaceBreak; + rule EmptyLine; + rule AlphaNum; + rule Cut; + rule check_indent; + rule CheckIndent; + rule advance; + rule Advance; + rule push_indent; + rule PushIndent; + rule PreventIndent; + rule PopIndent; + rule InBlock; + rule ImportName; + rule ImportNameList; + rule import_literal_chain; + rule ImportTabItem; + rule ImportTabList; + rule ImportTabLine; + rule import_tab_lines; + rule WithExp; + rule DisableDo; + rule EnableDo; + rule DisableChain; + rule EnableChain; + rule DisableDoChain; + rule EnableDoChain; + rule SwitchElse; + rule SwitchBlock; + rule IfElseIf; + rule IfElse; + rule for_args; + rule for_in; + rule CompClause; + rule Chain; + rule KeyValue; + rule single_string_inner; + rule interp; + rule double_string_plain; + rule lua_string_open; + rule lua_string_close; + rule FnArgsExpList; + rule FnArgs; + rule macro_args_def; + rule chain_call; + rule chain_item; + rule ChainItems; + rule chain_dot_chain; + rule ColonChain; + rule chain_with_colon; + rule ChainItem; + rule chain_line; + rule chain_block; + rule Index; + rule invoke_chain; + rule TableValue; + rule table_lit_lines; + rule TableLitLine; + rule TableValueList; + rule TableBlockInner; + rule ClassLine; + rule KeyValueLine; + rule KeyValueList; + rule ArgLine; + rule ArgBlock; + rule invoke_args_with_table; + rule BackcallOperator; + rule ExponentialOperator; + rule backcall_value; + rule backcall_exp; + rule expo_value; + rule expo_exp; + rule empty_line_stop; + rule Line; + rule Shebang; + + AST_RULE(Num) + AST_RULE(Name) + AST_RULE(Variable) + AST_RULE(LabelName) + AST_RULE(LuaKeyword) + AST_RULE(self) + AST_RULE(self_name) + AST_RULE(self_class) + AST_RULE(self_class_name) + AST_RULE(SelfName) + AST_RULE(KeyName) + AST_RULE(VarArg) + AST_RULE(Seperator) + AST_RULE(NameList) + AST_RULE(local_flag) + AST_RULE(local_values) + AST_RULE(Local) + AST_RULE(LocalAttrib); + AST_RULE(colon_import_name) + AST_RULE(import_literal_inner) + AST_RULE(ImportLiteral) + AST_RULE(ImportFrom) + AST_RULE(macro_name_pair) + AST_RULE(import_all_macro) + AST_RULE(ImportTabLit) + AST_RULE(ImportAs) + AST_RULE(Import) + AST_RULE(Label) + AST_RULE(Goto) + AST_RULE(fn_arrow_back) + AST_RULE(Backcall) + AST_RULE(BackcallBody) + AST_RULE(ExpListLow) + AST_RULE(ExpList) + AST_RULE(Return) + AST_RULE(With) + AST_RULE(SwitchCase) + AST_RULE(Switch) + AST_RULE(IfCond) + AST_RULE(If) + AST_RULE(Unless) + AST_RULE(While) + AST_RULE(Repeat) + AST_RULE(for_step_value) + AST_RULE(For) + AST_RULE(ForEach) + AST_RULE(Do) + AST_RULE(Comprehension) + AST_RULE(comp_value) + AST_RULE(TblComprehension) + AST_RULE(star_exp) + AST_RULE(CompForEach) + AST_RULE(CompFor) + AST_RULE(CompInner) + AST_RULE(Assign) + AST_RULE(update_op) + AST_RULE(Update) + AST_RULE(BinaryOperator) + AST_RULE(unary_operator) + AST_RULE(Assignable) + AST_RULE(AssignableChain) + AST_RULE(exp_op_value) + AST_RULE(Exp) + AST_RULE(Callable) + AST_RULE(ChainValue) + AST_RULE(simple_table) + AST_RULE(SimpleValue) + AST_RULE(Value) + AST_RULE(LuaStringOpen); + AST_RULE(LuaStringContent); + AST_RULE(LuaStringClose); + AST_RULE(LuaString) + AST_RULE(SingleString) + AST_RULE(double_string_inner) + AST_RULE(double_string_content) + AST_RULE(DoubleString) + AST_RULE(String) + AST_RULE(Parens) + AST_RULE(DotChainItem) + AST_RULE(ColonChainItem) + AST_RULE(default_value) + AST_RULE(Slice) + AST_RULE(Invoke) + AST_RULE(existential_op) + AST_RULE(TableLit) + AST_RULE(TableBlock) + AST_RULE(TableBlockIndent) + AST_RULE(class_member_list) + AST_RULE(ClassBlock) + AST_RULE(ClassDecl) + AST_RULE(global_values) + AST_RULE(global_op) + AST_RULE(Global) + AST_RULE(export_default) + AST_RULE(Export) + AST_RULE(variable_pair) + AST_RULE(normal_pair) + AST_RULE(FnArgDef) + AST_RULE(FnArgDefList) + AST_RULE(outer_var_shadow) + AST_RULE(FnArgsDef) + AST_RULE(fn_arrow) + AST_RULE(FunLit) + AST_RULE(MacroName) + AST_RULE(MacroLit) + AST_RULE(Macro) + AST_RULE(NameOrDestructure) + AST_RULE(AssignableNameList) + AST_RULE(InvokeArgs) + AST_RULE(const_value) + AST_RULE(unary_value) + AST_RULE(unary_exp) + AST_RULE(ExpListAssign) + AST_RULE(if_line) + AST_RULE(unless_line) + AST_RULE(BreakLoop) + AST_RULE(statement_appendix) + AST_RULE(statement_sep) + AST_RULE(Statement) + AST_RULE(Body) + AST_RULE(Block) + AST_RULE(File) +}; + +namespace Utils { + void replace(std::string& str, std::string_view from, std::string_view to); + void trim(std::string& str); +}; + +} // namespace yue diff --git a/src/yuescript/yuescript.cpp b/src/yuescript/yuescript.cpp new file mode 100644 index 0000000..9928f4d --- /dev/null +++ b/src/yuescript/yuescript.cpp @@ -0,0 +1,139 @@ +/* Copyright (c) 2021 Jin Li, http://www.luvfight.me + +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. */ +#include "yuescript/yue_compiler.h" + +extern "C" { + +#include "lua.h" +#include "lauxlib.h" + +static const char yuescriptCodes[] = +#include "yuescript/yuescript.h" + +static void init_yuescript(lua_State* L) { + if (luaL_loadbuffer(L, yuescriptCodes, sizeof(yuescriptCodes) / sizeof(yuescriptCodes[0]) - 1, "=(yuescript)") != 0) { + std::string err = std::string("failed to load yuescript module.\n") + lua_tostring(L, -1); + luaL_error(L, err.c_str()); + } else if (lua_pcall(L, 0, 0, 0) != 0) { + std::string err = std::string("failed to init yuescript module.\n") + lua_tostring(L, -1); + luaL_error(L, err.c_str()); + } +} + +static const char stpCodes[] = +#include "yuescript/stacktraceplus.h" + +static int init_stacktraceplus(lua_State* L) { + if (luaL_loadbuffer(L, stpCodes, sizeof(stpCodes) / sizeof(stpCodes[0]) - 1, "=(stacktraceplus)") != 0) { + std::string err = std::string("failed to load stacktraceplus module.\n") + lua_tostring(L, -1); + luaL_error(L, err.c_str()); + } else if (lua_pcall(L, 0, 1, 0) != 0) { + std::string err = std::string("failed to init stacktraceplus module.\n") + lua_tostring(L, -1); + luaL_error(L, err.c_str()); + } + return 1; +} + +static int yuetolua(lua_State* L) { + size_t size = 0; + const char* input = luaL_checklstring(L, 1, &size); + yue::YueConfig config; + bool sameModule = false; + if (lua_gettop(L) == 2) { + luaL_checktype(L, 2, LUA_TTABLE); + lua_pushliteral(L, "lint_global"); + lua_gettable(L, -2); + if (lua_isboolean(L, -1) != 0) { + config.lintGlobalVariable = lua_toboolean(L, -1) != 0; + } + lua_pop(L, 1); + lua_pushliteral(L, "implicit_return_root"); + lua_gettable(L, -2); + if (lua_isboolean(L, -1) != 0) { + config.implicitReturnRoot = lua_toboolean(L, -1) != 0; + } + lua_pop(L, 1); + lua_pushliteral(L, "reserve_line_number"); + lua_gettable(L, -2); + if (lua_isboolean(L, -1) != 0) { + config.reserveLineNumber = lua_toboolean(L, -1) != 0; + } + lua_pop(L, 1); + lua_pushliteral(L, "space_over_tab"); + lua_gettable(L, -2); + if (lua_isboolean(L, -1) != 0) { + config.useSpaceOverTab = lua_toboolean(L, -1) != 0; + } + lua_pop(L, 1); + lua_pushliteral(L, "same_module"); + lua_gettable(L, -2); + if (lua_isboolean(L, -1) != 0) { + sameModule = lua_toboolean(L, -1) != 0; + } + lua_pop(L, 1); + lua_pushliteral(L, "line_offset"); + lua_gettable(L, -2); + if (lua_isnumber(L, -1) != 0) { + config.lineOffset = static_cast(lua_tonumber(L, -1)); + } + lua_pop(L, 1); + } + std::string s(input, size); + auto result = yue::YueCompiler(L, nullptr, sameModule).compile(s, config); + if (result.codes.empty() && !result.error.empty()) { + lua_pushnil(L); + } else { + lua_pushlstring(L, result.codes.c_str(), result.codes.size()); + } + if (result.error.empty()) { + lua_pushnil(L); + } else { + lua_pushlstring(L, result.error.c_str(), result.error.size()); + } + if (result.globals) { + lua_createtable(L, static_cast(result.globals->size()), 0); + int i = 1; + for (const auto& var : *result.globals) { + lua_createtable(L, 3, 0); + lua_pushlstring(L, var.name.c_str(), var.name.size()); + lua_rawseti(L, -2, 1); + lua_pushinteger(L, var.line); + lua_rawseti(L, -2, 2); + lua_pushinteger(L, var.col); + lua_rawseti(L, -2, 3); + lua_rawseti(L, -2, i); + i++; + } + } else { + lua_pushnil(L); + } + return 3; +} + +int luaopen_yue(lua_State* L) { + lua_getglobal(L, "package"); // package + lua_getfield(L, -1, "loaded"); // package loaded + lua_createtable(L, 0, 0); // package loaded yue + lua_pushcfunction(L, yuetolua); // package loaded yue func + lua_setfield(L, -2, "to_lua"); // yue["to_lua"] = func, package loaded yue + lua_pushlstring(L, &yue::version.front(), yue::version.size()); // package loaded yue version + lua_setfield(L, -2, "version"); // yue["version"] = version, package loaded yue + lua_createtable(L, 0, 0); // package loaded yue options + lua_pushlstring(L, &yue::extension.front(), yue::extension.size()); // package loaded yue options ext + lua_setfield(L, -2, "extension"); // options["extension"] = ext, package loaded yue options + lua_setfield(L, -2, "options"); // yue["options"] = options, package loaded yue + lua_pushcfunction(L, init_stacktraceplus); // package loaded yue func1 + lua_setfield(L, -2, "load_stacktraceplus"); // yue["load_stacktraceplus"] = func1, package loaded yue + lua_setfield(L, -2, "yue"); // loaded["yue"] = yue, package loaded + lua_pop(L, 2); // empty + init_yuescript(L); + return 0; +} + +} // extern "C" + diff --git a/src/yuescript/yuescript.h b/src/yuescript/yuescript.h new file mode 100644 index 0000000..433751e --- /dev/null +++ b/src/yuescript/yuescript.h @@ -0,0 +1,251 @@ +R"yuescript_codes( +--[[ +Copyright (C) 2020 by Leaf Corcoran, modified by Li Jin, 2021 + +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.]] + +local yue = require("yue") +local concat, insert, remove = table.concat, table.insert, table.remove +local unpack = unpack or table.unpack +local lua = { + loadstring = loadstring, + load = load +} +local split, get_options, create_yuepath, yue_loader, load_text, yue_call, loadstring, loadfile, dofile, insert_loader, remove_loader, yue_require, find_modulepath +yue.dirsep = "/" +yue.yue_compiled = { } +yue.file_exist = function(fname) + local file = io.open(fname) + if file then + file:close() + return true + else + return false + end +end +yue.read_file = 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 text +end +split = function(str, delim) + if str == "" then + return { } + end + str = str .. delim + 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 +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 +create_yuepath = function(package_path) + local extension = yue.options.extension + local yuepaths + do + local _accum_0 = { } + local _len_0 = 1 + local _list_0 = split(package_path, ";") + for _index_0 = 1, #_list_0 do + local path = _list_0[_index_0] + local _continue_0 = false + repeat + local prefix = path:match("^(.-)%.lua$") + if not prefix then + _continue_0 = true + break + end + _accum_0[_len_0] = prefix .. "." .. extension + _len_0 = _len_0 + 1 + _continue_0 = true + until true + if not _continue_0 then + break + end + end + yuepaths = _accum_0 + end + return concat(yuepaths, ";") +end +find_modulepath = function(name) + if not package.yuepath then + package.yuepath = create_yuepath(package.path) + end + local name_path = name:match("[\\/]") and name or name:gsub("%.", yue.dirsep) + local file_exist, file_path + for path in package.yuepath:gmatch("[^;]+") do + file_path = path:gsub("?", name_path) + file_exist = yue.file_exist(file_path) + if file_exist then + break + end + end + if file_exist then + return file_path + else + return nil + end +end +load_text = function(name) + local file_path = find_modulepath(name) + if file_path then + return yue.read_file(file_path), file_path + end + return nil, nil +end +yue_loader = function(name) + local text, file_path = load_text(name) + if text then + local res, err = loadstring(text, file_path) + if not res then + error(file_path .. ": " .. err) + end + return res + end + return nil, "Could not find yue file" +end +yue_call = function(f, ...) + local args = { + ... + } + return xpcall((function() + return f(unpack(args)) + end), function(err) + return yue.stp.stacktrace(err, 1) + end) +end +loadstring = function(...) + local options, str, chunk_name, mode, env = get_options(...) + chunk_name = chunk_name or "=(yuescript.loadstring)" + local code, err = yue.to_lua(str, options) + if not code then + return nil, err + end + if chunk_name then + yue.yue_compiled["@" .. chunk_name] = code + end + return (lua.loadstring or lua.load)(code, chunk_name, unpack({ + mode, + env + })) +end +loadfile = function(fname, ...) + local text = yue.read_file(fname) + 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.yuepath then + package.yuepath = create_yuepath(package.path) + end + local loaders = package.loaders or package.searchers + for _index_0 = 1, #loaders do + local loader = loaders[_index_0] + if loader == yue_loader then + return false + end + end + insert(loaders, pos, yue_loader) + return true +end +remove_loader = function() + local loaders = package.loaders or package.searchers + for i, loader in ipairs(loaders) do + if loader == yue_loader then + remove(loaders, i) + return true + end + end + return false +end +yue_require = function(name) + insert_loader() + local success, res = xpcall((function() + return require(name) + end), function(err) + local msg = yue.stp.stacktrace(err, 1) + print(msg) + return msg + end) + if success then + return res + else + return nil + end +end +setmetatable(yue, { + __index = function(self, key) + if not (key == "stp") then + return nil + end + local stp = rawget(yue, "stp") + if not stp then + do + local _with_0 = yue.load_stacktraceplus() + _with_0.dump_locals = false + _with_0.simplified = true + stp = _with_0 + end + rawset(yue, "stp", stp) + rawset(yue, "load_stacktraceplus", nil) + end + return stp + end, + __call = function(self, name) + return self.require(name) + end +}) +for k, v in pairs({ + insert_loader = insert_loader, + remove_loader = remove_loader, + loader = yue_loader, + dofile = dofile, + loadfile = loadfile, + loadstring = loadstring, + create_yuepath = create_yuepath, + find_modulepath = find_modulepath, + pcall = yue_call, + require = yue_require +}) do + yue[k] = v +end +)yuescript_codes"; diff --git a/yuescript-dev-1.rockspec b/yuescript-dev-1.rockspec new file mode 100644 index 0000000..75835fc --- /dev/null +++ b/yuescript-dev-1.rockspec @@ -0,0 +1,33 @@ +rockspec_format = "3.0" +package = "Yuescript" +version = "dev-1" +source = { + url = "git+https://github.com/pigpigyyy/yuescript" +} +description = { + summary = "Yuescript is a Moonscript dialect.", + detailed = [[ + Yuescript is a Moonscript dialect. It is derived from Moonscript language 0.5.0 and continuously adopting new features to be more up to date. ]], + homepage = "https://github.com/pigpigyyy/yuescript", + maintainer = "Li Jin ", + labels = {"yuescript","cpp","transpiler","moonscript"}, + license = "MIT" +} +dependencies = { + "lua >= 5.1", +} +build = { + type = "cmake", + variables = { + LUA_INCDIR="$(LUA_INCDIR)", + LUA="$(LUA)", + }, + install = { + lib = { + "build.luarocks/yue.so" + }, + bin = { + "build.luarocks/yue" + } + } +} -- cgit v1.2.3-55-g6feb