From fe4d46b382ff034f28fd517966fd8b4f7d717379 Mon Sep 17 00:00:00 2001 From: aqzp <72946059+AsynchronousAI@users.noreply.github.com> Date: Wed, 2 Aug 2023 11:32:26 -0500 Subject: [PATCH 01/77] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index eed5b4e..9eae669 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Introduction + [![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#contributors-) From 6bc8d9bab6f080f1f8c5e954a1598e1d4865aa63 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sun, 20 Aug 2023 18:41:33 -0500 Subject: [PATCH 02/77] Prepare. --- ... 2023-07-10 at 12.06.03\342\200\257AM.png" | Bin 87738 -> 0 bytes .github/workflows/python-publish.yml | 40 ---- .github/workflows/sponsors.yml | 23 -- MANIFEST.in | 3 - c-docs/introduction.md | 52 ----- cli-docs/introduction.md | 39 ---- cli/installation.md | 33 --- docs/404.html | 9 - docs/c.html | 9 - docs/cpp.html | 9 - docs/index.html | 55 ----- docs/moonscript.html | 96 -------- docs/moonscript.js | 213 ------------------ docs/python.html | 96 -------- lunar-docs/introduction.md | 27 --- lunar/api.md | 7 - py-docs/introduction.md | 29 --- pyproject.toml | 3 - python/api.md | 116 ---------- rojo-test/.gitignore | 6 - rojo-test/README.md | 17 -- rojo-test/default.project.json | 72 ------ rojo-test/default.project.lua | 2 - rojo-test/src-compiled/client/init.client.lua | 22 -- rojo-test/src-compiled/server/init.server.lua | 28 --- rojo-test/src-compiled/shared/metadata.lua | 2 - rojo-test/src/.rpyc | 0 rojo-test/src/client/init.client.lua | 22 -- rojo-test/src/client/init.client.py | 1 - rojo-test/src/server/init.server.lua | 28 --- rojo-test/src/server/init.server.py | 5 - rojo-test/src/shared/Hello.py | 84 ------- rojo-test/src/shared/metadata.json | 9 - rojo-test/src/shared/metadata.lua | 2 - setup.cfg | 23 -- setup.py | 26 --- {robloxpyc => src}/LuauAST/README.md | 0 {robloxpyc => src}/LuauAST/bundle.ts | 0 {robloxpyc => src}/LuauAST/impl/List.ts | 0 {robloxpyc => src}/LuauAST/impl/create.ts | 0 {robloxpyc => src}/LuauAST/impl/enums.ts | 0 {robloxpyc => src}/LuauAST/impl/globals.ts | 0 {robloxpyc => src}/LuauAST/impl/strings.ts | 0 {robloxpyc => src}/LuauAST/impl/typeGuards.ts | 0 {robloxpyc => src}/LuauAST/index.ts | 0 {robloxpyc => src}/LuauAST/types/mapping.ts | 0 {robloxpyc => src}/LuauAST/types/nodes.ts | 0 {robloxpyc => src}/LuauAST/types/operators.ts | 0 {robloxpyc => src}/LuauAST/util/assert.ts | 0 .../LuauAST/util/getKindName.ts | 0 .../LuauAST/util/isMetamethod.ts | 0 .../LuauAST/util/isReservedClassField.ts | 0 .../LuauAST/util/isReservedIdentifier.ts | 0 .../LuauAST/util/isValidIdentifier.ts | 0 .../LuauAST/util/isValidNumberLiteral.ts | 0 {robloxpyc => src}/LuauRenderer/README.md | 0 .../LuauRenderer/RenderState.ts | 0 {robloxpyc => src}/LuauRenderer/index.ts | 0 .../indexable/renderCallExpression.ts | 0 .../renderComputedIndexExpression.ts | 0 .../expressions/indexable/renderIdentifier.ts | 0 .../indexable/renderMethodCallExpression.ts | 0 .../renderParenthesizedExpression.ts | 0 .../renderPropertyAccessExpression.ts | 0 .../indexable/renderTemporaryIdentifier.ts | 0 .../nodes/expressions/renderArray.ts | 0 .../expressions/renderBinaryExpression.ts | 0 .../expressions/renderFunctionExpression.ts | 0 .../nodes/expressions/renderIfExpression.ts | 0 .../nodes/expressions/renderMap.ts | 0 .../nodes/expressions/renderMixedTable.ts | 0 .../nodes/expressions/renderNumberLiteral.ts | 0 .../nodes/expressions/renderSet.ts | 0 .../nodes/expressions/renderStringLiteral.ts | 0 .../expressions/renderUnaryExpression.ts | 0 .../nodes/fields/renderMapField.ts | 0 .../nodes/statements/renderAssignment.ts | 0 .../nodes/statements/renderBreakStatement.ts | 0 .../nodes/statements/renderCallStatement.ts | 0 .../nodes/statements/renderComment.ts | 0 .../statements/renderContinueStatement.ts | 0 .../nodes/statements/renderDoStatement.ts | 0 .../nodes/statements/renderForStatement.ts | 0 .../statements/renderFunctionDeclaration.ts | 0 .../nodes/statements/renderIfStatement.ts | 0 .../statements/renderMethodDeclaration.ts | 0 .../statements/renderNumericForStatement.ts | 0 .../nodes/statements/renderRepeatStatement.ts | 0 .../nodes/statements/renderReturnStatement.ts | 0 .../statements/renderVariableDeclaration.ts | 0 .../nodes/statements/renderWhileStatement.ts | 0 {robloxpyc => src}/LuauRenderer/render.ts | 0 .../LuauRenderer/solveTempIds.ts | 0 .../LuauRenderer/util/getEnding.ts | 0 .../LuauRenderer/util/getOrSetDefault.ts | 0 .../LuauRenderer/util/getSafeBracketEquals.ts | 0 .../LuauRenderer/util/identity.ts | 0 .../LuauRenderer/util/needsParentheses.ts | 0 .../LuauRenderer/util/renderArguments.ts | 0 .../LuauRenderer/util/renderParameters.ts | 0 .../LuauRenderer/util/renderStatements.ts | 0 {robloxpyc => src}/LuauRenderer/util/visit.ts | 0 .../__communication__/README.md | 0 {robloxpyc => src}/__communication__/cfg.pkl | Bin {robloxpyc => src}/__communication__/ts.json | 0 {robloxpyc => src}/__init__.py | 0 {robloxpyc => src}/basecompilers.py | 0 {robloxpyc => src}/bindings.py | 0 {robloxpyc => src}/binopdesc.py | 0 {robloxpyc => src}/boolopdesc.py | 0 {robloxpyc => src}/climaker.py | 0 {robloxpyc => src}/cmpopdesc.py | 0 {robloxpyc => src}/cnodevisitor.py | 0 {robloxpyc => src}/colortext.py | 0 {robloxpyc => src}/config.py | 0 {robloxpyc => src}/configmanager.py | 0 {robloxpyc => src}/context.py | 0 {robloxpyc => src}/cpAST.py | 0 {robloxpyc => src}/ctranslator.py | 0 {robloxpyc => src}/errormanager.py | 0 {robloxpyc => src}/header.py | 0 {robloxpyc => src}/installationmanager.py | 0 {robloxpyc => src}/loader.py | 0 {robloxpyc => src}/loopcounter.py | 0 {robloxpyc => src}/luainit.py | 0 {robloxpyc => src}/luainitlua.lua | 0 {robloxpyc => src}/model.py | 0 {robloxpyc => src}/nameconstdesc.py | 0 {robloxpyc => src}/nodevisitor.py | 0 {robloxpyc => src}/parser.py | 0 {robloxpyc => src}/plugin.py | 0 {robloxpyc => src}/pytranslator.py | 0 {robloxpyc => src}/reporter.py | 0 {robloxpyc => src}/robloxpy.py | 2 +- {robloxpyc => src}/sealang.cpp | 0 {robloxpyc => src}/sealang.h | 0 {robloxpyc => src}/swiftparser.swift | 0 {robloxpyc => src}/symbolsstack.py | 0 {robloxpyc => src}/textcompiler.py | 0 {robloxpyc => src}/tokenendmode.py | 0 {robloxpyc => src}/unaryopdesc.py | 0 {robloxpyc => src}/util.py | 0 {robloxpyc => src}/wally.py | 0 {robloxpyc => src}/writer.py | 0 test/c/test.c | 19 -- test/cpp/test.cpp | 18 -- test/json.json | 9 - test/json.lua | 2 - test/lunar/count.lua | 43 ---- test/lunar/count.moon | 33 --- test/lunar/customlib.lua | 11 - test/lunar/customlib.moon | 13 -- test/python/API.lua | 47 ---- test/python/API.py | 23 -- test/python/ann.lua | 21 -- test/python/ann.py | 3 - test/python/asyncdef.lua | 25 -- test/python/asyncdef.py | 3 - test/python/asyncwith.lua | 31 --- test/python/asyncwith.py | 4 - test/python/bytes.lua | 23 -- test/python/bytes.py | 2 - test/python/class.lua | 48 ---- test/python/class.py | 24 -- test/python/continue.lua | 25 -- test/python/continue.py | 6 - test/python/dict.lua | 30 --- test/python/dict.py | 8 - test/python/doublestar.lua | 23 -- test/python/doublestar.py | 9 - test/python/formatusing%.lua | 25 -- test/python/formatusing%.py | 3 - test/python/helloworld.lua | 22 -- test/python/helloworld.py | 1 - test/python/import.lua | 26 --- test/python/import.py | 8 - test/python/importall.lua | 22 -- test/python/importall.py | 4 - test/python/in.lua | 28 --- test/python/in.py | 4 - test/python/lambda.lua | 25 -- test/python/lambda.py | 2 - test/python/list.lua | 33 --- test/python/list.py | 13 -- test/python/luaxpy.lua | 26 --- test/python/luaxpy.py | 4 - test/python/match.lua | 34 --- test/python/match.py | 11 - test/python/matrix.lua | 34 --- test/python/matrix.py | 14 -- test/python/memoryaddress.lua | 25 -- test/python/memoryaddress.py | 4 - test/python/multiassign.lua | 23 -- test/python/multiassign.py | 2 - test/python/slice.lua | 26 --- test/python/slice.py | 3 - test/python/staticmethod.lua | 36 --- test/python/staticmethod.py | 9 - test/python/string.lua | 26 --- test/python/string.py | 2 - test/python/try.lua | 25 -- test/python/try.py | 4 - test/python/typehints.lua | 29 --- test/python/typehints.py | 9 - test/python/with.lua | 26 --- test/python/with.py | 2 - test/python/xl.lua | 94 -------- test/python/xl.py | 64 ------ 208 files changed, 1 insertion(+), 2460 deletions(-) delete mode 100644 ".gitbook/assets/Screenshot 2023-07-10 at 12.06.03\342\200\257AM.png" delete mode 100644 .github/workflows/python-publish.yml delete mode 100644 .github/workflows/sponsors.yml delete mode 100644 MANIFEST.in delete mode 100644 c-docs/introduction.md delete mode 100644 cli-docs/introduction.md delete mode 100644 cli/installation.md delete mode 100644 docs/404.html delete mode 100644 docs/c.html delete mode 100644 docs/cpp.html delete mode 100644 docs/index.html delete mode 100644 docs/moonscript.html delete mode 100644 docs/moonscript.js delete mode 100644 docs/python.html delete mode 100644 lunar-docs/introduction.md delete mode 100644 lunar/api.md delete mode 100644 py-docs/introduction.md delete mode 100644 pyproject.toml delete mode 100644 python/api.md delete mode 100644 rojo-test/.gitignore delete mode 100644 rojo-test/README.md delete mode 100644 rojo-test/default.project.json delete mode 100644 rojo-test/default.project.lua delete mode 100644 rojo-test/src-compiled/client/init.client.lua delete mode 100644 rojo-test/src-compiled/server/init.server.lua delete mode 100644 rojo-test/src-compiled/shared/metadata.lua delete mode 100644 rojo-test/src/.rpyc delete mode 100644 rojo-test/src/client/init.client.lua delete mode 100644 rojo-test/src/client/init.client.py delete mode 100644 rojo-test/src/server/init.server.lua delete mode 100644 rojo-test/src/server/init.server.py delete mode 100644 rojo-test/src/shared/Hello.py delete mode 100644 rojo-test/src/shared/metadata.json delete mode 100644 rojo-test/src/shared/metadata.lua delete mode 100644 setup.cfg delete mode 100644 setup.py rename {robloxpyc => src}/LuauAST/README.md (100%) rename {robloxpyc => src}/LuauAST/bundle.ts (100%) rename {robloxpyc => src}/LuauAST/impl/List.ts (100%) rename {robloxpyc => src}/LuauAST/impl/create.ts (100%) rename {robloxpyc => src}/LuauAST/impl/enums.ts (100%) rename {robloxpyc => src}/LuauAST/impl/globals.ts (100%) rename {robloxpyc => src}/LuauAST/impl/strings.ts (100%) rename {robloxpyc => src}/LuauAST/impl/typeGuards.ts (100%) rename {robloxpyc => src}/LuauAST/index.ts (100%) rename {robloxpyc => src}/LuauAST/types/mapping.ts (100%) rename {robloxpyc => src}/LuauAST/types/nodes.ts (100%) rename {robloxpyc => src}/LuauAST/types/operators.ts (100%) rename {robloxpyc => src}/LuauAST/util/assert.ts (100%) rename {robloxpyc => src}/LuauAST/util/getKindName.ts (100%) rename {robloxpyc => src}/LuauAST/util/isMetamethod.ts (100%) rename {robloxpyc => src}/LuauAST/util/isReservedClassField.ts (100%) rename {robloxpyc => src}/LuauAST/util/isReservedIdentifier.ts (100%) rename {robloxpyc => src}/LuauAST/util/isValidIdentifier.ts (100%) rename {robloxpyc => src}/LuauAST/util/isValidNumberLiteral.ts (100%) rename {robloxpyc => src}/LuauRenderer/README.md (100%) rename {robloxpyc => src}/LuauRenderer/RenderState.ts (100%) rename {robloxpyc => src}/LuauRenderer/index.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/expressions/indexable/renderCallExpression.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/expressions/indexable/renderComputedIndexExpression.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/expressions/indexable/renderIdentifier.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/expressions/indexable/renderMethodCallExpression.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/expressions/indexable/renderParenthesizedExpression.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/expressions/indexable/renderPropertyAccessExpression.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/expressions/indexable/renderTemporaryIdentifier.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/expressions/renderArray.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/expressions/renderBinaryExpression.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/expressions/renderFunctionExpression.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/expressions/renderIfExpression.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/expressions/renderMap.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/expressions/renderMixedTable.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/expressions/renderNumberLiteral.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/expressions/renderSet.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/expressions/renderStringLiteral.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/expressions/renderUnaryExpression.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/fields/renderMapField.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/statements/renderAssignment.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/statements/renderBreakStatement.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/statements/renderCallStatement.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/statements/renderComment.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/statements/renderContinueStatement.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/statements/renderDoStatement.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/statements/renderForStatement.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/statements/renderFunctionDeclaration.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/statements/renderIfStatement.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/statements/renderMethodDeclaration.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/statements/renderNumericForStatement.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/statements/renderRepeatStatement.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/statements/renderReturnStatement.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/statements/renderVariableDeclaration.ts (100%) rename {robloxpyc => src}/LuauRenderer/nodes/statements/renderWhileStatement.ts (100%) rename {robloxpyc => src}/LuauRenderer/render.ts (100%) rename {robloxpyc => src}/LuauRenderer/solveTempIds.ts (100%) rename {robloxpyc => src}/LuauRenderer/util/getEnding.ts (100%) rename {robloxpyc => src}/LuauRenderer/util/getOrSetDefault.ts (100%) rename {robloxpyc => src}/LuauRenderer/util/getSafeBracketEquals.ts (100%) rename {robloxpyc => src}/LuauRenderer/util/identity.ts (100%) rename {robloxpyc => src}/LuauRenderer/util/needsParentheses.ts (100%) rename {robloxpyc => src}/LuauRenderer/util/renderArguments.ts (100%) rename {robloxpyc => src}/LuauRenderer/util/renderParameters.ts (100%) rename {robloxpyc => src}/LuauRenderer/util/renderStatements.ts (100%) rename {robloxpyc => src}/LuauRenderer/util/visit.ts (100%) rename {robloxpyc => src}/__communication__/README.md (100%) rename {robloxpyc => src}/__communication__/cfg.pkl (100%) rename {robloxpyc => src}/__communication__/ts.json (100%) rename {robloxpyc => src}/__init__.py (100%) rename {robloxpyc => src}/basecompilers.py (100%) rename {robloxpyc => src}/bindings.py (100%) rename {robloxpyc => src}/binopdesc.py (100%) rename {robloxpyc => src}/boolopdesc.py (100%) rename {robloxpyc => src}/climaker.py (100%) rename {robloxpyc => src}/cmpopdesc.py (100%) rename {robloxpyc => src}/cnodevisitor.py (100%) rename {robloxpyc => src}/colortext.py (100%) rename {robloxpyc => src}/config.py (100%) rename {robloxpyc => src}/configmanager.py (100%) rename {robloxpyc => src}/context.py (100%) rename {robloxpyc => src}/cpAST.py (100%) rename {robloxpyc => src}/ctranslator.py (100%) rename {robloxpyc => src}/errormanager.py (100%) rename {robloxpyc => src}/header.py (100%) rename {robloxpyc => src}/installationmanager.py (100%) rename {robloxpyc => src}/loader.py (100%) rename {robloxpyc => src}/loopcounter.py (100%) rename {robloxpyc => src}/luainit.py (100%) rename {robloxpyc => src}/luainitlua.lua (100%) rename {robloxpyc => src}/model.py (100%) rename {robloxpyc => src}/nameconstdesc.py (100%) rename {robloxpyc => src}/nodevisitor.py (100%) rename {robloxpyc => src}/parser.py (100%) rename {robloxpyc => src}/plugin.py (100%) rename {robloxpyc => src}/pytranslator.py (100%) rename {robloxpyc => src}/reporter.py (100%) rename {robloxpyc => src}/robloxpy.py (99%) rename {robloxpyc => src}/sealang.cpp (100%) rename {robloxpyc => src}/sealang.h (100%) rename {robloxpyc => src}/swiftparser.swift (100%) rename {robloxpyc => src}/symbolsstack.py (100%) rename {robloxpyc => src}/textcompiler.py (100%) rename {robloxpyc => src}/tokenendmode.py (100%) rename {robloxpyc => src}/unaryopdesc.py (100%) rename {robloxpyc => src}/util.py (100%) rename {robloxpyc => src}/wally.py (100%) rename {robloxpyc => src}/writer.py (100%) delete mode 100644 test/c/test.c delete mode 100644 test/cpp/test.cpp delete mode 100644 test/json.json delete mode 100644 test/json.lua delete mode 100644 test/lunar/count.lua delete mode 100644 test/lunar/count.moon delete mode 100644 test/lunar/customlib.lua delete mode 100644 test/lunar/customlib.moon delete mode 100644 test/python/API.lua delete mode 100644 test/python/API.py delete mode 100644 test/python/ann.lua delete mode 100644 test/python/ann.py delete mode 100644 test/python/asyncdef.lua delete mode 100644 test/python/asyncdef.py delete mode 100644 test/python/asyncwith.lua delete mode 100644 test/python/asyncwith.py delete mode 100644 test/python/bytes.lua delete mode 100644 test/python/bytes.py delete mode 100644 test/python/class.lua delete mode 100644 test/python/class.py delete mode 100644 test/python/continue.lua delete mode 100644 test/python/continue.py delete mode 100644 test/python/dict.lua delete mode 100644 test/python/dict.py delete mode 100644 test/python/doublestar.lua delete mode 100644 test/python/doublestar.py delete mode 100644 test/python/formatusing%.lua delete mode 100644 test/python/formatusing%.py delete mode 100644 test/python/helloworld.lua delete mode 100644 test/python/helloworld.py delete mode 100644 test/python/import.lua delete mode 100644 test/python/import.py delete mode 100644 test/python/importall.lua delete mode 100644 test/python/importall.py delete mode 100644 test/python/in.lua delete mode 100644 test/python/in.py delete mode 100644 test/python/lambda.lua delete mode 100644 test/python/lambda.py delete mode 100644 test/python/list.lua delete mode 100644 test/python/list.py delete mode 100644 test/python/luaxpy.lua delete mode 100644 test/python/luaxpy.py delete mode 100644 test/python/match.lua delete mode 100644 test/python/match.py delete mode 100644 test/python/matrix.lua delete mode 100644 test/python/matrix.py delete mode 100644 test/python/memoryaddress.lua delete mode 100644 test/python/memoryaddress.py delete mode 100644 test/python/multiassign.lua delete mode 100644 test/python/multiassign.py delete mode 100644 test/python/slice.lua delete mode 100644 test/python/slice.py delete mode 100644 test/python/staticmethod.lua delete mode 100644 test/python/staticmethod.py delete mode 100644 test/python/string.lua delete mode 100644 test/python/string.py delete mode 100644 test/python/try.lua delete mode 100644 test/python/try.py delete mode 100644 test/python/typehints.lua delete mode 100644 test/python/typehints.py delete mode 100644 test/python/with.lua delete mode 100644 test/python/with.py delete mode 100644 test/python/xl.lua delete mode 100644 test/python/xl.py diff --git "a/.gitbook/assets/Screenshot 2023-07-10 at 12.06.03\342\200\257AM.png" "b/.gitbook/assets/Screenshot 2023-07-10 at 12.06.03\342\200\257AM.png" deleted file mode 100644 index 0ee1c46530b2b64ee97dd016486a299aa7dc7a27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 87738 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 z�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 V These languages are best for learning new syntax - -## C - -``` -rbxc c -``` - -Replaces all Lua scripts with C and comments old code. - -``` -rbxc w -``` - -Compile all C scripts, when enter is clicked again in the terminal compile again. - -``` -rbxc lib -``` - -Download the library, Recommended to install in the VsCode equivalent of ReplicatedStorage with the filename, for example, it is src/shared/roblox-pyc.lua in Rojo. - - - -## C++ - -``` -rbxcpp c -``` - -Replaces all Lua scripts with C++ and comments old code. - -``` -rbxcpp w -``` - -Compile all C++ scripts, when enter is clicked again in the terminal compile again. - -``` -rbxcpp lib -``` - -Download the library, Recommended to install in the VsCode equivalent of ReplicatedStorage with the filename, for example, it is src/shared/roblox-pyc.lua in Rojo. - - - -> The recommended C and C++ docs are by `devdocs.io` and are highly recommended - - - diff --git a/cli-docs/introduction.md b/cli-docs/introduction.md deleted file mode 100644 index b10767e..0000000 --- a/cli-docs/introduction.md +++ /dev/null @@ -1,39 +0,0 @@ -# Introduction - -## Commands - -The CLI is accessible by using - -``` -rpyc -``` - -it has the commands - -* config | Open config menu -* devforum | Open devforum post in your browser -* discord | Open the discord server in your browser -* github | Open the github page in your browser - -Everything else will open the help guide. - - - -*** - -``` -rpyc config -rpyc devforum -rpyc discord -rpyc github -``` - -## Config - -Configuration allows you to modify roblox-pyc and is **REQUIRED** for C and C++ to give it the - -std, stdlib, dynamiclib. - - - -Using the config command will give you options on which languages you want to modify, then some more individual settings. diff --git a/cli/installation.md b/cli/installation.md deleted file mode 100644 index 921f38d..0000000 --- a/cli/installation.md +++ /dev/null @@ -1,33 +0,0 @@ -# Installation - -### Install - -``` -pip install roblox-pyc -``` - -### Installed CLIs - -The following CLI's will be installed to your device - -#### Main compiler - -(You can use one of the equivalents also) - -* rbxpy | Python compiler -* rbxc | C compiler -* rbxcpp | C++ compiler -* rbxlun | Lunar compiler - -#### Primary tools - -* roblox-pyc | Primary tool, includes config and help -* rpyc | Same as roblox-pyc -* rblx-pyc | Same as roblox-pyc - -#### Equivalents - -* rblx-lun | Same as roblox-lunar -* rblx-py | Same as roblox-py -* rblx-c | Same as roblox-c -* rblx-cp | Same as roblox-cpp diff --git a/docs/404.html b/docs/404.html deleted file mode 100644 index 2687156..0000000 --- a/docs/404.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - -

404

- - \ No newline at end of file diff --git a/docs/c.html b/docs/c.html deleted file mode 100644 index 1d6f72e..0000000 --- a/docs/c.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - -

C and C++ are not available to the public

- - \ No newline at end of file diff --git a/docs/cpp.html b/docs/cpp.html deleted file mode 100644 index 1d6f72e..0000000 --- a/docs/cpp.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - -

C and C++ are not available to the public

- - \ No newline at end of file diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index b408e3c..0000000 --- a/docs/index.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - -

roblox-pyc playground

-
- - -
- - - \ No newline at end of file diff --git a/docs/moonscript.html b/docs/moonscript.html deleted file mode 100644 index b0faf3f..0000000 --- a/docs/moonscript.html +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - roblox-pyc playground - - - - - - - - - - - - -

roblox-pyc playground

- -
- -
- -
- -
- - - - \ No newline at end of file diff --git a/docs/moonscript.js b/docs/moonscript.js deleted file mode 100644 index d498f33..0000000 --- a/docs/moonscript.js +++ /dev/null @@ -1,213 +0,0 @@ -CodeMirror.defineMode("moonscript", function(config, parserConfig) { - var moonKeywords = parserConfig.keywords || {}; - var moonBuiltins = parserConfig.builtins || {}; - var moonAtoms = parserConfig.atoms || {}; - - var indentUnit = config.indentUnit; - - function prefixRE(words) { - return new RegExp("^(?:" + words.join("|") + ")\\b"); - } - - function wordRE(words) { - return new RegExp("^(?:" + words.join("|") + "))\\b"); - } - - var keywords = prefixRE(Object.keys(moonKeywords)); - var builtins = prefixRE(Object.keys(moonBuiltins)); - var atoms = wordRE(Object.keys(moonAtoms)); - - var indentStack = []; - - function tokenBase(stream, state) { - var ch = stream.next(); - - if (ch === "-" && stream.eat("-")) { - stream.skipToEnd(); - return "comment"; - } - - if (ch === "\"" || ch === "'") { - state.tokenize = tokenString(ch); - return state.tokenize(stream, state); - } - - if (ch === "[" && stream.match("[")) { - state.tokenize = tokenLongString(); - return state.tokenize(stream, state); - } - - if (/\d/.test(ch)) { - stream.eatWhile(/[\w\.]/); - return "number"; - } - - if (ch === ".") { - if (stream.eat(".")) { - if (stream.eat(".")) { - return "operator"; - } - return "punctuation"; - } - return "punctuation"; - } - - if (ch === "=") { - if (stream.eat("=")) { - return "operator"; - } - return "punctuation"; - } - - if (ch === "<" || ch === ">") { - if (stream.eat("=")) { - return "operator"; - } - return "operator"; - } - - if (ch === "~") { - if (stream.eat("=")) { - return "operator"; - } - return "punctuation"; - } - - if (ch === "+" || ch === "-" || ch === "*" || ch === "/" || ch === "%" || ch === "^") { - return "operator"; - } - - if (ch === "#" || ch === "@" || ch === "$" || ch === "&" || ch === "`") { - return "punctuation"; - } - - if (ch === "{" || ch === "}" || ch === "(" || ch === ")" || ch === "[" || ch === "]") { - return "bracket"; - } - - if (ch === ";" || ch === ",") { - return "punctuation"; - } - - if (/\w/.test(ch)) { - stream.eatWhile(/\w/); - var cur = stream.current(); - if (keywords.test(cur)) { - return "keyword"; - } - if (builtins.test(cur)) { - return "builtin"; - } - if (atoms.test(cur)) { - return "atom"; - } - return "variable"; - } - - return null; - } - - function tokenString(quote) { - return function(stream, state) { - var escaped = false; - var next; - while ((next = stream.next()) != null) { - if (next === quote && !escaped) { - state.tokenize = tokenBase; - break; - } - escaped = !escaped && next === "\\"; - } - return "string"; - }; - } - - function tokenLongString() { - return function(stream, state) { - var bracketCount = 0; - var next; - while ((next = stream.next()) != null) { - if (next === "]" && bracketCount === 2) { - state.tokenize = tokenBase; - break; - } - if (next === "]") { - bracketCount++; - } else { - bracketCount = 0; - } - } - return "string"; - }; - } - - function pushIndent(state, indent) { - state.indent.push(indent); - } - - function popIndent(state) { - state.indent.pop(); - } - - function topIndent(state) { - return state.indent[state.indent.length - 1]; - } - - return { - startState: function() { - return { - tokenize: tokenBase, - indent: [] - }; - }, - - token: function(stream, state) { - if (stream.eatSpace()) { - return null; - } - var style = state.tokenize(stream, state); - var currentIndent = topIndent(state); - if (style === "keyword" && stream.current() === "do") { - pushIndent(state, currentIndent + indentUnit); - } - if (style === "bracket" && stream.current() === "{") { - pushIndent(state, currentIndent + indentUnit); - } - if (style === "bracket" && stream.current() === "}") { - popIndent(state); - } - return style; - }, - - indent: function(state, textAfter) { - if (state.tokenize !== tokenBase) { - return null; - } - var currentIndent = topIndent(state); - if (/^\s*[\{\[]/.test(textAfter)) { - return currentIndent; - } - if (/^\s*\}/.test(textAfter)) { - if (state.indent.length > 1) { - return state.indent[state.indent.length - 2]; - } - return 0; - } - if (/^\s*else/.test(textAfter)) { - return currentIndent; - } - if (/^\s*end/.test(textAfter)) { - if (state.indent.length > 1) { - return state.indent[state.indent.length - 2]; - } - return 0; - } - return currentIndent; - }, - - lineComment: "--", - fold: "brace" - }; - }); - - CodeMirror.defineMIME("text/x-moonscript", "moonscript"); \ No newline at end of file diff --git a/docs/python.html b/docs/python.html deleted file mode 100644 index fbe2aef..0000000 --- a/docs/python.html +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - roblox-pyc playground - - - - - - - - - - - - -

roblox-pyc playground

- -
- -
- -
- -
- - - - \ No newline at end of file diff --git a/lunar-docs/introduction.md b/lunar-docs/introduction.md deleted file mode 100644 index fc3fb88..0000000 --- a/lunar-docs/introduction.md +++ /dev/null @@ -1,27 +0,0 @@ -# Introduction - -> This language is best for production use and great syntax sugar - -``` -rbxlun c -``` - -Replaces all Lua scripts with lunar and comments old code. - -``` -rbxlun w -``` - -Compile all lunar scripts, when enter is clicked again in the terminal compile again. - -``` -rbxlun lib -``` - -Starts dependencies installation - - - -> Lunar is based on MoonScript but modified for Roblox. -> -> The official docs page for Lunar will redirect you to the official moonscript docs. diff --git a/lunar/api.md b/lunar/api.md deleted file mode 100644 index c53307f..0000000 --- a/lunar/api.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -description: How can you use the built in API. ---- - -# API - -Lunar is pretty much Moonscript apart from the roblox classes. Thats it! diff --git a/py-docs/introduction.md b/py-docs/introduction.md deleted file mode 100644 index 2ef8364..0000000 --- a/py-docs/introduction.md +++ /dev/null @@ -1,29 +0,0 @@ -# Introduction - -> This language is best for a very functional and easy language. - -``` -rbxpy c -``` - -Replaces all Lua scripts with Python and comments old code. - -``` -rbxpy p -``` - -Start the plugin server (Python only). - -``` -rbxpy w -``` - -Compile all Python scripts, when enter is clicked again in the terminal compile again. - -``` -rbxpy lib -``` - -Starts dependencies installation - -> The official docs page for python will redirect you to the Python 3.13 docs diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 5f8f5e3..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,3 +0,0 @@ -[build-system] -build-backend = "setuptools.build_meta" -requires = ["setuptools", "wheel"] \ No newline at end of file diff --git a/python/api.md b/python/api.md deleted file mode 100644 index 293eb63..0000000 --- a/python/api.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -description: How can you use the built in API. ---- - -# API - -## Built-in - -Use the - -``` -game -``` - -variable to access the game in python, This is literarily the game variable in roblox. - -
ValidPlayers = [
-    "builderman"
-]
-
-@py.Workspace.Spawn.Touched
-def onTouch(touch):
-    print("Spawn has been touched by", touch.Name)
-    
-@game.Players.PlayerAdded
-def onPlrAdd(plr):
-    if plr.Name in ValidPlayers:
-        # The player is in our admin list, print admin joined
-        print("Admin", plr.Name, "has joined the game!!")
-        
-
- -The above code will do the following: - -* When a part in workspace called "Spawn" is touched print the touching parts name in the output -* When a player joins and their name is in the ValidPlayers list that means they are a admin and output that - -Compiled code: - -This code generated may be outdated, code generated with newer versions of roblox-pyc should deliver better results -```lua ---// Compiled using roblox-pyc \-- - - ------------------------------------- BUILT IN ------------------------------- -local py, libs, builtin = unpack(require(game.ReplicatedStorage["roblox.pyc"])(script).py) - -local stringmeta = builtin.stringmeta -local list = builtin.list -local str = builtin.str -local id = builtin.id -local int = builtin.int -local operator_in = builtin.operator_in -local min = builtin.min - ------------------------------------------------------------------------------ -local ValidPlayers = list {stringmeta "builderman"} -local function onTouch(touch) - print(stringmeta "Spawn has been touched by", touch.Name) -end -onTouch = py.Workspace.Spawn.Touched(onTouch) -local function onPlrAdd(plr) - if (operator_in(plr.Name, ValidPlayers)) then - print(stringmeta "Admin", plr.Name, stringmeta "has joined the game!!") - end -end -onPlrAdd = py.Players.PlayerAdded(onPlrAdd) -``` - -> stringmeta, list, dict are all functions used to add the Python API to lua objects. - -*** - -## Embedding lua - -Sometimes you need to do something in Lua, inside of a python script! But how? - -```python -print("Python code") -for i in range(1,10): - print("Python loop", i) -``` - -Thats python code, now let's add some lua code - -```psl -print("Python code") -for i in range(10): - print("Python loop", i) - -"""[[lua]] -``` - -```lua --- As seen above this is not a multiline comment because of the [[lua]] -for i = 1, 10 do - print(i) -end - --- We can still use python functions in lua this time without a problem -for i, v in range(10) do - print(i) -end -``` - -```python -""" -``` - -> This is the same script but I split it to 3 different code blocks for syntax highlighting - - - -*** - -Check out the tests in GitHub! diff --git a/rojo-test/.gitignore b/rojo-test/.gitignore deleted file mode 100644 index 0e4c27b..0000000 --- a/rojo-test/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# Project place file -/rojo-test.rbxlx - -# Roblox Studio lock files -/*.rbxlx.lock -/*.rbxl.lock \ No newline at end of file diff --git a/rojo-test/README.md b/rojo-test/README.md deleted file mode 100644 index 2ae0b09..0000000 --- a/rojo-test/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# rojo-test -Generated by [Rojo](https://github.com/rojo-rbx/rojo) 7.3.0. - -## Getting Started -To build the place from scratch, use: - -```bash -rojo build -o "rojo-test.rbxlx" -``` - -Next, open `rojo-test.rbxlx` in Roblox Studio and start the Rojo server: - -```bash -rojo serve -``` - -For more help, check out [the Rojo documentation](https://rojo.space/docs). \ No newline at end of file diff --git a/rojo-test/default.project.json b/rojo-test/default.project.json deleted file mode 100644 index ea185c3..0000000 --- a/rojo-test/default.project.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "name": "rojo-test", - "tree": { - "$className": "DataModel", - - "ReplicatedStorage": { - "Shared": { - "$path": "src-compiled/shared" - } - }, - - "ServerScriptService": { - "Server": { - "$path": "src-compiled/server" - } - }, - - "StarterPlayer": { - "StarterPlayerScripts": { - "Client": { - "$path": "src-compiled/client" - } - } - }, - - "Workspace": { - "$properties": { - "FilteringEnabled": true - }, - "Baseplate": { - "$className": "Part", - "$properties": { - "Anchored": true, - "Color": [ - 0.38823, - 0.37254, - 0.38823 - ], - "Locked": true, - "Position": [ - 0, - -10, - 0 - ], - "Size": [ - 512, - 20, - 512 - ] - } - } - }, - "Lighting": { - "$properties": { - "Ambient": [ - 0, - 0, - 0 - ], - "Brightness": 2, - "GlobalShadows": true, - "Outlines": false, - "Technology": "Voxel" - } - }, - "SoundService": { - "$properties": { - "RespectFilteringEnabled": true - } - } - } -} \ No newline at end of file diff --git a/rojo-test/default.project.lua b/rojo-test/default.project.lua deleted file mode 100644 index b532454..0000000 --- a/rojo-test/default.project.lua +++ /dev/null @@ -1,2 +0,0 @@ ---/ Compiled using roblox-pyc | JSON compiler \-- -return {Contents = {["name"] = "rojo-test", ["tree"] = {["$className"] = "DataModel", ["ReplicatedStorage"] = {["Shared"] = {["$path"] = "src-compiled/shared"}}, ["ServerScriptService"] = {["Server"] = {["$path"] = "src-compiled/server"}}, ["StarterPlayer"] = {["StarterPlayerScripts"] = {["Client"] = {["$path"] = "src-compiled/client"}}}, ["Workspace"] = {["$properties"] = {["FilteringEnabled"] = True}, ["Baseplate"] = {["$className"] = "Part", ["$properties"] = {["Anchored"] = True, ["Color"] = {0.38823, 0.37254, 0.38823}, ["Locked"] = True, ["Position"] = {0, -10, 0}, ["Size"] = {512, 20, 512}}}}, ["Lighting"] = {["$properties"] = {["Ambient"] = {0, 0, 0}, ["Brightness"] = 2, ["GlobalShadows"] = True, ["Outlines"] = False, ["Technology"] = "Voxel"}}, ["SoundService"] = {["$properties"] = {["RespectFilteringEnabled"] = True}}}}, Type = 'json', Extension = 'json', SetSource = function(self, input) self.Contents = input end} \ No newline at end of file diff --git a/rojo-test/src-compiled/client/init.client.lua b/rojo-test/src-compiled/client/init.client.lua deleted file mode 100644 index e8ffca1..0000000 --- a/rojo-test/src-compiled/client/init.client.lua +++ /dev/null @@ -1,22 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local int = builtin.int -local print = builtin.print - ------------------------------------------------------------------------------ -print("Hello!") - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/rojo-test/src-compiled/server/init.server.lua b/rojo-test/src-compiled/server/init.server.lua deleted file mode 100644 index 8a66a12..0000000 --- a/rojo-test/src-compiled/server/init.server.lua +++ /dev/null @@ -1,28 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local id = builtin.id -local shared = builtin.shared -local int = builtin.int -local print = builtin.print -local os = builtin.os - ------------------------------------------------------------------------------ -local Fluids = import("shared.Hello", "FluidCalculation") -local newFluid = Fluids(10, 10, 10, 10, 10) -newFluid.step(10) -print(newFluid.width, newFluid.height, newFluid.depth, newFluid.viscosity, newFluid.density, newFluid.velocity, newFluid.pressure) - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/rojo-test/src-compiled/shared/metadata.lua b/rojo-test/src-compiled/shared/metadata.lua deleted file mode 100644 index 3c00eb4..0000000 --- a/rojo-test/src-compiled/shared/metadata.lua +++ /dev/null @@ -1,2 +0,0 @@ ---/ Compiled using roblox-pyc | JSON compiler \-- -return {Contents = {["default"] = {["density"] = 1.0, ["viscosity"] = 1.0, ["width"] = 1.0, ["height"] = 1.0, ["depth"] = 1.0}}, Type = 'json', Extension = 'json', SetSource = function(self, input) self.Contents = input end} \ No newline at end of file diff --git a/rojo-test/src/.rpyc b/rojo-test/src/.rpyc deleted file mode 100644 index e69de29..0000000 diff --git a/rojo-test/src/client/init.client.lua b/rojo-test/src/client/init.client.lua deleted file mode 100644 index e8ffca1..0000000 --- a/rojo-test/src/client/init.client.lua +++ /dev/null @@ -1,22 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local int = builtin.int -local print = builtin.print - ------------------------------------------------------------------------------ -print("Hello!") - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/rojo-test/src/client/init.client.py b/rojo-test/src/client/init.client.py deleted file mode 100644 index f8f41c4..0000000 --- a/rojo-test/src/client/init.client.py +++ /dev/null @@ -1 +0,0 @@ -print("Hello!") \ No newline at end of file diff --git a/rojo-test/src/server/init.server.lua b/rojo-test/src/server/init.server.lua deleted file mode 100644 index 8a66a12..0000000 --- a/rojo-test/src/server/init.server.lua +++ /dev/null @@ -1,28 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local id = builtin.id -local shared = builtin.shared -local int = builtin.int -local print = builtin.print -local os = builtin.os - ------------------------------------------------------------------------------ -local Fluids = import("shared.Hello", "FluidCalculation") -local newFluid = Fluids(10, 10, 10, 10, 10) -newFluid.step(10) -print(newFluid.width, newFluid.height, newFluid.depth, newFluid.viscosity, newFluid.density, newFluid.velocity, newFluid.pressure) - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/rojo-test/src/server/init.server.py b/rojo-test/src/server/init.server.py deleted file mode 100644 index f33c76f..0000000 --- a/rojo-test/src/server/init.server.py +++ /dev/null @@ -1,5 +0,0 @@ -from shared.Hello import FluidCalculation as Fluids - -newFluid = Fluids(10, 10, 10, 10, 10) -newFluid.step(10) -print(newFluid.width, newFluid.height, newFluid.depth, newFluid.viscosity, newFluid.density, newFluid.velocity, newFluid.pressure) \ No newline at end of file diff --git a/rojo-test/src/shared/Hello.py b/rojo-test/src/shared/Hello.py deleted file mode 100644 index 37715f2..0000000 --- a/rojo-test/src/shared/Hello.py +++ /dev/null @@ -1,84 +0,0 @@ -""" -Simple fluid simulation 3d, based on the Navier-Stokes equations. -""" -from . import metadata as profiles -class FluidCalculation: - def __init__(self, width, height, depth, viscosity, density): - self.width = width or profiles.default.width - self.height = height or profiles.default.height - self.depth = depth or profiles.default.depth - self.viscosity = viscosity or profiles.default.viscosity - self.density = density or profiles.default.density - self.velocity = [[[0.0 for z in range(depth)] for y in range(height)] for x in range(width)] - self.pressure = [[[0.0 for z in range(depth)] for y in range(height)] for x in range(width)] - - def step(self, dt): - # Calculate velocity - for x in range(1, self.width - 1): - for y in range(1, self.height - 1): - for z in range(1, self.depth - 1): - u = self.velocity[x][y][z][0] - v = self.velocity[x][y][z][1] - w = self.velocity[x][y][z][2] - u += dt * (-u * (u - self.velocity[x - 1][y][z][0]) - - v * (u - self.velocity[x][y - 1][z][0]) - - w * (u - self.velocity[x][y][z - 1][0]) + - self.viscosity * ((self.velocity[x + 1][y][z][0] + - self.velocity[x - 1][y][z][0] + - self.velocity[x][y + 1][z][0] + - self.velocity[x][y - 1][z][0] + - self.velocity[x][y][z + 1][0] + - self.velocity[x][y][z - 1][0]) - - 6 * u)) - v += dt * (-u * (v - self.velocity[x - 1][y][z][1]) - - v * (v - self.velocity[x][y - 1][z][1]) - - w * (v - self.velocity[x][y][z - 1][1]) + - self.viscosity * ((self.velocity[x + 1][y][z][1] + - self.velocity[x - 1][y][z][1] + - self.velocity[x][y + 1][z][1] + - self.velocity[x][y - 1][z][1] + - self.velocity[x][y][z + 1][1] + - self.velocity[x][y][z - 1][1]) - - 6 * v)) - w += dt * (-u * (w - self.velocity[x - 1][y][z][2]) - - v * (w - self.velocity[x][y - 1][z][2]) - - w * (w - self.velocity[x][y][z - 1][2]) + - self.viscosity * ((self.velocity[x + 1][y][z][2] + - self.velocity[x - 1][y][z][2] + - self.velocity[x][y + 1][z][2] + - self.velocity[x][y - 1][z][2] + - self.velocity[x][y][z + 1][2] + - self.velocity[x][y][z - 1][2]) - - 6 * w)) - self.velocity[x][y][z][0] = u - self.velocity[x][y][z][1] = v - self.velocity[x][y][z][2] = w - - # Calculate pressure - for i in range(20): - for x in range(1, self.width - 1): - for y in range(1, self.height - 1): - for z in range(1, self.depth - 1): - self.pressure[x][y][z] = ((self.pressure[x + 1][y][z] + - self.pressure[x - 1][y][z] + - self.pressure[x][y + 1][z] + - self.pressure[x][y - 1][z] + - self.pressure[x][y][z + 1] + - self.pressure[x][y][z - 1]) + - self.density * (self.velocity[x][y][z][0] - - self.velocity[x - 1][y][z][0] + - self.velocity[x][y][z][1] - - self.velocity[x][y - 1][z][1] + - self.velocity[x][y][z][2] - - self.velocity[x][y][z - 1][2])) / 6.0 - - # Update velocity - for x in range(1, self.width - 1): - for y in range(1, self.height - 1): - for z in range(1, self.depth - 1): - self.velocity[x][y][z][0] -= dt * (self.pressure[x + 1][y][z] - - self.pressure[x - 1][y][z]) / (2.0 * self.density) - self.velocity[x][y][z][1] -= dt * (self.pressure[x][y + 1][z] - - self.pressure[x][y - 1][z]) / (2.0 * self.density) - self.velocity[x][y][z][2] -= dt * (self.pressure[x][y][z + 1] - - self.pressure[x][y][z - 1]) / (2.0 * self.density) \ No newline at end of file diff --git a/rojo-test/src/shared/metadata.json b/rojo-test/src/shared/metadata.json deleted file mode 100644 index f428ed3..0000000 --- a/rojo-test/src/shared/metadata.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "default": { - "density": 1.0, - "viscosity": 1.0, - "width": 1.0, - "height": 1.0, - "depth": 1.0 - } -} \ No newline at end of file diff --git a/rojo-test/src/shared/metadata.lua b/rojo-test/src/shared/metadata.lua deleted file mode 100644 index 3c00eb4..0000000 --- a/rojo-test/src/shared/metadata.lua +++ /dev/null @@ -1,2 +0,0 @@ ---/ Compiled using roblox-pyc | JSON compiler \-- -return {Contents = {["default"] = {["density"] = 1.0, ["viscosity"] = 1.0, ["width"] = 1.0, ["height"] = 1.0, ["depth"] = 1.0}}, Type = 'json', Extension = 'json', SetSource = function(self, input) self.Contents = input end} \ No newline at end of file diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index bd30bc8..0000000 --- a/setup.cfg +++ /dev/null @@ -1,23 +0,0 @@ -[metadata] -name = roblox-pyc -version = 2.26.113 - -[options] -packages = robloxpyc - - -[options.entry_points] -console_scripts = - rblx-py = robloxpyc.robloxpy:w - rblx-c = robloxpyc.robloxpy:candcpperror - rblx-cpp = robloxpyc.robloxpy:candcpperror - rblx-lunar = robloxpyc.robloxpy:lunar - rblx-pyc = robloxpyc.robloxpy:pyc - - rpyc = robloxpyc.robloxpy:pyc - - rbxpyc = robloxpyc.robloxpy:pyc - rbxlun = robloxpyc.robloxpy:lunar - rbxpy = robloxpyc.robloxpy:w - rbxc = robloxpyc.robloxpy:candcpperror - rbxcpp = robloxpyc.robloxpy:candcpperror diff --git a/setup.py b/setup.py deleted file mode 100644 index dd2f9cf..0000000 --- a/setup.py +++ /dev/null @@ -1,26 +0,0 @@ -from setuptools import setup, find_packages - - -# metadata goes here, =) -setup(install_requires=[ - 'clang', - 'libclang', - 'typer', - 'flask', - 'pyflakes', - 'pyyaml', - 'tqdm', - 'requests', - 'packaging' - ], - name = 'roblox-pyc', - description='The Python, Lunar, C, C++ to Roblox Lua compiler', - url="https://github.com/AsynchronousAI/roblox.pyc", - long_description=open('README.md').read(), - long_description_content_type='text/markdown', - include_package_data=True, - packages=find_packages(), - author='roblox-pyc team', - author_email="roblox.pyc@gmail.com", - license="GNU AFFERO GENERAL PUBLIC LICENSE", - ) diff --git a/robloxpyc/LuauAST/README.md b/src/LuauAST/README.md similarity index 100% rename from robloxpyc/LuauAST/README.md rename to src/LuauAST/README.md diff --git a/robloxpyc/LuauAST/bundle.ts b/src/LuauAST/bundle.ts similarity index 100% rename from robloxpyc/LuauAST/bundle.ts rename to src/LuauAST/bundle.ts diff --git a/robloxpyc/LuauAST/impl/List.ts b/src/LuauAST/impl/List.ts similarity index 100% rename from robloxpyc/LuauAST/impl/List.ts rename to src/LuauAST/impl/List.ts diff --git a/robloxpyc/LuauAST/impl/create.ts b/src/LuauAST/impl/create.ts similarity index 100% rename from robloxpyc/LuauAST/impl/create.ts rename to src/LuauAST/impl/create.ts diff --git a/robloxpyc/LuauAST/impl/enums.ts b/src/LuauAST/impl/enums.ts similarity index 100% rename from robloxpyc/LuauAST/impl/enums.ts rename to src/LuauAST/impl/enums.ts diff --git a/robloxpyc/LuauAST/impl/globals.ts b/src/LuauAST/impl/globals.ts similarity index 100% rename from robloxpyc/LuauAST/impl/globals.ts rename to src/LuauAST/impl/globals.ts diff --git a/robloxpyc/LuauAST/impl/strings.ts b/src/LuauAST/impl/strings.ts similarity index 100% rename from robloxpyc/LuauAST/impl/strings.ts rename to src/LuauAST/impl/strings.ts diff --git a/robloxpyc/LuauAST/impl/typeGuards.ts b/src/LuauAST/impl/typeGuards.ts similarity index 100% rename from robloxpyc/LuauAST/impl/typeGuards.ts rename to src/LuauAST/impl/typeGuards.ts diff --git a/robloxpyc/LuauAST/index.ts b/src/LuauAST/index.ts similarity index 100% rename from robloxpyc/LuauAST/index.ts rename to src/LuauAST/index.ts diff --git a/robloxpyc/LuauAST/types/mapping.ts b/src/LuauAST/types/mapping.ts similarity index 100% rename from robloxpyc/LuauAST/types/mapping.ts rename to src/LuauAST/types/mapping.ts diff --git a/robloxpyc/LuauAST/types/nodes.ts b/src/LuauAST/types/nodes.ts similarity index 100% rename from robloxpyc/LuauAST/types/nodes.ts rename to src/LuauAST/types/nodes.ts diff --git a/robloxpyc/LuauAST/types/operators.ts b/src/LuauAST/types/operators.ts similarity index 100% rename from robloxpyc/LuauAST/types/operators.ts rename to src/LuauAST/types/operators.ts diff --git a/robloxpyc/LuauAST/util/assert.ts b/src/LuauAST/util/assert.ts similarity index 100% rename from robloxpyc/LuauAST/util/assert.ts rename to src/LuauAST/util/assert.ts diff --git a/robloxpyc/LuauAST/util/getKindName.ts b/src/LuauAST/util/getKindName.ts similarity index 100% rename from robloxpyc/LuauAST/util/getKindName.ts rename to src/LuauAST/util/getKindName.ts diff --git a/robloxpyc/LuauAST/util/isMetamethod.ts b/src/LuauAST/util/isMetamethod.ts similarity index 100% rename from robloxpyc/LuauAST/util/isMetamethod.ts rename to src/LuauAST/util/isMetamethod.ts diff --git a/robloxpyc/LuauAST/util/isReservedClassField.ts b/src/LuauAST/util/isReservedClassField.ts similarity index 100% rename from robloxpyc/LuauAST/util/isReservedClassField.ts rename to src/LuauAST/util/isReservedClassField.ts diff --git a/robloxpyc/LuauAST/util/isReservedIdentifier.ts b/src/LuauAST/util/isReservedIdentifier.ts similarity index 100% rename from robloxpyc/LuauAST/util/isReservedIdentifier.ts rename to src/LuauAST/util/isReservedIdentifier.ts diff --git a/robloxpyc/LuauAST/util/isValidIdentifier.ts b/src/LuauAST/util/isValidIdentifier.ts similarity index 100% rename from robloxpyc/LuauAST/util/isValidIdentifier.ts rename to src/LuauAST/util/isValidIdentifier.ts diff --git a/robloxpyc/LuauAST/util/isValidNumberLiteral.ts b/src/LuauAST/util/isValidNumberLiteral.ts similarity index 100% rename from robloxpyc/LuauAST/util/isValidNumberLiteral.ts rename to src/LuauAST/util/isValidNumberLiteral.ts diff --git a/robloxpyc/LuauRenderer/README.md b/src/LuauRenderer/README.md similarity index 100% rename from robloxpyc/LuauRenderer/README.md rename to src/LuauRenderer/README.md diff --git a/robloxpyc/LuauRenderer/RenderState.ts b/src/LuauRenderer/RenderState.ts similarity index 100% rename from robloxpyc/LuauRenderer/RenderState.ts rename to src/LuauRenderer/RenderState.ts diff --git a/robloxpyc/LuauRenderer/index.ts b/src/LuauRenderer/index.ts similarity index 100% rename from robloxpyc/LuauRenderer/index.ts rename to src/LuauRenderer/index.ts diff --git a/robloxpyc/LuauRenderer/nodes/expressions/indexable/renderCallExpression.ts b/src/LuauRenderer/nodes/expressions/indexable/renderCallExpression.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/expressions/indexable/renderCallExpression.ts rename to src/LuauRenderer/nodes/expressions/indexable/renderCallExpression.ts diff --git a/robloxpyc/LuauRenderer/nodes/expressions/indexable/renderComputedIndexExpression.ts b/src/LuauRenderer/nodes/expressions/indexable/renderComputedIndexExpression.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/expressions/indexable/renderComputedIndexExpression.ts rename to src/LuauRenderer/nodes/expressions/indexable/renderComputedIndexExpression.ts diff --git a/robloxpyc/LuauRenderer/nodes/expressions/indexable/renderIdentifier.ts b/src/LuauRenderer/nodes/expressions/indexable/renderIdentifier.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/expressions/indexable/renderIdentifier.ts rename to src/LuauRenderer/nodes/expressions/indexable/renderIdentifier.ts diff --git a/robloxpyc/LuauRenderer/nodes/expressions/indexable/renderMethodCallExpression.ts b/src/LuauRenderer/nodes/expressions/indexable/renderMethodCallExpression.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/expressions/indexable/renderMethodCallExpression.ts rename to src/LuauRenderer/nodes/expressions/indexable/renderMethodCallExpression.ts diff --git a/robloxpyc/LuauRenderer/nodes/expressions/indexable/renderParenthesizedExpression.ts b/src/LuauRenderer/nodes/expressions/indexable/renderParenthesizedExpression.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/expressions/indexable/renderParenthesizedExpression.ts rename to src/LuauRenderer/nodes/expressions/indexable/renderParenthesizedExpression.ts diff --git a/robloxpyc/LuauRenderer/nodes/expressions/indexable/renderPropertyAccessExpression.ts b/src/LuauRenderer/nodes/expressions/indexable/renderPropertyAccessExpression.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/expressions/indexable/renderPropertyAccessExpression.ts rename to src/LuauRenderer/nodes/expressions/indexable/renderPropertyAccessExpression.ts diff --git a/robloxpyc/LuauRenderer/nodes/expressions/indexable/renderTemporaryIdentifier.ts b/src/LuauRenderer/nodes/expressions/indexable/renderTemporaryIdentifier.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/expressions/indexable/renderTemporaryIdentifier.ts rename to src/LuauRenderer/nodes/expressions/indexable/renderTemporaryIdentifier.ts diff --git a/robloxpyc/LuauRenderer/nodes/expressions/renderArray.ts b/src/LuauRenderer/nodes/expressions/renderArray.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/expressions/renderArray.ts rename to src/LuauRenderer/nodes/expressions/renderArray.ts diff --git a/robloxpyc/LuauRenderer/nodes/expressions/renderBinaryExpression.ts b/src/LuauRenderer/nodes/expressions/renderBinaryExpression.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/expressions/renderBinaryExpression.ts rename to src/LuauRenderer/nodes/expressions/renderBinaryExpression.ts diff --git a/robloxpyc/LuauRenderer/nodes/expressions/renderFunctionExpression.ts b/src/LuauRenderer/nodes/expressions/renderFunctionExpression.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/expressions/renderFunctionExpression.ts rename to src/LuauRenderer/nodes/expressions/renderFunctionExpression.ts diff --git a/robloxpyc/LuauRenderer/nodes/expressions/renderIfExpression.ts b/src/LuauRenderer/nodes/expressions/renderIfExpression.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/expressions/renderIfExpression.ts rename to src/LuauRenderer/nodes/expressions/renderIfExpression.ts diff --git a/robloxpyc/LuauRenderer/nodes/expressions/renderMap.ts b/src/LuauRenderer/nodes/expressions/renderMap.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/expressions/renderMap.ts rename to src/LuauRenderer/nodes/expressions/renderMap.ts diff --git a/robloxpyc/LuauRenderer/nodes/expressions/renderMixedTable.ts b/src/LuauRenderer/nodes/expressions/renderMixedTable.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/expressions/renderMixedTable.ts rename to src/LuauRenderer/nodes/expressions/renderMixedTable.ts diff --git a/robloxpyc/LuauRenderer/nodes/expressions/renderNumberLiteral.ts b/src/LuauRenderer/nodes/expressions/renderNumberLiteral.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/expressions/renderNumberLiteral.ts rename to src/LuauRenderer/nodes/expressions/renderNumberLiteral.ts diff --git a/robloxpyc/LuauRenderer/nodes/expressions/renderSet.ts b/src/LuauRenderer/nodes/expressions/renderSet.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/expressions/renderSet.ts rename to src/LuauRenderer/nodes/expressions/renderSet.ts diff --git a/robloxpyc/LuauRenderer/nodes/expressions/renderStringLiteral.ts b/src/LuauRenderer/nodes/expressions/renderStringLiteral.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/expressions/renderStringLiteral.ts rename to src/LuauRenderer/nodes/expressions/renderStringLiteral.ts diff --git a/robloxpyc/LuauRenderer/nodes/expressions/renderUnaryExpression.ts b/src/LuauRenderer/nodes/expressions/renderUnaryExpression.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/expressions/renderUnaryExpression.ts rename to src/LuauRenderer/nodes/expressions/renderUnaryExpression.ts diff --git a/robloxpyc/LuauRenderer/nodes/fields/renderMapField.ts b/src/LuauRenderer/nodes/fields/renderMapField.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/fields/renderMapField.ts rename to src/LuauRenderer/nodes/fields/renderMapField.ts diff --git a/robloxpyc/LuauRenderer/nodes/statements/renderAssignment.ts b/src/LuauRenderer/nodes/statements/renderAssignment.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/statements/renderAssignment.ts rename to src/LuauRenderer/nodes/statements/renderAssignment.ts diff --git a/robloxpyc/LuauRenderer/nodes/statements/renderBreakStatement.ts b/src/LuauRenderer/nodes/statements/renderBreakStatement.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/statements/renderBreakStatement.ts rename to src/LuauRenderer/nodes/statements/renderBreakStatement.ts diff --git a/robloxpyc/LuauRenderer/nodes/statements/renderCallStatement.ts b/src/LuauRenderer/nodes/statements/renderCallStatement.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/statements/renderCallStatement.ts rename to src/LuauRenderer/nodes/statements/renderCallStatement.ts diff --git a/robloxpyc/LuauRenderer/nodes/statements/renderComment.ts b/src/LuauRenderer/nodes/statements/renderComment.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/statements/renderComment.ts rename to src/LuauRenderer/nodes/statements/renderComment.ts diff --git a/robloxpyc/LuauRenderer/nodes/statements/renderContinueStatement.ts b/src/LuauRenderer/nodes/statements/renderContinueStatement.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/statements/renderContinueStatement.ts rename to src/LuauRenderer/nodes/statements/renderContinueStatement.ts diff --git a/robloxpyc/LuauRenderer/nodes/statements/renderDoStatement.ts b/src/LuauRenderer/nodes/statements/renderDoStatement.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/statements/renderDoStatement.ts rename to src/LuauRenderer/nodes/statements/renderDoStatement.ts diff --git a/robloxpyc/LuauRenderer/nodes/statements/renderForStatement.ts b/src/LuauRenderer/nodes/statements/renderForStatement.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/statements/renderForStatement.ts rename to src/LuauRenderer/nodes/statements/renderForStatement.ts diff --git a/robloxpyc/LuauRenderer/nodes/statements/renderFunctionDeclaration.ts b/src/LuauRenderer/nodes/statements/renderFunctionDeclaration.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/statements/renderFunctionDeclaration.ts rename to src/LuauRenderer/nodes/statements/renderFunctionDeclaration.ts diff --git a/robloxpyc/LuauRenderer/nodes/statements/renderIfStatement.ts b/src/LuauRenderer/nodes/statements/renderIfStatement.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/statements/renderIfStatement.ts rename to src/LuauRenderer/nodes/statements/renderIfStatement.ts diff --git a/robloxpyc/LuauRenderer/nodes/statements/renderMethodDeclaration.ts b/src/LuauRenderer/nodes/statements/renderMethodDeclaration.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/statements/renderMethodDeclaration.ts rename to src/LuauRenderer/nodes/statements/renderMethodDeclaration.ts diff --git a/robloxpyc/LuauRenderer/nodes/statements/renderNumericForStatement.ts b/src/LuauRenderer/nodes/statements/renderNumericForStatement.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/statements/renderNumericForStatement.ts rename to src/LuauRenderer/nodes/statements/renderNumericForStatement.ts diff --git a/robloxpyc/LuauRenderer/nodes/statements/renderRepeatStatement.ts b/src/LuauRenderer/nodes/statements/renderRepeatStatement.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/statements/renderRepeatStatement.ts rename to src/LuauRenderer/nodes/statements/renderRepeatStatement.ts diff --git a/robloxpyc/LuauRenderer/nodes/statements/renderReturnStatement.ts b/src/LuauRenderer/nodes/statements/renderReturnStatement.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/statements/renderReturnStatement.ts rename to src/LuauRenderer/nodes/statements/renderReturnStatement.ts diff --git a/robloxpyc/LuauRenderer/nodes/statements/renderVariableDeclaration.ts b/src/LuauRenderer/nodes/statements/renderVariableDeclaration.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/statements/renderVariableDeclaration.ts rename to src/LuauRenderer/nodes/statements/renderVariableDeclaration.ts diff --git a/robloxpyc/LuauRenderer/nodes/statements/renderWhileStatement.ts b/src/LuauRenderer/nodes/statements/renderWhileStatement.ts similarity index 100% rename from robloxpyc/LuauRenderer/nodes/statements/renderWhileStatement.ts rename to src/LuauRenderer/nodes/statements/renderWhileStatement.ts diff --git a/robloxpyc/LuauRenderer/render.ts b/src/LuauRenderer/render.ts similarity index 100% rename from robloxpyc/LuauRenderer/render.ts rename to src/LuauRenderer/render.ts diff --git a/robloxpyc/LuauRenderer/solveTempIds.ts b/src/LuauRenderer/solveTempIds.ts similarity index 100% rename from robloxpyc/LuauRenderer/solveTempIds.ts rename to src/LuauRenderer/solveTempIds.ts diff --git a/robloxpyc/LuauRenderer/util/getEnding.ts b/src/LuauRenderer/util/getEnding.ts similarity index 100% rename from robloxpyc/LuauRenderer/util/getEnding.ts rename to src/LuauRenderer/util/getEnding.ts diff --git a/robloxpyc/LuauRenderer/util/getOrSetDefault.ts b/src/LuauRenderer/util/getOrSetDefault.ts similarity index 100% rename from robloxpyc/LuauRenderer/util/getOrSetDefault.ts rename to src/LuauRenderer/util/getOrSetDefault.ts diff --git a/robloxpyc/LuauRenderer/util/getSafeBracketEquals.ts b/src/LuauRenderer/util/getSafeBracketEquals.ts similarity index 100% rename from robloxpyc/LuauRenderer/util/getSafeBracketEquals.ts rename to src/LuauRenderer/util/getSafeBracketEquals.ts diff --git a/robloxpyc/LuauRenderer/util/identity.ts b/src/LuauRenderer/util/identity.ts similarity index 100% rename from robloxpyc/LuauRenderer/util/identity.ts rename to src/LuauRenderer/util/identity.ts diff --git a/robloxpyc/LuauRenderer/util/needsParentheses.ts b/src/LuauRenderer/util/needsParentheses.ts similarity index 100% rename from robloxpyc/LuauRenderer/util/needsParentheses.ts rename to src/LuauRenderer/util/needsParentheses.ts diff --git a/robloxpyc/LuauRenderer/util/renderArguments.ts b/src/LuauRenderer/util/renderArguments.ts similarity index 100% rename from robloxpyc/LuauRenderer/util/renderArguments.ts rename to src/LuauRenderer/util/renderArguments.ts diff --git a/robloxpyc/LuauRenderer/util/renderParameters.ts b/src/LuauRenderer/util/renderParameters.ts similarity index 100% rename from robloxpyc/LuauRenderer/util/renderParameters.ts rename to src/LuauRenderer/util/renderParameters.ts diff --git a/robloxpyc/LuauRenderer/util/renderStatements.ts b/src/LuauRenderer/util/renderStatements.ts similarity index 100% rename from robloxpyc/LuauRenderer/util/renderStatements.ts rename to src/LuauRenderer/util/renderStatements.ts diff --git a/robloxpyc/LuauRenderer/util/visit.ts b/src/LuauRenderer/util/visit.ts similarity index 100% rename from robloxpyc/LuauRenderer/util/visit.ts rename to src/LuauRenderer/util/visit.ts diff --git a/robloxpyc/__communication__/README.md b/src/__communication__/README.md similarity index 100% rename from robloxpyc/__communication__/README.md rename to src/__communication__/README.md diff --git a/robloxpyc/__communication__/cfg.pkl b/src/__communication__/cfg.pkl similarity index 100% rename from robloxpyc/__communication__/cfg.pkl rename to src/__communication__/cfg.pkl diff --git a/robloxpyc/__communication__/ts.json b/src/__communication__/ts.json similarity index 100% rename from robloxpyc/__communication__/ts.json rename to src/__communication__/ts.json diff --git a/robloxpyc/__init__.py b/src/__init__.py similarity index 100% rename from robloxpyc/__init__.py rename to src/__init__.py diff --git a/robloxpyc/basecompilers.py b/src/basecompilers.py similarity index 100% rename from robloxpyc/basecompilers.py rename to src/basecompilers.py diff --git a/robloxpyc/bindings.py b/src/bindings.py similarity index 100% rename from robloxpyc/bindings.py rename to src/bindings.py diff --git a/robloxpyc/binopdesc.py b/src/binopdesc.py similarity index 100% rename from robloxpyc/binopdesc.py rename to src/binopdesc.py diff --git a/robloxpyc/boolopdesc.py b/src/boolopdesc.py similarity index 100% rename from robloxpyc/boolopdesc.py rename to src/boolopdesc.py diff --git a/robloxpyc/climaker.py b/src/climaker.py similarity index 100% rename from robloxpyc/climaker.py rename to src/climaker.py diff --git a/robloxpyc/cmpopdesc.py b/src/cmpopdesc.py similarity index 100% rename from robloxpyc/cmpopdesc.py rename to src/cmpopdesc.py diff --git a/robloxpyc/cnodevisitor.py b/src/cnodevisitor.py similarity index 100% rename from robloxpyc/cnodevisitor.py rename to src/cnodevisitor.py diff --git a/robloxpyc/colortext.py b/src/colortext.py similarity index 100% rename from robloxpyc/colortext.py rename to src/colortext.py diff --git a/robloxpyc/config.py b/src/config.py similarity index 100% rename from robloxpyc/config.py rename to src/config.py diff --git a/robloxpyc/configmanager.py b/src/configmanager.py similarity index 100% rename from robloxpyc/configmanager.py rename to src/configmanager.py diff --git a/robloxpyc/context.py b/src/context.py similarity index 100% rename from robloxpyc/context.py rename to src/context.py diff --git a/robloxpyc/cpAST.py b/src/cpAST.py similarity index 100% rename from robloxpyc/cpAST.py rename to src/cpAST.py diff --git a/robloxpyc/ctranslator.py b/src/ctranslator.py similarity index 100% rename from robloxpyc/ctranslator.py rename to src/ctranslator.py diff --git a/robloxpyc/errormanager.py b/src/errormanager.py similarity index 100% rename from robloxpyc/errormanager.py rename to src/errormanager.py diff --git a/robloxpyc/header.py b/src/header.py similarity index 100% rename from robloxpyc/header.py rename to src/header.py diff --git a/robloxpyc/installationmanager.py b/src/installationmanager.py similarity index 100% rename from robloxpyc/installationmanager.py rename to src/installationmanager.py diff --git a/robloxpyc/loader.py b/src/loader.py similarity index 100% rename from robloxpyc/loader.py rename to src/loader.py diff --git a/robloxpyc/loopcounter.py b/src/loopcounter.py similarity index 100% rename from robloxpyc/loopcounter.py rename to src/loopcounter.py diff --git a/robloxpyc/luainit.py b/src/luainit.py similarity index 100% rename from robloxpyc/luainit.py rename to src/luainit.py diff --git a/robloxpyc/luainitlua.lua b/src/luainitlua.lua similarity index 100% rename from robloxpyc/luainitlua.lua rename to src/luainitlua.lua diff --git a/robloxpyc/model.py b/src/model.py similarity index 100% rename from robloxpyc/model.py rename to src/model.py diff --git a/robloxpyc/nameconstdesc.py b/src/nameconstdesc.py similarity index 100% rename from robloxpyc/nameconstdesc.py rename to src/nameconstdesc.py diff --git a/robloxpyc/nodevisitor.py b/src/nodevisitor.py similarity index 100% rename from robloxpyc/nodevisitor.py rename to src/nodevisitor.py diff --git a/robloxpyc/parser.py b/src/parser.py similarity index 100% rename from robloxpyc/parser.py rename to src/parser.py diff --git a/robloxpyc/plugin.py b/src/plugin.py similarity index 100% rename from robloxpyc/plugin.py rename to src/plugin.py diff --git a/robloxpyc/pytranslator.py b/src/pytranslator.py similarity index 100% rename from robloxpyc/pytranslator.py rename to src/pytranslator.py diff --git a/robloxpyc/reporter.py b/src/reporter.py similarity index 100% rename from robloxpyc/reporter.py rename to src/reporter.py diff --git a/robloxpyc/robloxpy.py b/src/robloxpy.py similarity index 99% rename from robloxpyc/robloxpy.py rename to src/robloxpy.py index 4ebb7e9..510786d 100644 --- a/robloxpyc/robloxpy.py +++ b/src/robloxpy.py @@ -9,7 +9,7 @@ # FILES if __name__ == "__main__": - from robloxpyc import colortext #ctranslator is old and not used + from src import colortext #ctranslator is old and not used # MODULAR from errormanager import * diff --git a/robloxpyc/sealang.cpp b/src/sealang.cpp similarity index 100% rename from robloxpyc/sealang.cpp rename to src/sealang.cpp diff --git a/robloxpyc/sealang.h b/src/sealang.h similarity index 100% rename from robloxpyc/sealang.h rename to src/sealang.h diff --git a/robloxpyc/swiftparser.swift b/src/swiftparser.swift similarity index 100% rename from robloxpyc/swiftparser.swift rename to src/swiftparser.swift diff --git a/robloxpyc/symbolsstack.py b/src/symbolsstack.py similarity index 100% rename from robloxpyc/symbolsstack.py rename to src/symbolsstack.py diff --git a/robloxpyc/textcompiler.py b/src/textcompiler.py similarity index 100% rename from robloxpyc/textcompiler.py rename to src/textcompiler.py diff --git a/robloxpyc/tokenendmode.py b/src/tokenendmode.py similarity index 100% rename from robloxpyc/tokenendmode.py rename to src/tokenendmode.py diff --git a/robloxpyc/unaryopdesc.py b/src/unaryopdesc.py similarity index 100% rename from robloxpyc/unaryopdesc.py rename to src/unaryopdesc.py diff --git a/robloxpyc/util.py b/src/util.py similarity index 100% rename from robloxpyc/util.py rename to src/util.py diff --git a/robloxpyc/wally.py b/src/wally.py similarity index 100% rename from robloxpyc/wally.py rename to src/wally.py diff --git a/robloxpyc/writer.py b/src/writer.py similarity index 100% rename from robloxpyc/writer.py rename to src/writer.py diff --git a/test/c/test.c b/test/c/test.c deleted file mode 100644 index 5d6e495..0000000 --- a/test/c/test.c +++ /dev/null @@ -1,19 +0,0 @@ -#include - -int factorial(int n) { - if (n == 0 || n == 1) { - return 1; - } else { - return n * factorial(n - 1); - } -} - -int main() { - int num = 5; - int result = factorial(num); - - printf("The factorial of %d is %d\n", num, result); - - return 0; -} - diff --git a/test/cpp/test.cpp b/test/cpp/test.cpp deleted file mode 100644 index 74cad96..0000000 --- a/test/cpp/test.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include - -int factorial(int n) { - if (n == 0 || n == 1) { - return 1; - } else { - return n * factorial(n - 1); - } -} - -int main() { - int num = 5; - int result = factorial(num); - - std::cout << "The factorial of " << num << " is " << result << std::endl; - - return 0; -} diff --git a/test/json.json b/test/json.json deleted file mode 100644 index 7196507..0000000 --- a/test/json.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "example", - "version": "1.0.0", - "stuff": { - "stuff numero uno": "1", - "stuff numero dos": "2", - "stuff numero tres": "]]3" - } -} diff --git a/test/json.lua b/test/json.lua deleted file mode 100644 index 662abeb..0000000 --- a/test/json.lua +++ /dev/null @@ -1,2 +0,0 @@ ---/ Compiled using roblox-pyc | JSON compiler \-- -return {Contents = {["name"] = "example", ["version"] = "1.0.0", ["stuff"] = {["stuff numero uno"] = "1", ["stuff numero dos"] = "2", ["stuff numero tres"] = "]]3"}}, Type = 'json', Extension = 'json', SetSource = function(self, input) self.Contents = input end} \ No newline at end of file diff --git a/test/lunar/count.lua b/test/lunar/count.lua deleted file mode 100644 index 96ca6f5..0000000 --- a/test/lunar/count.lua +++ /dev/null @@ -1,43 +0,0 @@ -local count -count = function(current) - if current ~= nil then - if typeof(current == "number") then - if current > 0 then - print(current) - return count(current - 1) - else - if current == 0 then - return print(current) - else - print(current) - return count(current + 1) - end - end - elseif typeof(current == "table") then - if current.length > 0 then - print(current) - return count(current.slice(0, current.length - 1)) - else - return print(current) - end - else - print(current) - return count(current + 1) - end - else - return print("Next time, give me a number!") - end -end -count(0) -count(5) -count() -count(-1) -count(0) -count(1) -count(2.0) -count("hi") -return count({ - 1, - 2, - 3 -}) diff --git a/test/lunar/count.moon b/test/lunar/count.moon deleted file mode 100644 index ed31823..0000000 --- a/test/lunar/count.moon +++ /dev/null @@ -1,33 +0,0 @@ -count = (current)-> - if current != nil - if typeof current == "number" - if current > 0 - print current - count current-1 - else if current == 0 - print current - else - print current - count current+1 - elseif typeof current == "table" - if current.length > 0 - print current - count current.slice(0,current.length-1) - else - print current - else - print current - count current+1 - - else - print "Next time, give me a number!" - -count(0) -count 5 -count! -count -1 -count 0 -count 1 -count 2.0 -count "hi" -count {1,2,3} \ No newline at end of file diff --git a/test/lunar/customlib.lua b/test/lunar/customlib.lua deleted file mode 100644 index 22647ac..0000000 --- a/test/lunar/customlib.lua +++ /dev/null @@ -1,11 +0,0 @@ -local checker = type("string", "number", "boolean", "nil", "table", "function") -assert(checker("hello", 1, true, nil, { })) -assert(checker(1, 2, 3, 4, 5, 6)) -print(table.shuffle({ - 1, - 2, - 3, - 4, - 5 -})) -return nil diff --git a/test/lunar/customlib.moon b/test/lunar/customlib.moon deleted file mode 100644 index b69e292..0000000 --- a/test/lunar/customlib.moon +++ /dev/null @@ -1,13 +0,0 @@ --- Lunar has an extended table library, which is a superset of the standard Lua table library, use table. --- Lunar also has an extended type system, which is a superset of the standard Lua type system, use type. - --- TYPECHECKER AND EXTENDED TABLE LIBRARY ARE NOW DECREAPTED!!!!!!!!!!!!!!!!!! --- ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ -checker = type("string", "number", "boolean", "nil", "table", "function") - -assert(checker("hello", 1, true, nil, {})) -- Matches classes so no errors -assert(checker(1, 2, 3, 4, 5, 6)) -- Doesnt match classes so errors - -print table.shuffle({1, 2, 3, 4, 5}) - -nil -- last line is return value diff --git a/test/python/API.lua b/test/python/API.lua deleted file mode 100644 index 604f0fe..0000000 --- a/test/python/API.lua +++ /dev/null @@ -1,47 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local list = builtin.list -local id = builtin.id -local int = builtin.int -local print = builtin.print -local Spawn = builtin.Spawn -local Workspace = builtin.Workspace -local operator_in = builtin.operator_in -local min = builtin.min -local game = builtin.game - ------------------------------------------------------------------------------ -local examplelibrary = import("example") -local newSignal = import("signal", "new") -local ValidPlayers = list {"builderman"} -local function onTouch(touch) - print("Spawn has been touched by", touch.Name) -end -onTouch = py.Workspace.Spawn.Touched(onTouch) -local function onPlrAdd(plr) - if (operator_in(plr.Name, ValidPlayers)) then - print("Admin", plr.Name, "has joined the game!!") - end -end -onPlrAdd = py.Players.PlayerAdded(onPlrAdd) -examplelibrary() -local function onSignal() - print("Signal received!") -end -onSignal = newSignal(onSignal) -onSignal.Fire() - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/API.py b/test/python/API.py deleted file mode 100644 index 290fdfb..0000000 --- a/test/python/API.py +++ /dev/null @@ -1,23 +0,0 @@ -import example as examplelibrary -from signal import new as newSignal - -ValidPlayers = [ - "builderman" -] - -@py.Workspace.Spawn.Touched -def onTouch(touch): - print("Spawn has been touched by", touch.Name) - -@py.Players.PlayerAdded -def onPlrAdd(plr): - if plr.Name in ValidPlayers: - # The player is in our admin list, print admin joined - print("Admin", plr.Name, "has joined the game!!") - -examplelibrary() # -> Example Library! -@newSignal -def onSignal(): - print("Signal received!") - -onSignal.Fire() # -> Signal received! \ No newline at end of file diff --git a/test/python/ann.lua b/test/python/ann.lua deleted file mode 100644 index 2515b1e..0000000 --- a/test/python/ann.lua +++ /dev/null @@ -1,21 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - - ------------------------------------------------------------------------------ -local x = 10 -local y = 20 - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/ann.py b/test/python/ann.py deleted file mode 100644 index 4db8e71..0000000 --- a/test/python/ann.py +++ /dev/null @@ -1,3 +0,0 @@ -x: int = 10 -y: int = 20 - diff --git a/test/python/asyncdef.lua b/test/python/asyncdef.lua deleted file mode 100644 index d15cdcc..0000000 --- a/test/python/asyncdef.lua +++ /dev/null @@ -1,25 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local asynchronousfunction = builtin.asynchronousfunction -local int = builtin.int -local print = builtin.print - ------------------------------------------------------------------------------ -local hello = asynchronousfunction(function() - print("Hello, world!") -end) - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/asyncdef.py b/test/python/asyncdef.py deleted file mode 100644 index 3d4533f..0000000 --- a/test/python/asyncdef.py +++ /dev/null @@ -1,3 +0,0 @@ -async def hello(): - print("Hello, world!") - diff --git a/test/python/asyncwith.lua b/test/python/asyncwith.lua deleted file mode 100644 index 65dcb83..0000000 --- a/test/python/asyncwith.lua +++ /dev/null @@ -1,31 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local asynchronousfunction = builtin.asynchronousfunction -local open = builtin.open -local coroutine = builtin.coroutine -local int = builtin.int -local print = builtin.print - ------------------------------------------------------------------------------ -local async_with = asynchronousfunction(function() - do - local f = open("test.txt") - local contents = coroutine.yield(f.read()) - print(contents) - end -end) - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/asyncwith.py b/test/python/asyncwith.py deleted file mode 100644 index b5f3c80..0000000 --- a/test/python/asyncwith.py +++ /dev/null @@ -1,4 +0,0 @@ -async def async_with(): - with open('test.txt') as f: - contents = await f.read() - print(contents) \ No newline at end of file diff --git a/test/python/bytes.lua b/test/python/bytes.lua deleted file mode 100644 index ad4349f..0000000 --- a/test/python/bytes.lua +++ /dev/null @@ -1,23 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local int = builtin.int -local print = builtin.print - ------------------------------------------------------------------------------ -local byte = 'Hello World' -print(byte) - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/bytes.py b/test/python/bytes.py deleted file mode 100644 index 4d9f2fb..0000000 --- a/test/python/bytes.py +++ /dev/null @@ -1,2 +0,0 @@ -byte = b"Hello World" -print(byte) \ No newline at end of file diff --git a/test/python/class.lua b/test/python/class.lua deleted file mode 100644 index 56aee78..0000000 --- a/test/python/class.lua +++ /dev/null @@ -1,48 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local class = builtin.class -local int = builtin.int -local print = builtin.print -local Game = builtin.Game - ------------------------------------------------------------------------------ -local Example = class(function(Example) - function Example.__init__(self, name) - self.name = name - end - function Example.print_name(self) - print(self.name) - end - function Example.sethobby(self, hobby) - self.hobby = hobby - end - function Example.printhobby(self) - print(self.hobby) - end - function Example.setage(self, age) - self.age = age - end - function Example.printage(self) - print(self.age) - end - return Example -end, {}) -local new = Example("John") -new.print_name() -new.sethobby("Roblox Game Development") -new.printhobby() - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/class.py b/test/python/class.py deleted file mode 100644 index 0591a15..0000000 --- a/test/python/class.py +++ /dev/null @@ -1,24 +0,0 @@ -class Example: - def __init__(self, name): - self.name = name - - def print_name(self): - print(self.name) - - def sethobby(self, hobby): - self.hobby = hobby - - def printhobby(self): - print(self.hobby) - - def setage(self, age): - self.age = age - - def printage(self): - print(self.age) - - -new = Example("John") -new.print_name() -new.sethobby("Roblox Game Development") -new.printhobby() \ No newline at end of file diff --git a/test/python/continue.lua b/test/python/continue.lua deleted file mode 100644 index 4efd820..0000000 --- a/test/python/continue.lua +++ /dev/null @@ -1,25 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local range = builtin.range - ------------------------------------------------------------------------------ -for i in range(10) do - if (i == 5) then - continue - end -end - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/continue.py b/test/python/continue.py deleted file mode 100644 index 48f5359..0000000 --- a/test/python/continue.py +++ /dev/null @@ -1,6 +0,0 @@ -# continue tests - -for i in range(10): - if i == 5: - continue - diff --git a/test/python/dict.lua b/test/python/dict.lua deleted file mode 100644 index 575928c..0000000 --- a/test/python/dict.lua +++ /dev/null @@ -1,30 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local dict = builtin.dict -local int = builtin.int -local print = builtin.print - ------------------------------------------------------------------------------ -local newdict = dict {} -newdict["one"] = 1 -newdict["two"] = 2 -newdict["three"] = 3 -newdict["four"] = 4 -for key in newdict do - print(key, newdict[key]) -end - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/dict.py b/test/python/dict.py deleted file mode 100644 index a65ebf2..0000000 --- a/test/python/dict.py +++ /dev/null @@ -1,8 +0,0 @@ -newdict = {} -newdict['one'] = 1 -newdict['two'] = 2 -newdict['three'] = 3 -newdict['four'] = 4 - -for key in newdict: - print(key, newdict[key]) \ No newline at end of file diff --git a/test/python/doublestar.lua b/test/python/doublestar.lua deleted file mode 100644 index 5fedad7..0000000 --- a/test/python/doublestar.lua +++ /dev/null @@ -1,23 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local dict = builtin.dict - ------------------------------------------------------------------------------ -local x = dict {["a"] = "b", ["c"] = "d"} -local a = "Hello, World!" -a.b() - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/doublestar.py b/test/python/doublestar.py deleted file mode 100644 index 9ab8cf4..0000000 --- a/test/python/doublestar.py +++ /dev/null @@ -1,9 +0,0 @@ -# Use ** in this python script - -# Path: test/python/doublestar.py - -x = {"a": "b", "c": "d"} -a = "Hello, World!" -a.b(**x) - -# This is an imaginary API, but it's just to show that the compiler works \ No newline at end of file diff --git a/test/python/formatusing%.lua b/test/python/formatusing%.lua deleted file mode 100644 index c8d8ac4..0000000 --- a/test/python/formatusing%.lua +++ /dev/null @@ -1,25 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local format = builtin.format -local formatmod = builtin.formatmod -local int = builtin.int -local print = builtin.print - ------------------------------------------------------------------------------ -local text = (formatmod("%s is my name", "John")) -print(text) - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/formatusing%.py b/test/python/formatusing%.py deleted file mode 100644 index cf8930f..0000000 --- a/test/python/formatusing%.py +++ /dev/null @@ -1,3 +0,0 @@ -text = "%s is my name" % "John" - -print(text) \ No newline at end of file diff --git a/test/python/helloworld.lua b/test/python/helloworld.lua deleted file mode 100644 index 802ec1a..0000000 --- a/test/python/helloworld.lua +++ /dev/null @@ -1,22 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local int = builtin.int -local print = builtin.print - ------------------------------------------------------------------------------ -print("Hello World!") - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/helloworld.py b/test/python/helloworld.py deleted file mode 100644 index 1dc45ac..0000000 --- a/test/python/helloworld.py +++ /dev/null @@ -1 +0,0 @@ -print("Hello World!") \ No newline at end of file diff --git a/test/python/import.lua b/test/python/import.lua deleted file mode 100644 index 78eca94..0000000 --- a/test/python/import.lua +++ /dev/null @@ -1,26 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - - ------------------------------------------------------------------------------ -local examplelib = import("examplelib") -local submodule = import("examplelib.submodule") -local mysubsubmodule = import("examplelib.submodule.subsubmodule") -local submodule = import("examplefromlib", "submodule") -local mysubsubmodule = import("examplefromlib.submodule", "mysubsubmodule") -local mysubmodule2 = import("examplefromlib", "submodule") -local mysubsubmodule3 = import("examplefromlib.submodule", "subsubmodule") - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/import.py b/test/python/import.py deleted file mode 100644 index ae129f6..0000000 --- a/test/python/import.py +++ /dev/null @@ -1,8 +0,0 @@ -import examplelib -import examplelib.submodule -import examplelib.submodule.subsubmodule as mysubsubmodule -from examplefromlib import submodule -from examplefromlib.submodule import mysubsubmodule -from examplefromlib import submodule as mysubmodule2 -from examplefromlib.submodule import subsubmodule as mysubsubmodule3 - diff --git a/test/python/importall.lua b/test/python/importall.lua deleted file mode 100644 index 759d3b6..0000000 --- a/test/python/importall.lua +++ /dev/null @@ -1,22 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - - ------------------------------------------------------------------------------ -local newspring = import("spring", "new") -local b = import("a") -local newSpring = newspring() - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/importall.py b/test/python/importall.py deleted file mode 100644 index b28b024..0000000 --- a/test/python/importall.py +++ /dev/null @@ -1,4 +0,0 @@ -from spring import new as newspring -import a as b - -newSpring = newspring() diff --git a/test/python/in.lua b/test/python/in.lua deleted file mode 100644 index 1091c91..0000000 --- a/test/python/in.lua +++ /dev/null @@ -1,28 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local dict = builtin.dict -local table = builtin.table -local operator_in = builtin.operator_in -local int = builtin.int -local print = builtin.print - ------------------------------------------------------------------------------ -local table = dict {["a"] = "b", ["c"] = "d"} -if (operator_in("a", table)) then - print("a is present in table") -end - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/in.py b/test/python/in.py deleted file mode 100644 index e9c7434..0000000 --- a/test/python/in.py +++ /dev/null @@ -1,4 +0,0 @@ -table = {"a": "b", "c": "d"} - -if "a" in table: - print("a is present in table") \ No newline at end of file diff --git a/test/python/lambda.lua b/test/python/lambda.lua deleted file mode 100644 index e4ba9b8..0000000 --- a/test/python/lambda.lua +++ /dev/null @@ -1,25 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local safeadd = builtin.safeadd -local bit32 = builtin.bit32 -local int = builtin.int -local print = builtin.print - ------------------------------------------------------------------------------ -local x = function(a) return (bit32.bxor((bit32.bxor((safeadd(a, 10)), 2)), a)) end -print(x(5)) - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/lambda.py b/test/python/lambda.py deleted file mode 100644 index 4b65f93..0000000 --- a/test/python/lambda.py +++ /dev/null @@ -1,2 +0,0 @@ -x = lambda a : a + 10^2^a -print(x(5)) \ No newline at end of file diff --git a/test/python/list.lua b/test/python/list.lua deleted file mode 100644 index 88918d2..0000000 --- a/test/python/list.lua +++ /dev/null @@ -1,33 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local list = builtin.list -local int = builtin.int -local print = builtin.print - ------------------------------------------------------------------------------ -local newlist = list {1, 2, 3, 4, 5} -newlist.append(6) -newlist.append(7) -newlist.append(8) -for item in newlist do - print(item) -end -print(newlist[0]) -print(newlist[1]) -newlist.sort() -newlist.reverse() - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/list.py b/test/python/list.py deleted file mode 100644 index 9df2661..0000000 --- a/test/python/list.py +++ /dev/null @@ -1,13 +0,0 @@ -newlist = [1, 2, 3, 4, 5] -newlist.append(6) -newlist.append(7) -newlist.append(8) - -for item in newlist: - print(item) - -print(newlist[0]) -print(newlist[1]) - -newlist.sort() -newlist.reverse() \ No newline at end of file diff --git a/test/python/luaxpy.lua b/test/python/luaxpy.lua deleted file mode 100644 index 2a1aec8..0000000 --- a/test/python/luaxpy.lua +++ /dev/null @@ -1,26 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local python = builtin.python -local int = builtin.int -local print = builtin.print - ------------------------------------------------------------------------------ -print("This is from python!") - -print("This is from lua!") - - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/luaxpy.py b/test/python/luaxpy.py deleted file mode 100644 index f81e31e..0000000 --- a/test/python/luaxpy.py +++ /dev/null @@ -1,4 +0,0 @@ -print("This is from python!") -"""[[lua]] -print("This is from lua!") -""" \ No newline at end of file diff --git a/test/python/match.lua b/test/python/match.lua deleted file mode 100644 index ab104be..0000000 --- a/test/python/match.lua +++ /dev/null @@ -1,34 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local match = builtin.match -local int = builtin.int -local print = builtin.print - ------------------------------------------------------------------------------ -local x = "10" -match(x, { -[10] = function() - print("x is 10") -end, -[20] = function() - print("x is 20") -end, -["default"] = function() - print("x is not 10 or 20") -end, -}) - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/match.py b/test/python/match.py deleted file mode 100644 index 80970c4..0000000 --- a/test/python/match.py +++ /dev/null @@ -1,11 +0,0 @@ -x = "10" - -match x: - case "10": - print("x is 10") - case "20": - print("x is 20") - case _: - print("x is not 10 or 20") - - diff --git a/test/python/matrix.lua b/test/python/matrix.lua deleted file mode 100644 index 058f81c..0000000 --- a/test/python/matrix.lua +++ /dev/null @@ -1,34 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local list = builtin.list -local range = builtin.range -local len = builtin.len -local int = builtin.int -local print = builtin.print - ------------------------------------------------------------------------------ -local X = list {list {12, 7}, list {4, 5}, list {3, 8}} -local result = list {list {0, 0, 0}, list {0, 0, 0}} -for i in range(len(X)) do - for j in range(len(X[0])) do - result[j][i] = X[i][j] - end -end -for r in result do - print(r) -end - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/matrix.py b/test/python/matrix.py deleted file mode 100644 index 8d1ae9c..0000000 --- a/test/python/matrix.py +++ /dev/null @@ -1,14 +0,0 @@ - -X = [[12,7], - [4 ,5], - [3 ,8]] - -result = [[0,0,0], - [0,0,0]] - -for i in range(len(X)): - for j in range(len(X[0])): - result[j][i] = X[i][j] - -for r in result: - print(r) diff --git a/test/python/memoryaddress.lua b/test/python/memoryaddress.lua deleted file mode 100644 index 33f6f0b..0000000 --- a/test/python/memoryaddress.lua +++ /dev/null @@ -1,25 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local id = builtin.id -local int = builtin.int -local print = builtin.print - ------------------------------------------------------------------------------ -local item = "Hello!" -local location = id(item) -print(location) - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/memoryaddress.py b/test/python/memoryaddress.py deleted file mode 100644 index c350633..0000000 --- a/test/python/memoryaddress.py +++ /dev/null @@ -1,4 +0,0 @@ -item = "Hello!" -location = id(item) - -print(location) \ No newline at end of file diff --git a/test/python/multiassign.lua b/test/python/multiassign.lua deleted file mode 100644 index c33a0df..0000000 --- a/test/python/multiassign.lua +++ /dev/null @@ -1,23 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local int = builtin.int -local print = builtin.print - ------------------------------------------------------------------------------ -local a, b, c = 1, 2, 3 -print(a, b, c) - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/multiassign.py b/test/python/multiassign.py deleted file mode 100644 index 26c8937..0000000 --- a/test/python/multiassign.py +++ /dev/null @@ -1,2 +0,0 @@ -a,b,c = 1,2,3 -print(a,b,c) \ No newline at end of file diff --git a/test/python/slice.lua b/test/python/slice.lua deleted file mode 100644 index 2699717..0000000 --- a/test/python/slice.lua +++ /dev/null @@ -1,26 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local str = builtin.str -local string = builtin.string -local int = builtin.int -local slice = builtin.slice -local print = builtin.print - ------------------------------------------------------------------------------ -local string = "Hello World" -print(slice(string, 0, 5)) - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/slice.py b/test/python/slice.py deleted file mode 100644 index 6f732ca..0000000 --- a/test/python/slice.py +++ /dev/null @@ -1,3 +0,0 @@ -string = "Hello World" -#print(string[0:5]) # Not enabled -print(slice(string, 0, 5)) \ No newline at end of file diff --git a/test/python/staticmethod.lua b/test/python/staticmethod.lua deleted file mode 100644 index 55ede76..0000000 --- a/test/python/staticmethod.lua +++ /dev/null @@ -1,36 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local int = builtin.int -local format = builtin.format -local exec = builtin.exec -local formatmod = builtin.formatmod -local print = builtin.print -local staticmethod = builtin.staticmethod -local class = builtin.class - ------------------------------------------------------------------------------ -local function foo(x) - print((formatmod("executing foo(%s)", x))) -end -foo = staticmethod(foo) -local A = class(function(A) - A.foo = foo - return A -end, {}) -local a = A() -a.foo("hi") - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/staticmethod.py b/test/python/staticmethod.py deleted file mode 100644 index b62d553..0000000 --- a/test/python/staticmethod.py +++ /dev/null @@ -1,9 +0,0 @@ -@staticmethod -def foo(x): - print("executing foo(%s)" % (x)) - -class A: - foo = foo - -a = A() -a.foo('hi') \ No newline at end of file diff --git a/test/python/string.lua b/test/python/string.lua deleted file mode 100644 index 51b0bd3..0000000 --- a/test/python/string.lua +++ /dev/null @@ -1,26 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local str = builtin.str -local string = builtin.string -local safeadd = builtin.safeadd -local int = builtin.int -local print = builtin.print - ------------------------------------------------------------------------------ -local newstring = "Hello World" -print((safeadd(newstring, "Hi"))) - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/string.py b/test/python/string.py deleted file mode 100644 index a149800..0000000 --- a/test/python/string.py +++ /dev/null @@ -1,2 +0,0 @@ -newstring = 'Hello World' -print(newstring+"Hi") \ No newline at end of file diff --git a/test/python/try.lua b/test/python/try.lua deleted file mode 100644 index c5e9fb6..0000000 --- a/test/python/try.lua +++ /dev/null @@ -1,25 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local all = builtin.all -local pcall = builtin.pcall -local error = builtin.error - ------------------------------------------------------------------------------ -local success, result = pcall(function() - error("Some error") -end) - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/try.py b/test/python/try.py deleted file mode 100644 index 8274b43..0000000 --- a/test/python/try.py +++ /dev/null @@ -1,4 +0,0 @@ -try: - raise "Some error" -finally: - print("This is from finally") \ No newline at end of file diff --git a/test/python/typehints.lua b/test/python/typehints.lua deleted file mode 100644 index ab58144..0000000 --- a/test/python/typehints.lua +++ /dev/null @@ -1,29 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local sum = builtin.sum -local safeadd = builtin.safeadd -local int = builtin.int -local print = builtin.print - ------------------------------------------------------------------------------ -local function sum_numbers(a, b) - return (safeadd(a, b)) -end -print(sum_numbers(10, 5)) -print(sum_numbers(10.3, 5)) -print(sum_numbers("Bob", "Mark")) - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/typehints.py b/test/python/typehints.py deleted file mode 100644 index 95f36ac..0000000 --- a/test/python/typehints.py +++ /dev/null @@ -1,9 +0,0 @@ -# TODO: Add compiler support for type hints, in functions and async functions. - - -def sum_numbers(a: int, b: int) -> int: - return a + b - -print(sum_numbers(10, 5)) -print(sum_numbers(10.3, 5)) -print(sum_numbers('Bob', 'Mark')) \ No newline at end of file diff --git a/test/python/with.lua b/test/python/with.lua deleted file mode 100644 index 9f38e42..0000000 --- a/test/python/with.lua +++ /dev/null @@ -1,26 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local id = builtin.id -local int = builtin.int -local print = builtin.print - ------------------------------------------------------------------------------ -do - local id = id("Text") - print(id) -end - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/with.py b/test/python/with.py deleted file mode 100644 index 48fa474..0000000 --- a/test/python/with.py +++ /dev/null @@ -1,2 +0,0 @@ -with id("Text") as id: - print(id) \ No newline at end of file diff --git a/test/python/xl.lua b/test/python/xl.lua deleted file mode 100644 index f6c6552..0000000 --- a/test/python/xl.lua +++ /dev/null @@ -1,94 +0,0 @@ ---/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -local python = builtin.python -local all = builtin.all -local compile = builtin.compile -local script = builtin.script -local range = builtin.range -local int = builtin.int -local print = builtin.print -local open = builtin.open -local id = builtin.id -local asynchronousfunction = builtin.asynchronousfunction -local coroutine = builtin.coroutine -local dict = builtin.dict -local class = builtin.class -local staticmethod = builtin.staticmethod -local str = builtin.str -local string = builtin.string -local operator_in = builtin.operator_in - ------------------------------------------------------------------------------ ---[[ -XL.py (Stands for Extralarge) - -A test script for the roblox-pyc compiler that uses all of the python 3.13 features. - ]] -local y = import("x") -local SSS = py.services.ServerScriptService -for i in range(10) do - print(i) -end -while true do - print("This is an infinite loop") - break -end -do - local f = open("test.txt", "w") - f.write("This is a test file") -end -local function test() - print("This is a function, ran inside of the async function") -end -local async_test = asynchronousfunction(function() - print("This is an async function") - coroutine.yield(test()) -end) -async_test() -local data = dict {["test"] = "This is a test"} -for i in data do - print(i) -end -local Test = class(function(Test) - function Test.__init__(self) - print("This is a class") - end - function Test.test(self) - print("This is a class method") - end - function Test.static_test() - print("This is a static method") - end - Test.static_test = staticmethod(Test.static_test) - function Test.class_test(cls) - print("This is a class method") - end - Test.class_test = classmethod(Test.class_test) - return Test -end, {}) -local new_test = Test() -new_test.test() -new_test.static_test() -new_test.class_test() -local string = "This" -local string2 = "This is a test string" -if (not operator_in(string, string2)) then - print("x is not in y") -else - print("x is in y") -end - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - diff --git a/test/python/xl.py b/test/python/xl.py deleted file mode 100644 index 67ee62d..0000000 --- a/test/python/xl.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -XL.py (Stands for Extralarge) - -A test script for the roblox-pyc compiler that uses all of the python 3.13 features. -""" - -# Imports -import x as y - -SSS = py.services.ServerScriptService - -for i in range(10): - print(i) - -while True: - print("This is an infinite loop") - break - -with open("test.txt", "w") as f: - f.write("This is a test file") - -def test(): - print("This is a function, ran inside of the async function") - -async def async_test(): - print("This is an async function") - await test() - -async_test() - -data = { - "test": "This is a test" -} - -for i in data: - print(i) - -class Test: - def __init__(self): - print("This is a class") - - def test(self): - print("This is a class method") - - @staticmethod - def static_test(): - print("This is a static method") - - @classmethod - def class_test(cls): - print("This is a class method") - -new_test = Test() -new_test.test() -new_test.static_test() -new_test.class_test() - -string = "This" -string2 = "This is a test string" - -if string not in string2: - print("x is not in y") -else: - print("x is in y") From cc972bd58ce82289000f26eb7076e88d9bf8a6bd Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sun, 20 Aug 2023 18:41:55 -0500 Subject: [PATCH 03/77] Remove LuauAST Engine --- src/LuauAST/README.md | 28 --- src/LuauAST/bundle.ts | 22 -- src/LuauAST/impl/List.ts | 212 ------------------ src/LuauAST/impl/create.ts | 184 --------------- src/LuauAST/impl/enums.ts | 57 ----- src/LuauAST/impl/globals.ts | 68 ------ src/LuauAST/impl/strings.ts | 21 -- src/LuauAST/impl/typeGuards.ts | 144 ------------ src/LuauAST/index.ts | 4 - src/LuauAST/types/mapping.ts | 71 ------ src/LuauAST/types/nodes.ts | 209 ----------------- src/LuauAST/types/operators.ts | 20 -- src/LuauAST/util/assert.ts | 18 -- src/LuauAST/util/getKindName.ts | 12 - src/LuauAST/util/isMetamethod.ts | 26 --- src/LuauAST/util/isReservedClassField.ts | 11 - src/LuauAST/util/isReservedIdentifier.ts | 5 - src/LuauAST/util/isValidIdentifier.ts | 31 --- src/LuauAST/util/isValidNumberLiteral.ts | 7 - src/LuauRenderer/README.md | 13 -- src/LuauRenderer/RenderState.ts | 107 --------- src/LuauRenderer/index.ts | 4 - .../indexable/renderCallExpression.ts | 7 - .../renderComputedIndexExpression.ts | 12 - .../expressions/indexable/renderIdentifier.ts | 8 - .../indexable/renderMethodCallExpression.ts | 9 - .../renderParenthesizedExpression.ts | 15 -- .../renderPropertyAccessExpression.ts | 12 - .../indexable/renderTemporaryIdentifier.ts | 9 - .../nodes/expressions/renderArray.ts | 11 - .../expressions/renderBinaryExpression.ts | 13 -- .../expressions/renderFunctionExpression.ts | 16 -- .../nodes/expressions/renderIfExpression.ts | 23 -- .../nodes/expressions/renderMap.ts | 15 -- .../nodes/expressions/renderMixedTable.ts | 16 -- .../nodes/expressions/renderNumberLiteral.ts | 6 - .../nodes/expressions/renderSet.ts | 15 -- .../nodes/expressions/renderStringLiteral.ts | 37 --- .../expressions/renderUnaryExpression.ts | 48 ---- .../nodes/fields/renderMapField.ts | 13 -- .../nodes/statements/renderAssignment.ts | 23 -- .../nodes/statements/renderBreakStatement.ts | 5 - .../nodes/statements/renderCallStatement.ts | 6 - .../nodes/statements/renderComment.ts | 16 -- .../statements/renderContinueStatement.ts | 5 - .../nodes/statements/renderDoStatement.ts | 11 - .../nodes/statements/renderForStatement.ts | 15 -- .../statements/renderFunctionDeclaration.ts | 19 -- .../nodes/statements/renderIfStatement.ts | 28 --- .../statements/renderMethodDeclaration.ts | 12 - .../statements/renderNumericForStatement.ts | 24 -- .../nodes/statements/renderRepeatStatement.ts | 11 - .../nodes/statements/renderReturnStatement.ts | 9 - .../statements/renderVariableDeclaration.ts | 26 --- .../nodes/statements/renderWhileStatement.ts | 11 - src/LuauRenderer/render.ts | 135 ----------- src/LuauRenderer/solveTempIds.ts | 134 ----------- src/LuauRenderer/util/getEnding.ts | 101 --------- src/LuauRenderer/util/getOrSetDefault.ts | 14 -- src/LuauRenderer/util/getSafeBracketEquals.ts | 7 - src/LuauRenderer/util/identity.ts | 3 - src/LuauRenderer/util/needsParentheses.ts | 65 ------ src/LuauRenderer/util/renderArguments.ts | 7 - src/LuauRenderer/util/renderParameters.ts | 15 -- src/LuauRenderer/util/renderStatements.ts | 30 --- src/LuauRenderer/util/visit.ts | 162 ------------- src/__communication__/README.md | 3 - src/__communication__/cfg.pkl | Bin 98 -> 0 bytes src/__communication__/ts.json | 0 69 files changed, 2456 deletions(-) delete mode 100644 src/LuauAST/README.md delete mode 100644 src/LuauAST/bundle.ts delete mode 100644 src/LuauAST/impl/List.ts delete mode 100644 src/LuauAST/impl/create.ts delete mode 100644 src/LuauAST/impl/enums.ts delete mode 100644 src/LuauAST/impl/globals.ts delete mode 100644 src/LuauAST/impl/strings.ts delete mode 100644 src/LuauAST/impl/typeGuards.ts delete mode 100644 src/LuauAST/index.ts delete mode 100644 src/LuauAST/types/mapping.ts delete mode 100644 src/LuauAST/types/nodes.ts delete mode 100644 src/LuauAST/types/operators.ts delete mode 100644 src/LuauAST/util/assert.ts delete mode 100644 src/LuauAST/util/getKindName.ts delete mode 100644 src/LuauAST/util/isMetamethod.ts delete mode 100644 src/LuauAST/util/isReservedClassField.ts delete mode 100644 src/LuauAST/util/isReservedIdentifier.ts delete mode 100644 src/LuauAST/util/isValidIdentifier.ts delete mode 100644 src/LuauAST/util/isValidNumberLiteral.ts delete mode 100644 src/LuauRenderer/README.md delete mode 100644 src/LuauRenderer/RenderState.ts delete mode 100644 src/LuauRenderer/index.ts delete mode 100644 src/LuauRenderer/nodes/expressions/indexable/renderCallExpression.ts delete mode 100644 src/LuauRenderer/nodes/expressions/indexable/renderComputedIndexExpression.ts delete mode 100644 src/LuauRenderer/nodes/expressions/indexable/renderIdentifier.ts delete mode 100644 src/LuauRenderer/nodes/expressions/indexable/renderMethodCallExpression.ts delete mode 100644 src/LuauRenderer/nodes/expressions/indexable/renderParenthesizedExpression.ts delete mode 100644 src/LuauRenderer/nodes/expressions/indexable/renderPropertyAccessExpression.ts delete mode 100644 src/LuauRenderer/nodes/expressions/indexable/renderTemporaryIdentifier.ts delete mode 100644 src/LuauRenderer/nodes/expressions/renderArray.ts delete mode 100644 src/LuauRenderer/nodes/expressions/renderBinaryExpression.ts delete mode 100644 src/LuauRenderer/nodes/expressions/renderFunctionExpression.ts delete mode 100644 src/LuauRenderer/nodes/expressions/renderIfExpression.ts delete mode 100644 src/LuauRenderer/nodes/expressions/renderMap.ts delete mode 100644 src/LuauRenderer/nodes/expressions/renderMixedTable.ts delete mode 100644 src/LuauRenderer/nodes/expressions/renderNumberLiteral.ts delete mode 100644 src/LuauRenderer/nodes/expressions/renderSet.ts delete mode 100644 src/LuauRenderer/nodes/expressions/renderStringLiteral.ts delete mode 100644 src/LuauRenderer/nodes/expressions/renderUnaryExpression.ts delete mode 100644 src/LuauRenderer/nodes/fields/renderMapField.ts delete mode 100644 src/LuauRenderer/nodes/statements/renderAssignment.ts delete mode 100644 src/LuauRenderer/nodes/statements/renderBreakStatement.ts delete mode 100644 src/LuauRenderer/nodes/statements/renderCallStatement.ts delete mode 100644 src/LuauRenderer/nodes/statements/renderComment.ts delete mode 100644 src/LuauRenderer/nodes/statements/renderContinueStatement.ts delete mode 100644 src/LuauRenderer/nodes/statements/renderDoStatement.ts delete mode 100644 src/LuauRenderer/nodes/statements/renderForStatement.ts delete mode 100644 src/LuauRenderer/nodes/statements/renderFunctionDeclaration.ts delete mode 100644 src/LuauRenderer/nodes/statements/renderIfStatement.ts delete mode 100644 src/LuauRenderer/nodes/statements/renderMethodDeclaration.ts delete mode 100644 src/LuauRenderer/nodes/statements/renderNumericForStatement.ts delete mode 100644 src/LuauRenderer/nodes/statements/renderRepeatStatement.ts delete mode 100644 src/LuauRenderer/nodes/statements/renderReturnStatement.ts delete mode 100644 src/LuauRenderer/nodes/statements/renderVariableDeclaration.ts delete mode 100644 src/LuauRenderer/nodes/statements/renderWhileStatement.ts delete mode 100644 src/LuauRenderer/render.ts delete mode 100644 src/LuauRenderer/solveTempIds.ts delete mode 100644 src/LuauRenderer/util/getEnding.ts delete mode 100644 src/LuauRenderer/util/getOrSetDefault.ts delete mode 100644 src/LuauRenderer/util/getSafeBracketEquals.ts delete mode 100644 src/LuauRenderer/util/identity.ts delete mode 100644 src/LuauRenderer/util/needsParentheses.ts delete mode 100644 src/LuauRenderer/util/renderArguments.ts delete mode 100644 src/LuauRenderer/util/renderParameters.ts delete mode 100644 src/LuauRenderer/util/renderStatements.ts delete mode 100644 src/LuauRenderer/util/visit.ts delete mode 100644 src/__communication__/README.md delete mode 100644 src/__communication__/cfg.pkl delete mode 100644 src/__communication__/ts.json diff --git a/src/LuauAST/README.md b/src/LuauAST/README.md deleted file mode 100644 index 24c5105..0000000 --- a/src/LuauAST/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# roblox-ts LuauAST - -## Structure - -**index.ts** - re-exports all exported values in each file - -**types/enums.ts** - enums for luau.SyntaxKind, luau.BinaryOperator, luau.UnaryOperator - -**types/nodes.ts** - contains interfaces that describe each node - -**impl/mapping.ts** - contains interfaces to describe the mapping of each node to IndexableExpression, Expression, Statement, and Field - -**impl/create.ts** - helper functions for creating nodes - -**impl/traversal.ts** - helper functions for traversing nodes - -**impl/typeGuards.ts** - helper functions for determining what a particular node is - -**impl/List.ts** - types + helper functions for luau.List and luau.ListNode - -## Adding a new node - -In order to add a new type of node, you must add a new: - -1. enum to luau.SyntaxKind in enums.ts -2. interface to nodes.ts that describes the node -3. field in mapping.ts for what kind of node it is -4. typeGuard using makeGuard in typeGuards.ts AND add to specific generic guard Set diff --git a/src/LuauAST/bundle.ts b/src/LuauAST/bundle.ts deleted file mode 100644 index 84fe503..0000000 --- a/src/LuauAST/bundle.ts +++ /dev/null @@ -1,22 +0,0 @@ -// types -export * from "../LuauAST/types/mapping"; -export * from "../LuauAST/types/nodes"; -export * from "../LuauAST/types/operators"; - -// impls -export * from "../LuauAST/impl/create"; -export * from "../LuauAST/impl/enums"; -export * from "../LuauAST/impl/List"; -export * from "../LuauAST/impl/typeGuards"; - -// util -export * from "../LuauAST/util/getKindName"; -export * from "../LuauAST/util/isMetamethod"; -export * from "../LuauAST/util/isReservedClassField"; -export * from "../LuauAST/util/isReservedIdentifier"; -export * from "../LuauAST/util/isValidIdentifier"; -export * from "../LuauAST/util/isValidNumberLiteral"; - -// depends on above files -export * from "../LuauAST/impl/globals"; -export * from "../LuauAST/impl/strings"; diff --git a/src/LuauAST/impl/List.ts b/src/LuauAST/impl/List.ts deleted file mode 100644 index 39bb3d4..0000000 --- a/src/LuauAST/impl/List.ts +++ /dev/null @@ -1,212 +0,0 @@ -import luau from "../../LuauAST"; -import { assert } from "../../LuauAST/util/assert"; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unnecessary-type-constraint -export type NoInfer = [A][A extends any ? 0 : never]; - -const LIST_MARKER = Symbol("List"); - -export type ListNode = { - prev?: luau.ListNode; - next?: luau.ListNode; - value: T; -}; - -export type List = { - [LIST_MARKER]: true; - head?: luau.ListNode; - tail?: luau.ListNode; - readonly: boolean; -}; - -export namespace list { - // list creation functions - - export function makeNode(value: T): luau.ListNode { - assert(luau.isNode(value)); - return { value }; - } - - export function make(...values: Array): luau.List { - assert(values.every(node => luau.isNode(node))); - if (values.length > 0) { - const head = luau.list.makeNode(values[0]); - let tail = head; - for (let i = 1; i < values.length; i++) { - const node = luau.list.makeNode(values[i]); - tail.next = node; - node.prev = tail; - tail = node; - } - return { [LIST_MARKER]: true, head, tail, readonly: false }; - } else { - return { [LIST_MARKER]: true, readonly: false }; - } - } - - // type guard - - export function isList(value: unknown): value is luau.List { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return typeof value === "object" && (value as any)[LIST_MARKER] === true; - } - - // list utility functions - - export function clone(list: luau.List): luau.List { - const newList = luau.list.make(); - luau.list.forEach(list, element => { - luau.list.push(newList, { ...element }); - }); - return newList; - } - - export function push(list: luau.List, value: NoInfer) { - assert(luau.isNode(value)); - assert(!list.readonly); - const node = luau.list.makeNode(value); - if (list.tail) { - list.tail.next = node; - node.prev = list.tail; - } else { - list.head = node; - } - list.tail = node; - } - - export function pushList(list: luau.List, other: luau.List) { - assert(!list.readonly); - assert(!other.readonly); - other.readonly = true; - - if (other.head && other.tail) { - if (list.head && list.tail) { - list.tail.next = other.head; - other.head.prev = list.tail; - list.tail = other.tail; - } else { - list.head = other.head; - list.tail = other.tail; - } - } - } - - export function shift(list: luau.List): T | undefined { - assert(!list.readonly); - if (list.head) { - const head = list.head; - if (head.next) { - list.head = head.next; - head.next.prev = undefined; - } else { - list.tail = undefined; - list.head = undefined; - } - return head.value; - } - } - - export function unshift(list: luau.List, value: NoInfer) { - assert(luau.isNode(value)); - assert(!list.readonly); - const node = luau.list.makeNode(value); - if (list.head) { - list.head.prev = node; - node.next = list.head; - } else { - list.tail = node; - } - list.head = node; - } - - export function unshiftList(list: luau.List, other: luau.List) { - assert(!list.readonly); - assert(!other.readonly); - other.readonly = true; - - if (other.head && other.tail) { - if (list.head && list.tail) { - list.head.prev = other.tail; - other.tail.next = list.head; - list.head = other.head; - } else { - list.head = other.head; - list.tail = other.tail; - } - } - } - - export function isEmpty(list: luau.List) { - return list.head === undefined; - } - - export function isNonEmpty(list: luau.List): list is Required> { - return list.head !== undefined; - } - - export function forEach(list: luau.List, callback: (value: NoInfer) => void) { - let node = list.head; - while (node) { - callback(node.value); - node = node.next; - } - } - - export function forEachListNode( - list: luau.List, - callback: (value: luau.ListNode) => void, - ) { - let node = list.head; - while (node) { - callback(node); - node = node.next; - } - } - - export function mapToArray( - list: luau.List, - callback: (value: NoInfer) => U, - ): Array { - const result = new Array(); - luau.list.forEach(list, value => result.push(callback(value))); - return result; - } - - export function toArray(list: luau.List): Array { - const result = new Array(); - luau.list.forEach(list, value => result.push(value)); - return result; - } - - export function every(list: luau.List, callback: (value: NoInfer) => boolean) { - let node = list.head; - while (node) { - if (!callback(node.value)) { - return false; - } - node = node.next; - } - return true; - } - - export function some(list: luau.List, callback: (value: NoInfer) => boolean) { - let node = list.head; - while (node) { - if (callback(node.value)) { - return true; - } - node = node.next; - } - return false; - } - - export function size(list: luau.List) { - let size = 0; - let node = list.head; - while (node) { - size++; - node = node.next; - } - return size; - } -} diff --git a/src/LuauAST/impl/create.ts b/src/LuauAST/impl/create.ts deleted file mode 100644 index a3f3f01..0000000 --- a/src/LuauAST/impl/create.ts +++ /dev/null @@ -1,184 +0,0 @@ -// must import bundle explicitly to get `luau.create()` calls importing correctly -import * as luau from "../../LuauAST/bundle"; - -type AllowedFieldTypes = luau.BaseNode | luau.List | boolean | number | string | undefined; -type FilterProps = { [K in keyof T]: T[K] extends U ? T[K] : never }; -type FilteredNodeByKind = FilterProps; - -// creation -export function create( - kind: T, - fields: { - [K in Exclude, keyof luau.Node>]: FilteredNodeByKind[K]; - }, -): luau.NodeByKind[T] { - // super hack! - const node = Object.assign({ kind }, fields) as unknown as luau.NodeByKind[T]; - - for (const [key, value] of Object.entries(fields)) { - if (luau.isNode(value)) { - if (value.parent) { - const clone: luau.Node = { ...value }; - clone.parent = node; - node[key as never] = clone as never; - } else { - value.parent = node; - } - } else if (luau.list.isList(value)) { - luau.list.forEachListNode(value, listNode => { - if (listNode.value.parent) { - const clone: luau.Node = { ...listNode.value }; - clone.parent = node; - listNode.value = clone; - } else { - listNode.value.parent = node; - } - }); - } - } - - return node; -} - -let lastTempId = 0; - -/** - * Creates a new temporary identifier for a node. - */ -export function tempId(name = "") { - return luau.create(luau.SyntaxKind.TemporaryIdentifier, { name, id: lastTempId++ }); -} - -/** - * Creates a new `None` node. - */ -export function none() { - return luau.create(luau.SyntaxKind.None, {}); -} - -/** - * Creates a new `nil` literal node. - */ -export function nil() { - return luau.create(luau.SyntaxKind.NilLiteral, {}); -} - -/** - * Creates a new `boolean` literal node. - * @param value The value of the boolean literal. - */ -export function bool(value: boolean) { - if (value) { - return luau.create(luau.SyntaxKind.TrueLiteral, {}); - } else { - return luau.create(luau.SyntaxKind.FalseLiteral, {}); - } -} - -/** - * Creates a new `number` literal node. - * @param value The number to make - */ -export function number(value: number): luau.Expression { - if (value >= 0) { - return luau.create(luau.SyntaxKind.NumberLiteral, { value: String(value) }); - } else { - return luau.create(luau.SyntaxKind.UnaryExpression, { - operator: "-", - expression: luau.number(Math.abs(value)), - }); - } -} - -/** - * Creates a new `string` literal node. - * @param value The value of the string - */ -export function string(value: string) { - return luau.create(luau.SyntaxKind.StringLiteral, { value }); -} - -/** - * Creates a new identifier node. - * @param name The name of the identifier. - */ -export function id(name: string) { - return luau.create(luau.SyntaxKind.Identifier, { name }); -} - -/** - * Creates a new comment node. - * @param text The text of the comment - */ -export function comment(text: string) { - return luau.create(luau.SyntaxKind.Comment, { text }); -} - -/** - * Creates a new array node. - * @param members The `luau.Expression` nodes of the new array. - */ -export function array(members: Array = []) { - return luau.create(luau.SyntaxKind.Array, { - members: luau.list.make(...members), - }); -} - -/** - * Creates a new set node. - * @param members The `luau.Expression` nodes of the new set. - */ -export function set(members: Array = []) { - return luau.create(luau.SyntaxKind.Set, { - members: luau.list.make(...members), - }); -} - -/** - * Creates a new map node. - * @param fields The array of key-value mappings. - */ -export function map(fields: Array<[luau.Expression, luau.Expression]> = []) { - return luau.create(luau.SyntaxKind.Map, { - fields: luau.list.make( - ...fields.map(([index, value]) => luau.create(luau.SyntaxKind.MapField, { index, value })), - ), - }); -} - -/** - * Creates a new mixed table node. - * @param fields The array of either value or key-value mappings. - */ -export function mixedTable(fields: Array = []) { - return luau.create(luau.SyntaxKind.MixedTable, { - fields: luau.list.make( - ...fields.map(field => { - if (Array.isArray(field)) { - return luau.create(luau.SyntaxKind.MapField, { index: field[0], value: field[1] }); - } else { - return field; - } - }), - ), - }); -} - -export function binary(left: luau.Expression, operator: luau.BinaryOperator, right: luau.Expression) { - return luau.create(luau.SyntaxKind.BinaryExpression, { left, operator, right }); -} - -export function unary(operator: luau.UnaryOperator, expression: luau.Expression) { - return luau.create(luau.SyntaxKind.UnaryExpression, { operator, expression }); -} - -export function property(expression: luau.IndexableExpression, name: string) { - return luau.create(luau.SyntaxKind.PropertyAccessExpression, { expression, name }); -} - -export function call(expression: luau.IndexableExpression, args: Array = []) { - return luau.create(luau.SyntaxKind.CallExpression, { - expression, - args: luau.list.make(...args), - }); -} diff --git a/src/LuauAST/impl/enums.ts b/src/LuauAST/impl/enums.ts deleted file mode 100644 index aa03e25..0000000 --- a/src/LuauAST/impl/enums.ts +++ /dev/null @@ -1,57 +0,0 @@ -export enum SyntaxKind { - // indexable expressions - Identifier, - TemporaryIdentifier, - ComputedIndexExpression, - PropertyAccessExpression, - CallExpression, - MethodCallExpression, - ParenthesizedExpression, - - // expressions - None, - NilLiteral, - FalseLiteral, - TrueLiteral, - NumberLiteral, - StringLiteral, - VarArgsLiteral, - FunctionExpression, - BinaryExpression, - UnaryExpression, - IfExpression, - Array, - Map, - Set, - MixedTable, - - // statements - Assignment, - BreakStatement, - CallStatement, - ContinueStatement, - DoStatement, - WhileStatement, - RepeatStatement, - IfStatement, - NumericForStatement, - ForStatement, - FunctionDeclaration, - MethodDeclaration, - VariableDeclaration, - ReturnStatement, - Comment, - - // fields - MapField, - - // used to detect what category a given kind falls into - FirstIndexableExpression = Identifier, - LastIndexableExpression = ParenthesizedExpression, - FirstExpression = Identifier, - LastExpression = Set, - FirstStatement = Assignment, - LastStatement = Comment, - FirstField = MapField, - LastField = MapField, -} diff --git a/src/LuauAST/impl/globals.ts b/src/LuauAST/impl/globals.ts deleted file mode 100644 index ce68d3d..0000000 --- a/src/LuauAST/impl/globals.ts +++ /dev/null @@ -1,68 +0,0 @@ -import * as luau from "../../LuauAST/bundle"; - -const COROUTINE_ID = luau.id("coroutine"); -const MATH_ID = luau.id("math"); -const STRING_ID = luau.id("string"); -const TABLE_ID = luau.id("table"); -const UTF8_ID = luau.id("utf8"); - -export const globals = { - _G: luau.id("_G"), - TS: luau.id("TS"), - assert: luau.id("assert"), - bit32: luau.id("bit32"), - coroutine: { - yield: luau.property(COROUTINE_ID, "yield"), - }, - error: luau.id("error"), - exports: luau.id("exports"), - getmetatable: luau.id("getmetatable"), - ipairs: luau.id("ipairs"), - next: luau.id("next"), - pairs: luau.id("pairs"), - pcall: luau.id("pcall"), - require: luau.id("require"), - script: luau.id("script"), - select: luau.id("select"), - self: luau.id("self"), - setmetatable: luau.id("setmetatable"), - string: { - byte: luau.property(STRING_ID, "byte"), - find: luau.property(STRING_ID, "find"), - format: luau.property(STRING_ID, "format"), - gmatch: luau.property(STRING_ID, "gmatch"), - gsub: luau.property(STRING_ID, "gsub"), - lower: luau.property(STRING_ID, "lower"), - match: luau.property(STRING_ID, "match"), - rep: luau.property(STRING_ID, "rep"), - reverse: luau.property(STRING_ID, "reverse"), - split: luau.property(STRING_ID, "split"), - sub: luau.property(STRING_ID, "sub"), - upper: luau.property(STRING_ID, "upper"), - }, - super: luau.id("super"), - table: { - clear: luau.property(TABLE_ID, "clear"), - concat: luau.property(TABLE_ID, "concat"), - create: luau.property(TABLE_ID, "create"), - find: luau.property(TABLE_ID, "find"), - insert: luau.property(TABLE_ID, "insert"), - move: luau.property(TABLE_ID, "move"), - remove: luau.property(TABLE_ID, "remove"), - sort: luau.property(TABLE_ID, "sort"), - }, - utf8: { - charpattern: luau.property(UTF8_ID, "charpattern"), - codes: luau.property(UTF8_ID, "codes"), - }, - math: { - min: luau.property(MATH_ID, "min"), - }, - tostring: luau.id("tostring"), - type: luau.id("type"), - typeof: luau.id("typeof"), - unpack: luau.id("unpack"), - - // roblox - game: luau.id("game"), -}; diff --git a/src/LuauAST/impl/strings.ts b/src/LuauAST/impl/strings.ts deleted file mode 100644 index 95bfcf6..0000000 --- a/src/LuauAST/impl/strings.ts +++ /dev/null @@ -1,21 +0,0 @@ -import * as luau from "../../LuauAST/bundle"; - -export const strings = { - // metamethods - __index: luau.string("__index"), - __tostring: luau.string("__tostring"), - __mode: luau.string("__mode"), - k: luau.string("k"), // used for __mode - - // types - number: luau.string("number"), - table: luau.string("table"), - - // opcall - success: luau.string("success"), - value: luau.string("value"), - error: luau.string("error"), - - // other - ", ": luau.string(", "), // used for ReadonlyArray.join() -}; diff --git a/src/LuauAST/impl/typeGuards.ts b/src/LuauAST/impl/typeGuards.ts deleted file mode 100644 index 767bd3c..0000000 --- a/src/LuauAST/impl/typeGuards.ts +++ /dev/null @@ -1,144 +0,0 @@ -// must import bundle explicitly to get SyntaxKind importing correctly -import * as luau from "../../LuauAST/bundle"; - -function makeGuard(...kinds: [...Array]) { - const set = new Set(kinds); - return (node: luau.Node): node is luau.NodeByKind[T] => set.has(node.kind); -} - -// indexable expressions -export const isAnyIdentifier = makeGuard(luau.SyntaxKind.Identifier, luau.SyntaxKind.TemporaryIdentifier); -export const isIdentifier = makeGuard(luau.SyntaxKind.Identifier); -export const isTemporaryIdentifier = makeGuard(luau.SyntaxKind.TemporaryIdentifier); -export const isComputedIndexExpression = makeGuard(luau.SyntaxKind.ComputedIndexExpression); -export const isPropertyAccessExpression = makeGuard(luau.SyntaxKind.PropertyAccessExpression); -export const isCallExpression = makeGuard(luau.SyntaxKind.CallExpression); -export const isMethodCallExpression = makeGuard(luau.SyntaxKind.MethodCallExpression); -export const isParenthesizedExpression = makeGuard(luau.SyntaxKind.ParenthesizedExpression); - -export function isIndexableExpression(node: luau.Node): node is luau.IndexableExpression { - return ( - node.kind >= luau.SyntaxKind.FirstIndexableExpression && node.kind <= luau.SyntaxKind.LastIndexableExpression - ); -} - -// expressions -export const isNone = makeGuard(luau.SyntaxKind.None); -export const isNilLiteral = makeGuard(luau.SyntaxKind.NilLiteral); -export const isFalseLiteral = makeGuard(luau.SyntaxKind.FalseLiteral); -export const isTrueLiteral = makeGuard(luau.SyntaxKind.TrueLiteral); -export const isNumberLiteral = makeGuard(luau.SyntaxKind.NumberLiteral); -export const isStringLiteral = makeGuard(luau.SyntaxKind.StringLiteral); -export const isVarArgsLiteral = makeGuard(luau.SyntaxKind.VarArgsLiteral); -export const isFunctionExpression = makeGuard(luau.SyntaxKind.FunctionExpression); -export const isBinaryExpression = makeGuard(luau.SyntaxKind.BinaryExpression); -export const isUnaryExpression = makeGuard(luau.SyntaxKind.UnaryExpression); -export const isIfExpression = makeGuard(luau.SyntaxKind.IfExpression); -export const isArray = makeGuard(luau.SyntaxKind.Array); -export const isMap = makeGuard(luau.SyntaxKind.Map); -export const isSet = makeGuard(luau.SyntaxKind.Set); -export const isMixedTable = makeGuard(luau.SyntaxKind.MixedTable); - -export function isExpression(node: luau.Node): node is luau.Expression { - return node.kind >= luau.SyntaxKind.FirstExpression && node.kind <= luau.SyntaxKind.LastExpression; -} - -// statements -export const isAssignment = makeGuard(luau.SyntaxKind.Assignment); -export const isBreakStatement = makeGuard(luau.SyntaxKind.BreakStatement); -export const isCallStatement = makeGuard(luau.SyntaxKind.CallStatement); -export const isContinueStatement = makeGuard(luau.SyntaxKind.ContinueStatement); -export const isDoStatement = makeGuard(luau.SyntaxKind.DoStatement); -export const isWhileStatement = makeGuard(luau.SyntaxKind.WhileStatement); -export const isRepeatStatement = makeGuard(luau.SyntaxKind.RepeatStatement); -export const isIfStatement = makeGuard(luau.SyntaxKind.IfStatement); -export const isNumericForStatement = makeGuard(luau.SyntaxKind.NumericForStatement); -export const isForStatement = makeGuard(luau.SyntaxKind.ForStatement); -export const isFunctionDeclaration = makeGuard(luau.SyntaxKind.FunctionDeclaration); -export const isMethodDeclaration = makeGuard(luau.SyntaxKind.MethodDeclaration); -export const isVariableDeclaration = makeGuard(luau.SyntaxKind.VariableDeclaration); -export const isReturnStatement = makeGuard(luau.SyntaxKind.ReturnStatement); -export const isComment = makeGuard(luau.SyntaxKind.Comment); - -export function isStatement(node: luau.Node): node is luau.Statement { - return node.kind >= luau.SyntaxKind.FirstStatement && node.kind <= luau.SyntaxKind.LastStatement; -} - -// fields -export const isMapField = makeGuard(luau.SyntaxKind.MapField); - -export function isField(node: luau.Node): node is luau.Field { - return node.kind >= luau.SyntaxKind.FirstField && node.kind <= luau.SyntaxKind.LastField; -} - -export function isNode(value: unknown): value is luau.Node { - if (typeof value === "object" && value !== null && "kind" in value) { - // hack - const { kind } = value as { kind: unknown }; - return ( - typeof kind === "number" && - kind >= luau.SyntaxKind.FirstIndexableExpression && - kind <= luau.SyntaxKind.LastField - ); - } - return false; -} - -export const isSimple = makeGuard( - luau.SyntaxKind.Identifier, - luau.SyntaxKind.TemporaryIdentifier, - luau.SyntaxKind.NilLiteral, - luau.SyntaxKind.TrueLiteral, - luau.SyntaxKind.FalseLiteral, - luau.SyntaxKind.NumberLiteral, - luau.SyntaxKind.StringLiteral, -); - -export const isSimplePrimitive = makeGuard( - luau.SyntaxKind.NilLiteral, - luau.SyntaxKind.TrueLiteral, - luau.SyntaxKind.FalseLiteral, - luau.SyntaxKind.NumberLiteral, - luau.SyntaxKind.StringLiteral, -); - -export const isTable = makeGuard(luau.SyntaxKind.Array, luau.SyntaxKind.Set, luau.SyntaxKind.Map); - -export const isFinalStatement = makeGuard( - luau.SyntaxKind.BreakStatement, - luau.SyntaxKind.ReturnStatement, - luau.SyntaxKind.ContinueStatement, -); - -export const isCall = makeGuard(luau.SyntaxKind.CallExpression, luau.SyntaxKind.MethodCallExpression); - -export const isWritableExpression: (node: luau.Node) => node is luau.WritableExpression = makeGuard( - luau.SyntaxKind.Identifier, - luau.SyntaxKind.TemporaryIdentifier, - luau.SyntaxKind.PropertyAccessExpression, - luau.SyntaxKind.ComputedIndexExpression, -); - -export const isFunctionLike = makeGuard( - luau.SyntaxKind.FunctionDeclaration, - luau.SyntaxKind.FunctionExpression, - luau.SyntaxKind.MethodDeclaration, -); - -export const hasStatements = makeGuard( - luau.SyntaxKind.ForStatement, - luau.SyntaxKind.NumericForStatement, - luau.SyntaxKind.FunctionExpression, - luau.SyntaxKind.DoStatement, - luau.SyntaxKind.FunctionDeclaration, - luau.SyntaxKind.IfStatement, - luau.SyntaxKind.MethodDeclaration, - luau.SyntaxKind.RepeatStatement, - luau.SyntaxKind.WhileStatement, -); - -export const isExpressionWithPrecedence = makeGuard( - luau.SyntaxKind.IfExpression, - luau.SyntaxKind.UnaryExpression, - luau.SyntaxKind.BinaryExpression, -); diff --git a/src/LuauAST/index.ts b/src/LuauAST/index.ts deleted file mode 100644 index 8c5c762..0000000 --- a/src/LuauAST/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import * as luau from "../LuauAST/bundle"; -export default luau; - -export * from "../LuauRenderer"; diff --git a/src/LuauAST/types/mapping.ts b/src/LuauAST/types/mapping.ts deleted file mode 100644 index f509d29..0000000 --- a/src/LuauAST/types/mapping.ts +++ /dev/null @@ -1,71 +0,0 @@ -import luau from "../../LuauAST"; - -export interface IndexableExpressionByKind { - [luau.SyntaxKind.None]: luau.None; - [luau.SyntaxKind.Identifier]: luau.Identifier; - [luau.SyntaxKind.TemporaryIdentifier]: luau.TemporaryIdentifier; - [luau.SyntaxKind.ComputedIndexExpression]: luau.ComputedIndexExpression; - [luau.SyntaxKind.PropertyAccessExpression]: luau.PropertyAccessExpression; - [luau.SyntaxKind.CallExpression]: luau.CallExpression; - [luau.SyntaxKind.MethodCallExpression]: luau.MethodCallExpression; - [luau.SyntaxKind.ParenthesizedExpression]: luau.ParenthesizedExpression; -} - -export interface ExpressionByKind extends IndexableExpressionByKind { - [luau.SyntaxKind.NilLiteral]: luau.NilLiteral; - [luau.SyntaxKind.FalseLiteral]: luau.FalseLiteral; - [luau.SyntaxKind.TrueLiteral]: luau.TrueLiteral; - [luau.SyntaxKind.NumberLiteral]: luau.NumberLiteral; - [luau.SyntaxKind.StringLiteral]: luau.StringLiteral; - [luau.SyntaxKind.VarArgsLiteral]: luau.VarArgsLiteral; - [luau.SyntaxKind.FunctionExpression]: luau.FunctionExpression; - [luau.SyntaxKind.BinaryExpression]: luau.BinaryExpression; - [luau.SyntaxKind.UnaryExpression]: luau.UnaryExpression; - [luau.SyntaxKind.IfExpression]: luau.IfExpression; - [luau.SyntaxKind.Array]: luau.Array; - [luau.SyntaxKind.Map]: luau.Map; - [luau.SyntaxKind.Set]: luau.Set; - [luau.SyntaxKind.MixedTable]: luau.MixedTable; -} - -export interface StatementByKind { - [luau.SyntaxKind.Assignment]: luau.Assignment; - [luau.SyntaxKind.BreakStatement]: luau.BreakStatement; - [luau.SyntaxKind.CallStatement]: luau.CallStatement; - [luau.SyntaxKind.ContinueStatement]: luau.ContinueStatement; - [luau.SyntaxKind.DoStatement]: luau.DoStatement; - [luau.SyntaxKind.WhileStatement]: luau.WhileStatement; - [luau.SyntaxKind.RepeatStatement]: luau.RepeatStatement; - [luau.SyntaxKind.IfStatement]: luau.IfStatement; - [luau.SyntaxKind.NumericForStatement]: luau.NumericForStatement; - [luau.SyntaxKind.ForStatement]: luau.ForStatement; - [luau.SyntaxKind.FunctionDeclaration]: luau.FunctionDeclaration; - [luau.SyntaxKind.MethodDeclaration]: luau.MethodDeclaration; - [luau.SyntaxKind.VariableDeclaration]: luau.VariableDeclaration; - [luau.SyntaxKind.ReturnStatement]: luau.ReturnStatement; - [luau.SyntaxKind.Comment]: luau.Comment; -} - -export interface FieldByKind { - [luau.SyntaxKind.MapField]: luau.MapField; -} - -export interface NodeByKind extends luau.ExpressionByKind, luau.StatementByKind, luau.FieldByKind {} - -export type IndexableExpression = { - [K in keyof IndexableExpressionByKind]: IndexableExpressionByKind[K]["kind"] extends T - ? IndexableExpressionByKind[K] - : never; -}[keyof IndexableExpressionByKind]; -export type Expression = { - [K in keyof ExpressionByKind]: ExpressionByKind[K]["kind"] extends T ? ExpressionByKind[K] : never; -}[keyof ExpressionByKind]; -export type Statement = { - [K in keyof StatementByKind]: StatementByKind[K]["kind"] extends T ? StatementByKind[K] : never; -}[keyof StatementByKind]; -export type Field = { - [K in keyof FieldByKind]: FieldByKind[K]["kind"] extends T ? FieldByKind[K] : never; -}[keyof FieldByKind]; -export type Node = { - [K in keyof NodeByKind]: NodeByKind[K]["kind"] extends T ? NodeByKind[K] : never; -}[keyof NodeByKind]; diff --git a/src/LuauAST/types/nodes.ts b/src/LuauAST/types/nodes.ts deleted file mode 100644 index b4a778f..0000000 --- a/src/LuauAST/types/nodes.ts +++ /dev/null @@ -1,209 +0,0 @@ -import luau from "../../LuauAST"; - -// base types -export interface BaseNode { - kind: T; - parent?: luau.Node; -} - -export interface BaseIndexableExpression< - T extends keyof luau.IndexableExpressionByKind = keyof luau.IndexableExpressionByKind, -> extends luau.BaseNode {} - -export interface BaseExpression - extends luau.BaseNode {} - -export interface BaseStatement - extends luau.BaseNode {} - -export interface BaseField extends luau.BaseNode {} - -export interface HasParameters { - parameters: luau.List; - hasDotDotDot: boolean; -} - -export type AnyIdentifier = luau.Identifier | luau.TemporaryIdentifier; - -export type WritableExpression = luau.AnyIdentifier | luau.PropertyAccessExpression | luau.ComputedIndexExpression; - -export type SimpleTypes = - | luau.Identifier - | luau.TemporaryIdentifier - | luau.NilLiteral - | luau.TrueLiteral - | luau.FalseLiteral - | luau.NumberLiteral - | luau.StringLiteral; - -export type ExpressionWithPrecedence = luau.IfExpression | luau.UnaryExpression | luau.BinaryExpression; - -// expressions -export interface None extends luau.BaseExpression {} - -export interface NilLiteral extends luau.BaseExpression {} - -export interface FalseLiteral extends luau.BaseExpression {} - -export interface TrueLiteral extends luau.BaseExpression {} - -export interface NumberLiteral extends luau.BaseExpression { - value: string; -} - -export interface StringLiteral extends luau.BaseExpression { - value: string; -} - -export interface VarArgsLiteral extends luau.BaseExpression {} - -export interface FunctionExpression extends luau.BaseExpression, HasParameters { - statements: luau.List; -} - -export interface Identifier extends luau.BaseExpression { - name: string; -} - -export interface TemporaryIdentifier extends luau.BaseExpression { - name: string; - id: number; -} - -export interface ComputedIndexExpression extends luau.BaseExpression { - expression: luau.IndexableExpression; - index: luau.Expression; -} - -export interface PropertyAccessExpression extends luau.BaseExpression { - expression: luau.IndexableExpression; - name: string; -} - -export interface CallExpression extends luau.BaseExpression { - expression: luau.IndexableExpression; - args: luau.List; -} - -export interface MethodCallExpression extends luau.BaseExpression { - name: string; - expression: luau.IndexableExpression; - args: luau.List; -} - -export interface ParenthesizedExpression extends luau.BaseExpression { - expression: luau.Expression; -} - -export interface BinaryExpression extends luau.BaseExpression { - left: luau.Expression; - operator: luau.BinaryOperator; - right: luau.Expression; -} - -export interface UnaryExpression extends luau.BaseExpression { - operator: luau.UnaryOperator; - expression: luau.Expression; -} - -export interface IfExpression extends luau.BaseExpression { - condition: luau.Expression; - expression: luau.Expression; - alternative: luau.Expression; -} - -export interface Array extends luau.BaseExpression { - members: luau.List; -} - -export interface Map extends luau.BaseExpression { - fields: luau.List; -} - -export interface Set extends luau.BaseExpression { - members: luau.List; -} - -export interface MixedTable extends luau.BaseExpression { - fields: luau.List; -} - -// statements -export interface Assignment extends luau.BaseStatement { - left: luau.WritableExpression | luau.List; - operator: luau.AssignmentOperator; - right: luau.Expression | luau.List; -} - -export interface BreakStatement extends luau.BaseStatement {} - -export interface CallStatement extends luau.BaseStatement { - expression: luau.CallExpression | luau.MethodCallExpression; -} - -export interface ContinueStatement extends luau.BaseStatement {} - -export interface DoStatement extends luau.BaseStatement { - statements: luau.List; -} - -export interface WhileStatement extends luau.BaseStatement { - condition: luau.Expression; - statements: luau.List; -} - -export interface RepeatStatement extends luau.BaseStatement { - condition: luau.Expression; - statements: luau.List; -} - -export interface IfStatement extends luau.BaseStatement { - condition: luau.Expression; - statements: luau.List; - elseBody: luau.IfStatement | luau.List; -} - -export interface NumericForStatement extends luau.BaseStatement { - id: luau.AnyIdentifier; - start: luau.Expression; - end: luau.Expression; - step?: luau.Expression; - statements: luau.List; -} - -export interface ForStatement extends luau.BaseStatement { - ids: luau.List; - expression: luau.Expression; - statements: luau.List; -} - -export interface FunctionDeclaration extends luau.BaseStatement, HasParameters { - localize: boolean; - name: luau.AnyIdentifier | luau.PropertyAccessExpression; - statements: luau.List; -} - -export interface MethodDeclaration extends luau.BaseStatement, HasParameters { - expression: luau.IndexableExpression; - name: string; - statements: luau.List; -} - -export interface VariableDeclaration extends luau.BaseStatement { - left: luau.AnyIdentifier | luau.List; - right: luau.Expression | luau.List | undefined; -} - -export interface ReturnStatement extends luau.BaseStatement { - expression: luau.Expression | luau.List; -} - -export interface Comment extends luau.BaseStatement { - text: string; -} - -// fields -export interface MapField extends luau.BaseField { - index: luau.Expression; - value: luau.Expression; -} diff --git a/src/LuauAST/types/operators.ts b/src/LuauAST/types/operators.ts deleted file mode 100644 index 74cc513..0000000 --- a/src/LuauAST/types/operators.ts +++ /dev/null @@ -1,20 +0,0 @@ -export type BinaryOperator = - | "+" - | "-" - | "*" - | "/" - | "^" - | "%" - | ".." - | "<" - | "<=" - | ">" - | ">=" - | "==" - | "~=" - | "and" - | "or"; - -export type UnaryOperator = "-" | "not" | "#"; - -export type AssignmentOperator = "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "^=" | "..="; diff --git a/src/LuauAST/util/assert.ts b/src/LuauAST/util/assert.ts deleted file mode 100644 index e44225f..0000000 --- a/src/LuauAST/util/assert.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Asserts the truthiness of `value`, stops the debugger on failure. - * @param value The value to check the truthiness of - * @param message Optional. The message of the error - */ -export function assert(value: unknown, message?: string): asserts value; -export function assert(value: false, message?: string): never; -export function assert(value: unknown, message?: string): asserts value { - /* istanbul ignore if */ - if (!value) { - debugger; - throw new Error( - `Assertion Failed! ${message ?? ""}` + - "\nThis is a compiler bug! Please submit a bug report here:" + - "\nhttps://github.com/roblox-ts/roblox-ts/issues", - ); - } -} diff --git a/src/LuauAST/util/getKindName.ts b/src/LuauAST/util/getKindName.ts deleted file mode 100644 index 4d4ae7a..0000000 --- a/src/LuauAST/util/getKindName.ts +++ /dev/null @@ -1,12 +0,0 @@ -import luau from "../../LuauAST"; - -export function getKindName(kind: luau.SyntaxKind) { - // avoid FirstExpression, LastExpression, etc. - if (kind === luau.SyntaxKind.Identifier) return "Identifier"; - else if (kind === luau.SyntaxKind.ParenthesizedExpression) return "ParenthesizedExpression"; - else if (kind === luau.SyntaxKind.Set) return "Set"; - else if (kind === luau.SyntaxKind.Assignment) return "Assignment"; - else if (kind === luau.SyntaxKind.Comment) return "Comment"; - else if (kind === luau.SyntaxKind.MapField) return "MapField"; - return luau.SyntaxKind[kind]; -} diff --git a/src/LuauAST/util/isMetamethod.ts b/src/LuauAST/util/isMetamethod.ts deleted file mode 100644 index f2f0bd3..0000000 --- a/src/LuauAST/util/isMetamethod.ts +++ /dev/null @@ -1,26 +0,0 @@ -const LUAU_METAMETHODS = new Set([ - "__index", - "__newindex", - "__call", - "__concat", - "__unm", - "__add", - "__sub", - "__mul", - "__div", - "__mod", - "__pow", - "__tostring", - "__metatable", - "__eq", - "__lt", - "__le", - "__mode", - "__gc", - "__len", -]); - -/** Returns true if the given string is a valid Luau metamethod */ -export function isMetamethod(id: string) { - return LUAU_METAMETHODS.has(id); -} diff --git a/src/LuauAST/util/isReservedClassField.ts b/src/LuauAST/util/isReservedClassField.ts deleted file mode 100644 index 5872bf1..0000000 --- a/src/LuauAST/util/isReservedClassField.ts +++ /dev/null @@ -1,11 +0,0 @@ -const LUAU_RESERVED_CLASS_FIELDS = new Set(["__index", "new"]); - -export function isReservedClassField(id: string) { - return LUAU_RESERVED_CLASS_FIELDS.has(id); -} - -const ROACT_RESERVED_CLASS_FIELDS = new Set(["init"]); - -export function isReservedRoactClassField(id: string) { - return ROACT_RESERVED_CLASS_FIELDS.has(id); -} diff --git a/src/LuauAST/util/isReservedIdentifier.ts b/src/LuauAST/util/isReservedIdentifier.ts deleted file mode 100644 index 85441d4..0000000 --- a/src/LuauAST/util/isReservedIdentifier.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { globals } from "../../LuauAST/impl/globals"; - -export function isReservedIdentifier(id: string) { - return id in globals; -} diff --git a/src/LuauAST/util/isValidIdentifier.ts b/src/LuauAST/util/isValidIdentifier.ts deleted file mode 100644 index a276a67..0000000 --- a/src/LuauAST/util/isValidIdentifier.ts +++ /dev/null @@ -1,31 +0,0 @@ -// X = reserved by TypeScript -const LUAU_RESERVED_KEYWORDS = new Set([ - "and", - "break", // X - "do", // X - "else", // X - "elseif", - "end", - "false", // X - "for", // X - "function", // X - "if", // X - "in", // X - "local", - "nil", - "not", - "or", - "repeat", - "return", // X - "then", - "true", // X - "until", - "while", // X -]); - -const LUAU_IDENTIFIER_REGEX = /^[A-Za-z_][A-Za-z0-9_]*$/; - -/** Returns true if the given string is a valid Luau identifier, and does not conflict with a temporary identifier */ -export function isValidIdentifier(id: string) { - return !LUAU_RESERVED_KEYWORDS.has(id) && LUAU_IDENTIFIER_REGEX.test(id); -} diff --git a/src/LuauAST/util/isValidNumberLiteral.ts b/src/LuauAST/util/isValidNumberLiteral.ts deleted file mode 100644 index ebafe9e..0000000 --- a/src/LuauAST/util/isValidNumberLiteral.ts +++ /dev/null @@ -1,7 +0,0 @@ -const DECIMAL_LITERAL_REGEX = /^(?:\d[\d_]*(?:\.[\d_]*)?|\.\d[\d_]*)(?:[eE][+-]?_*\d[\d_]*)?$/; -const BINARY_LITERAL_REGEX = /^0_*[bB]_*[01][01_]*$/; -const HEXADECIMAL_LITERAL_REGEX = /^0_*[xX]_*[\da-fA-F][\da-fA-F_]*$/; - -export function isValidNumberLiteral(text: string) { - return DECIMAL_LITERAL_REGEX.test(text) || BINARY_LITERAL_REGEX.test(text) || HEXADECIMAL_LITERAL_REGEX.test(text); -} diff --git a/src/LuauRenderer/README.md b/src/LuauRenderer/README.md deleted file mode 100644 index 450b3ca..0000000 --- a/src/LuauRenderer/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# roblox-ts LuauRenderer - -This project takes a Luau AST (from LuauAST) and converts it into Luau source code in the form of a string. - -## Structure - -**index.ts** - contains the global render function, takes any node and routes it to the appropriate `renderX()` function to turn it into a string - -**RenderState.ts** - stores the current state of the render process, instance is passed into every `renderX()` function - -**nodes/** - folder containing modules that each export a `renderX(state: RenderState, node: luau.X): string` function - -**util/** - various helper modules to aid in rendering diff --git a/src/LuauRenderer/RenderState.ts b/src/LuauRenderer/RenderState.ts deleted file mode 100644 index 6b52f90..0000000 --- a/src/LuauRenderer/RenderState.ts +++ /dev/null @@ -1,107 +0,0 @@ -import luau from "../LuauAST"; -import { assert } from "../LuauAST/util/assert"; -import { getEnding } from "../LuauRenderer/util/getEnding"; -import { getOrSetDefault } from "../LuauRenderer/util/getOrSetDefault"; - -const INDENT_CHARACTER = "\t"; -const INDENT_CHARACTER_LENGTH = INDENT_CHARACTER.length; - -/** - * Represents the state of a rendering process. - */ -export class RenderState { - private indent = ""; - public seenTempNodes = new Map(); - private readonly listNodesStack = new Array>(); - - /** - * Pushes an indent to the current indent level. - */ - private pushIndent() { - this.indent += INDENT_CHARACTER; - } - - /** - * Pops an indent from the current indent level. - */ - private popIndent() { - this.indent = this.indent.substr(INDENT_CHARACTER_LENGTH); - } - - private tempIdFallback = 0; - - /** - * Returns an unique identifier that is unused in the current scope. - * `this.seenTempNodes` should already be fully populated by this point! - * This is a fallback mechanism for when `solveTempIds()` does not catch something properly. - * @param node The identifier of the node - */ - public getTempName(node: luau.TemporaryIdentifier) { - const name = getOrSetDefault(this.seenTempNodes, node.id, () => `_${this.tempIdFallback++}`); - assert(name); - return name; - } - - /** - * Pushes a LuauAST node to the top of the list node stack - * @param listNode The syntax node to add to the stop of the stack. - */ - public pushListNode(listNode: luau.ListNode) { - this.listNodesStack.push(listNode); - } - - /** - * Returns the top of the scope stack. - */ - public peekListNode(): luau.ListNode | undefined { - return this.listNodesStack[this.listNodesStack.length - 1]; - } - - /** - * Pops the top list node off the syntax tree node stack. - */ - public popListNode() { - return this.listNodesStack.pop(); - } - - /** - * Adds a newline to the end of the string. - * @param text The text. - */ - public newline(text: string) { - return text + "\n"; - } - - /** - * Prefixes the text with the current indent. - * @param text The text. - */ - public indented(text: string) { - return this.indent + text; - } - - /** - * Renders a line, adding the current indent, a semicolon if necessary, and "\n". - * @param text The content of the line. - * @param endNode Node used to determine if a semicolon should be added. Undefined means no semi will be added. - */ - public line(text: string, endNode?: luau.Statement) { - let result = this.indented(text); - if (endNode) { - result += getEnding(this, endNode); - } - result = this.newline(result); - return result; - } - - /** - * Returns a rendered code block. - * @param callback The function used to render the block. - */ - public block(callback: () => T) { - this.pushIndent(); - const result = callback(); - this.popIndent(); - return result; - } -} diff --git a/src/LuauRenderer/index.ts b/src/LuauRenderer/index.ts deleted file mode 100644 index 2d5ab89..0000000 --- a/src/LuauRenderer/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from "../LuauRenderer/render"; -export * from "../LuauRenderer/RenderState"; -export * from "../LuauRenderer/solveTempIds"; -export * from "../LuauRenderer/util/renderStatements"; diff --git a/src/LuauRenderer/nodes/expressions/indexable/renderCallExpression.ts b/src/LuauRenderer/nodes/expressions/indexable/renderCallExpression.ts deleted file mode 100644 index 458eb3f..0000000 --- a/src/LuauRenderer/nodes/expressions/indexable/renderCallExpression.ts +++ /dev/null @@ -1,7 +0,0 @@ -import luau from "../../../../LuauAST"; -import { render, RenderState } from "../../../../LuauRenderer"; -import { renderArguments } from "../../../../LuauRenderer/util/renderArguments"; - -export function renderCallExpression(state: RenderState, node: luau.CallExpression) { - return `${render(state, node.expression)}(${renderArguments(state, node.args)})`; -} diff --git a/src/LuauRenderer/nodes/expressions/indexable/renderComputedIndexExpression.ts b/src/LuauRenderer/nodes/expressions/indexable/renderComputedIndexExpression.ts deleted file mode 100644 index 1f16351..0000000 --- a/src/LuauRenderer/nodes/expressions/indexable/renderComputedIndexExpression.ts +++ /dev/null @@ -1,12 +0,0 @@ -import luau from "../../../../LuauAST"; -import { render, RenderState } from "../../../../LuauRenderer"; - -export function renderComputedIndexExpression(state: RenderState, node: luau.ComputedIndexExpression) { - const expStr = render(state, node.expression); - if (luau.isStringLiteral(node.index) && luau.isValidIdentifier(node.index.value)) { - return `${expStr}.${node.index.value}`; - } else { - const indexStr = render(state, node.index); - return `${expStr}[${indexStr}]`; - } -} diff --git a/src/LuauRenderer/nodes/expressions/indexable/renderIdentifier.ts b/src/LuauRenderer/nodes/expressions/indexable/renderIdentifier.ts deleted file mode 100644 index 7250dd3..0000000 --- a/src/LuauRenderer/nodes/expressions/indexable/renderIdentifier.ts +++ /dev/null @@ -1,8 +0,0 @@ -import luau from "../../../../LuauAST"; -import { assert } from "../../../../LuauAST/util/assert"; -import { RenderState } from "../../../../LuauRenderer"; - -export function renderIdentifier(state: RenderState, node: luau.Identifier) { - assert(luau.isValidIdentifier(node.name), `Invalid Luau Identifier: "${node.name}"`); - return node.name; -} diff --git a/src/LuauRenderer/nodes/expressions/indexable/renderMethodCallExpression.ts b/src/LuauRenderer/nodes/expressions/indexable/renderMethodCallExpression.ts deleted file mode 100644 index 540b63b..0000000 --- a/src/LuauRenderer/nodes/expressions/indexable/renderMethodCallExpression.ts +++ /dev/null @@ -1,9 +0,0 @@ -import luau from "../../../../LuauAST"; -import { assert } from "../../../../LuauAST/util/assert"; -import { render, RenderState } from "../../../../LuauRenderer"; -import { renderArguments } from "../../../../LuauRenderer/util/renderArguments"; - -export function renderMethodCallExpression(state: RenderState, node: luau.MethodCallExpression) { - assert(luau.isValidIdentifier(node.name)); - return `${render(state, node.expression)}:${node.name}(${renderArguments(state, node.args)})`; -} diff --git a/src/LuauRenderer/nodes/expressions/indexable/renderParenthesizedExpression.ts b/src/LuauRenderer/nodes/expressions/indexable/renderParenthesizedExpression.ts deleted file mode 100644 index 7e0a8f3..0000000 --- a/src/LuauRenderer/nodes/expressions/indexable/renderParenthesizedExpression.ts +++ /dev/null @@ -1,15 +0,0 @@ -import luau from "../../../../LuauAST"; -import { render, RenderState } from "../../../../LuauRenderer"; - -export function renderParenthesizedExpression(state: RenderState, node: luau.ParenthesizedExpression) { - // skip nested parentheses - let expression = node.expression; - while (luau.isParenthesizedExpression(expression)) { - expression = expression.expression; - } - if (luau.isSimple(expression)) { - return render(state, node.expression); - } else { - return `(${render(state, node.expression)})`; - } -} diff --git a/src/LuauRenderer/nodes/expressions/indexable/renderPropertyAccessExpression.ts b/src/LuauRenderer/nodes/expressions/indexable/renderPropertyAccessExpression.ts deleted file mode 100644 index 784765a..0000000 --- a/src/LuauRenderer/nodes/expressions/indexable/renderPropertyAccessExpression.ts +++ /dev/null @@ -1,12 +0,0 @@ -import luau from "../../../../LuauAST"; -import { render, RenderState } from "../../../../LuauRenderer"; - -export function renderPropertyAccessExpression(state: RenderState, node: luau.PropertyAccessExpression) { - const expStr = render(state, node.expression); - const nameStr = node.name; - if (luau.isValidIdentifier(nameStr)) { - return `${expStr}.${nameStr}`; - } else { - return `${expStr}["${nameStr}"]`; - } -} diff --git a/src/LuauRenderer/nodes/expressions/indexable/renderTemporaryIdentifier.ts b/src/LuauRenderer/nodes/expressions/indexable/renderTemporaryIdentifier.ts deleted file mode 100644 index b269e9e..0000000 --- a/src/LuauRenderer/nodes/expressions/indexable/renderTemporaryIdentifier.ts +++ /dev/null @@ -1,9 +0,0 @@ -import luau from "../../../../LuauAST"; -import { assert } from "../../../../LuauAST/util/assert"; -import { RenderState } from "../../../../LuauRenderer"; - -export function renderTemporaryIdentifier(state: RenderState, node: luau.TemporaryIdentifier) { - const name = state.getTempName(node); - assert(luau.isValidIdentifier(name), `Invalid Temporary Identifier: "${name}"`); - return name; -} diff --git a/src/LuauRenderer/nodes/expressions/renderArray.ts b/src/LuauRenderer/nodes/expressions/renderArray.ts deleted file mode 100644 index 718a4ad..0000000 --- a/src/LuauRenderer/nodes/expressions/renderArray.ts +++ /dev/null @@ -1,11 +0,0 @@ -import luau from "../../../LuauAST"; -import { render, RenderState } from "../../../LuauRenderer"; - -export function renderArray(state: RenderState, node: luau.Array) { - if (luau.list.isEmpty(node.members)) { - return "{}"; - } - - const membersStr = luau.list.mapToArray(node.members, member => render(state, member)).join(", "); - return `{ ${membersStr} }`; -} diff --git a/src/LuauRenderer/nodes/expressions/renderBinaryExpression.ts b/src/LuauRenderer/nodes/expressions/renderBinaryExpression.ts deleted file mode 100644 index 75ccf2b..0000000 --- a/src/LuauRenderer/nodes/expressions/renderBinaryExpression.ts +++ /dev/null @@ -1,13 +0,0 @@ -import luau from "../../../LuauAST"; -import { render, RenderState } from "../../../LuauRenderer"; -import { needsParentheses } from "../../../LuauRenderer/util/needsParentheses"; - -export function renderBinaryExpression(state: RenderState, node: luau.BinaryExpression) { - let result = `${render(state, node.left)} ${node.operator} ${render(state, node.right)}`; - - if (needsParentheses(node)) { - result = `(${result})`; - } - - return result; -} diff --git a/src/LuauRenderer/nodes/expressions/renderFunctionExpression.ts b/src/LuauRenderer/nodes/expressions/renderFunctionExpression.ts deleted file mode 100644 index a7bb2bf..0000000 --- a/src/LuauRenderer/nodes/expressions/renderFunctionExpression.ts +++ /dev/null @@ -1,16 +0,0 @@ -import luau from "../../../LuauAST"; -import { RenderState } from "../../../LuauRenderer"; -import { renderParameters } from "../../../LuauRenderer/util/renderParameters"; -import { renderStatements } from "../../../LuauRenderer/util/renderStatements"; - -export function renderFunctionExpression(state: RenderState, node: luau.FunctionExpression) { - if (luau.list.isEmpty(node.statements)) { - return `function(${renderParameters(state, node)}) end`; - } - - let result = ""; - result += state.newline(`function(${renderParameters(state, node)})`); - result += state.block(() => renderStatements(state, node.statements)); - result += state.indented(`end`); - return result; -} diff --git a/src/LuauRenderer/nodes/expressions/renderIfExpression.ts b/src/LuauRenderer/nodes/expressions/renderIfExpression.ts deleted file mode 100644 index 2eb7024..0000000 --- a/src/LuauRenderer/nodes/expressions/renderIfExpression.ts +++ /dev/null @@ -1,23 +0,0 @@ -import luau from "../../../LuauAST"; -import { render, RenderState } from "../../../LuauRenderer"; -import { needsParentheses } from "../../../LuauRenderer/util/needsParentheses"; - -export function renderIfExpression(state: RenderState, node: luau.IfExpression) { - let result = `if ${render(state, node.condition)} then ${render(state, node.expression)} `; - - let currentAlternative = node.alternative; - while (luau.isIfExpression(currentAlternative)) { - const condition = render(state, currentAlternative.condition); - const expression = render(state, currentAlternative.expression); - result += `elseif ${condition} then ${expression} `; - currentAlternative = currentAlternative.alternative; - } - - result += `else ${render(state, currentAlternative)}`; - - if (needsParentheses(node)) { - result = `(${result})`; - } - - return result; -} diff --git a/src/LuauRenderer/nodes/expressions/renderMap.ts b/src/LuauRenderer/nodes/expressions/renderMap.ts deleted file mode 100644 index 9584617..0000000 --- a/src/LuauRenderer/nodes/expressions/renderMap.ts +++ /dev/null @@ -1,15 +0,0 @@ -import luau from "../../../LuauAST"; -import { render, RenderState } from "../../../LuauRenderer"; - -export function renderMap(state: RenderState, node: luau.Map) { - if (luau.list.isEmpty(node.fields)) { - return "{}"; - } - - let result = "{\n"; - state.block(() => { - luau.list.forEach(node.fields, field => (result += state.line(`${render(state, field)},`))); - }); - result += state.indented("}"); - return result; -} diff --git a/src/LuauRenderer/nodes/expressions/renderMixedTable.ts b/src/LuauRenderer/nodes/expressions/renderMixedTable.ts deleted file mode 100644 index 1656057..0000000 --- a/src/LuauRenderer/nodes/expressions/renderMixedTable.ts +++ /dev/null @@ -1,16 +0,0 @@ -import luau from "../../../LuauAST"; -import { render, RenderState } from "../../../LuauRenderer"; - -export function renderMixedTable(state: RenderState, node: luau.MixedTable) { - if (luau.list.isEmpty(node.fields)) { - return "{}"; - } - - let result = "{\n"; - state.block(() => { - // temp fix for https://github.com/microsoft/TypeScript/issues/42932 - luau.list.forEach(node.fields, field => (result += state.line(`${render(state, field as luau.Node)},`))); - }); - result += state.indented("}"); - return result; -} diff --git a/src/LuauRenderer/nodes/expressions/renderNumberLiteral.ts b/src/LuauRenderer/nodes/expressions/renderNumberLiteral.ts deleted file mode 100644 index 8bac505..0000000 --- a/src/LuauRenderer/nodes/expressions/renderNumberLiteral.ts +++ /dev/null @@ -1,6 +0,0 @@ -import luau from "../../../LuauAST"; -import { RenderState } from "../../../LuauRenderer"; - -export function renderNumberLiteral(state: RenderState, node: luau.NumberLiteral) { - return luau.isValidNumberLiteral(node.value) ? node.value : String(Number(node.value.replace(/_/g, ""))); -} diff --git a/src/LuauRenderer/nodes/expressions/renderSet.ts b/src/LuauRenderer/nodes/expressions/renderSet.ts deleted file mode 100644 index 218c86f..0000000 --- a/src/LuauRenderer/nodes/expressions/renderSet.ts +++ /dev/null @@ -1,15 +0,0 @@ -import luau from "../../../LuauAST"; -import { render, RenderState } from "../../../LuauRenderer"; - -export function renderSet(state: RenderState, node: luau.Set) { - if (luau.list.isEmpty(node.members)) { - return "{}"; - } - - let result = "{\n"; - state.block(() => { - luau.list.forEach(node.members, member => (result += state.line(`[${render(state, member)}] = true,`))); - }); - result += state.indented("}"); - return result; -} diff --git a/src/LuauRenderer/nodes/expressions/renderStringLiteral.ts b/src/LuauRenderer/nodes/expressions/renderStringLiteral.ts deleted file mode 100644 index c3a938d..0000000 --- a/src/LuauRenderer/nodes/expressions/renderStringLiteral.ts +++ /dev/null @@ -1,37 +0,0 @@ -import luau from "../../../LuauAST"; -import { RenderState } from "../../../LuauRenderer"; -import { getSafeBracketEquals } from "../../../LuauRenderer/util/getSafeBracketEquals"; - -function needsBracketSpacing(node: luau.StringLiteral) { - const parent = node.parent; - if (!parent) { - return false; - } - - if (luau.isMapField(parent) && node === parent.index) { - return true; - } - - if (luau.isComputedIndexExpression(parent) && node === parent.index) { - return true; - } - - if (luau.isSet(parent)) { - return true; - } - - return false; -} - -export function renderStringLiteral(state: RenderState, node: luau.StringLiteral) { - const isMultiline = node.value.includes("\n"); - if (!isMultiline && !node.value.includes('"')) { - return `"${node.value}"`; - } else if (!isMultiline && !node.value.includes("'")) { - return `'${node.value}'`; - } else { - const eqStr = getSafeBracketEquals(node.value); - const spacing = needsBracketSpacing(node) ? " " : ""; - return `${spacing}[${eqStr}[${node.value}]${eqStr}]${spacing}`; - } -} diff --git a/src/LuauRenderer/nodes/expressions/renderUnaryExpression.ts b/src/LuauRenderer/nodes/expressions/renderUnaryExpression.ts deleted file mode 100644 index a248607..0000000 --- a/src/LuauRenderer/nodes/expressions/renderUnaryExpression.ts +++ /dev/null @@ -1,48 +0,0 @@ -import luau from "../../../LuauAST"; -import { render, RenderState } from "../../../LuauRenderer"; -import { needsParentheses } from "../../../LuauRenderer/util/needsParentheses"; - -function needsInnerParentheses(node: luau.UnaryExpression) { - // #{} and -{} are invalid - if ((node.operator === "#" || node.operator === "-") && luau.isTable(node.expression)) { - return true; - } - - return false; -} - -function needsSpace(node: luau.UnaryExpression) { - // not always needs a space - if (node.operator === "not") { - return true; - } - - // "--" will create a comment! - if (luau.isUnaryExpression(node.expression) && node.expression.operator === "-") { - // previous expression was also "-" - return true; - } - - return false; -} - -export function renderUnaryExpression(state: RenderState, node: luau.UnaryExpression) { - let expStr = render(state, node.expression); - let opStr = node.operator; - - if (needsSpace(node)) { - opStr += " "; - } - - if (needsInnerParentheses(node)) { - expStr = `(${expStr})`; - } - - let result = `${opStr}${expStr}`; - - if (needsParentheses(node)) { - result = `(${result})`; - } - - return result; -} diff --git a/src/LuauRenderer/nodes/fields/renderMapField.ts b/src/LuauRenderer/nodes/fields/renderMapField.ts deleted file mode 100644 index 501243b..0000000 --- a/src/LuauRenderer/nodes/fields/renderMapField.ts +++ /dev/null @@ -1,13 +0,0 @@ -import luau from "../../../LuauAST"; -import { render, RenderState } from "../../../LuauRenderer"; - -export function renderMapField(state: RenderState, node: luau.MapField) { - const { index, value } = node; - const valueStr = render(state, value); - if (luau.isStringLiteral(index) && luau.isValidIdentifier(index.value)) { - return `${index.value} = ${valueStr}`; - } else { - const indexStr = render(state, index); - return `[${indexStr}] = ${valueStr}`; - } -} diff --git a/src/LuauRenderer/nodes/statements/renderAssignment.ts b/src/LuauRenderer/nodes/statements/renderAssignment.ts deleted file mode 100644 index 0bf48a7..0000000 --- a/src/LuauRenderer/nodes/statements/renderAssignment.ts +++ /dev/null @@ -1,23 +0,0 @@ -import luau from "../../../LuauAST"; -import { assert } from "../../../LuauAST/util/assert"; -import { render, RenderState } from "../../../LuauRenderer"; - -export function renderAssignment(state: RenderState, node: luau.Assignment) { - let leftStr: string; - if (luau.list.isList(node.left)) { - assert(!luau.list.isEmpty(node.left)); - leftStr = luau.list.mapToArray(node.left, id => render(state, id)).join(", "); - } else { - leftStr = render(state, node.left); - } - - let rightStr: string; - if (luau.list.isList(node.right)) { - assert(!luau.list.isEmpty(node.right)); - rightStr = luau.list.mapToArray(node.right, expression => render(state, expression)).join(", "); - } else { - rightStr = render(state, node.right); - } - - return state.line(`${leftStr} ${node.operator} ${rightStr}`, node); -} diff --git a/src/LuauRenderer/nodes/statements/renderBreakStatement.ts b/src/LuauRenderer/nodes/statements/renderBreakStatement.ts deleted file mode 100644 index 570b4d6..0000000 --- a/src/LuauRenderer/nodes/statements/renderBreakStatement.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { RenderState } from "../../../LuauRenderer"; - -export function renderBreakStatement(state: RenderState) { - return state.line(`break`); -} diff --git a/src/LuauRenderer/nodes/statements/renderCallStatement.ts b/src/LuauRenderer/nodes/statements/renderCallStatement.ts deleted file mode 100644 index f1bc938..0000000 --- a/src/LuauRenderer/nodes/statements/renderCallStatement.ts +++ /dev/null @@ -1,6 +0,0 @@ -import luau from "../../../LuauAST"; -import { render, RenderState } from "../../../LuauRenderer"; - -export function renderCallStatement(state: RenderState, node: luau.CallStatement) { - return state.line(`${render(state, node.expression)}`, node); -} diff --git a/src/LuauRenderer/nodes/statements/renderComment.ts b/src/LuauRenderer/nodes/statements/renderComment.ts deleted file mode 100644 index 30130dd..0000000 --- a/src/LuauRenderer/nodes/statements/renderComment.ts +++ /dev/null @@ -1,16 +0,0 @@ -import luau from "../../../LuauAST"; -import { RenderState } from "../../../LuauRenderer"; -import { getSafeBracketEquals } from "../../../LuauRenderer/util/getSafeBracketEquals"; - -export function renderComment(state: RenderState, node: luau.Comment) { - const lines = node.text.split("\n"); - if (lines.length > 1) { - const eqStr = getSafeBracketEquals(node.text); - let result = state.line(`--[${eqStr}[`); - result += state.block(() => lines.map(line => state.line(line)).join("")); - result += state.line(`]${eqStr}]`); - return result; - } else { - return lines.map(line => state.line(`--${line}`)).join(""); - } -} diff --git a/src/LuauRenderer/nodes/statements/renderContinueStatement.ts b/src/LuauRenderer/nodes/statements/renderContinueStatement.ts deleted file mode 100644 index d983f52..0000000 --- a/src/LuauRenderer/nodes/statements/renderContinueStatement.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { RenderState } from "../../../LuauRenderer"; - -export function renderContinueStatement(state: RenderState) { - return state.line(`continue`); -} diff --git a/src/LuauRenderer/nodes/statements/renderDoStatement.ts b/src/LuauRenderer/nodes/statements/renderDoStatement.ts deleted file mode 100644 index c90e9c4..0000000 --- a/src/LuauRenderer/nodes/statements/renderDoStatement.ts +++ /dev/null @@ -1,11 +0,0 @@ -import luau from "../../../LuauAST"; -import { RenderState } from "../../../LuauRenderer"; -import { renderStatements } from "../../../LuauRenderer/util/renderStatements"; - -export function renderDoStatement(state: RenderState, node: luau.DoStatement) { - let result = ""; - result += state.line(`do`); - result += state.block(() => renderStatements(state, node.statements)); - result += state.line(`end`); - return result; -} diff --git a/src/LuauRenderer/nodes/statements/renderForStatement.ts b/src/LuauRenderer/nodes/statements/renderForStatement.ts deleted file mode 100644 index 4497bed..0000000 --- a/src/LuauRenderer/nodes/statements/renderForStatement.ts +++ /dev/null @@ -1,15 +0,0 @@ -import luau from "../../../LuauAST"; -import { render, RenderState } from "../../../LuauRenderer"; -import { renderStatements } from "../../../LuauRenderer/util/renderStatements"; - -export function renderForStatement(state: RenderState, node: luau.ForStatement) { - const idsStr = luau.list.mapToArray(node.ids, id => render(state, id)).join(", ") || "_"; - const expStr = render(state, node.expression); - - let result = ""; - result += state.line(`for ${idsStr} in ${expStr} do`); - result += state.block(() => renderStatements(state, node.statements)); - result += state.line(`end`); - - return result; -} diff --git a/src/LuauRenderer/nodes/statements/renderFunctionDeclaration.ts b/src/LuauRenderer/nodes/statements/renderFunctionDeclaration.ts deleted file mode 100644 index 6e9baf3..0000000 --- a/src/LuauRenderer/nodes/statements/renderFunctionDeclaration.ts +++ /dev/null @@ -1,19 +0,0 @@ -import luau from "../../../LuauAST"; -import { assert } from "../../../LuauAST/util/assert"; -import { render, RenderState } from "../../../LuauRenderer"; -import { renderParameters } from "../../../LuauRenderer/util/renderParameters"; -import { renderStatements } from "../../../LuauRenderer/util/renderStatements"; - -export function renderFunctionDeclaration(state: RenderState, node: luau.FunctionDeclaration) { - if (node.localize) { - assert(luau.isAnyIdentifier(node.name), "local function cannot be a property"); - } - const nameStr = render(state, node.name); - const paramStr = renderParameters(state, node); - - let result = ""; - result += state.line(`${node.localize ? "local " : ""}function ${nameStr}(${paramStr})`); - result += state.block(() => renderStatements(state, node.statements)); - result += state.line(`end`); - return result; -} diff --git a/src/LuauRenderer/nodes/statements/renderIfStatement.ts b/src/LuauRenderer/nodes/statements/renderIfStatement.ts deleted file mode 100644 index 66dd64b..0000000 --- a/src/LuauRenderer/nodes/statements/renderIfStatement.ts +++ /dev/null @@ -1,28 +0,0 @@ -import luau from "../../../LuauAST"; -import { render, RenderState } from "../../../LuauRenderer"; -import { renderStatements } from "../../../LuauRenderer/util/renderStatements"; - -export function renderIfStatement(state: RenderState, node: luau.IfStatement) { - let result = ""; - - result += state.line(`if ${render(state, node.condition)} then`); - result += state.block(() => renderStatements(state, node.statements)); - - let currentElseBody = node.elseBody; - while (luau.isNode(currentElseBody)) { - const statements = currentElseBody.statements; - result += state.line(`elseif ${render(state, currentElseBody.condition)} then`); - result += state.block(() => renderStatements(state, statements)); - currentElseBody = currentElseBody.elseBody; - } - - if (currentElseBody && luau.list.isNonEmpty(currentElseBody)) { - result += state.line(`else`); - const statements = currentElseBody; - result += state.block(() => renderStatements(state, statements)); - } - - result += state.line(`end`); - - return result; -} diff --git a/src/LuauRenderer/nodes/statements/renderMethodDeclaration.ts b/src/LuauRenderer/nodes/statements/renderMethodDeclaration.ts deleted file mode 100644 index 18df8e8..0000000 --- a/src/LuauRenderer/nodes/statements/renderMethodDeclaration.ts +++ /dev/null @@ -1,12 +0,0 @@ -import luau from "../../../LuauAST"; -import { render, RenderState } from "../../../LuauRenderer"; -import { renderParameters } from "../../../LuauRenderer/util/renderParameters"; -import { renderStatements } from "../../../LuauRenderer/util/renderStatements"; - -export function renderMethodDeclaration(state: RenderState, node: luau.MethodDeclaration) { - let result = ""; - result += state.line(`function ${render(state, node.expression)}:${node.name}(${renderParameters(state, node)})`); - result += state.block(() => renderStatements(state, node.statements)); - result += state.line(`end`); - return result; -} diff --git a/src/LuauRenderer/nodes/statements/renderNumericForStatement.ts b/src/LuauRenderer/nodes/statements/renderNumericForStatement.ts deleted file mode 100644 index 68edcd5..0000000 --- a/src/LuauRenderer/nodes/statements/renderNumericForStatement.ts +++ /dev/null @@ -1,24 +0,0 @@ -import luau from "../../../LuauAST"; -import { render, RenderState } from "../../../LuauRenderer"; -import { renderStatements } from "../../../LuauRenderer/util/renderStatements"; - -export function renderNumericForStatement(state: RenderState, node: luau.NumericForStatement) { - const idStr = render(state, node.id); - const startStr = render(state, node.start); - const endStr = render(state, node.end); - - let predicateStr = `${startStr}, ${endStr}`; - - // step of 1 can be omitted - if (node.step && (!luau.isNumberLiteral(node.step) || Number(node.step.value) !== 1)) { - const stepStr = render(state, node.step); - predicateStr += `, ${stepStr}`; - } - - let result = ""; - result += state.line(`for ${idStr} = ${predicateStr} do`); - result += state.block(() => renderStatements(state, node.statements)); - result += state.line(`end`); - - return result; -} diff --git a/src/LuauRenderer/nodes/statements/renderRepeatStatement.ts b/src/LuauRenderer/nodes/statements/renderRepeatStatement.ts deleted file mode 100644 index bdfd0d8..0000000 --- a/src/LuauRenderer/nodes/statements/renderRepeatStatement.ts +++ /dev/null @@ -1,11 +0,0 @@ -import luau from "../../../LuauAST"; -import { render, RenderState } from "../../../LuauRenderer"; -import { renderStatements } from "../../../LuauRenderer/util/renderStatements"; - -export function renderRepeatStatement(state: RenderState, node: luau.RepeatStatement) { - let result = ""; - result += state.line(`repeat`); - result += state.block(() => renderStatements(state, node.statements)); - result += state.line(`until ${render(state, node.condition)}`); - return result; -} diff --git a/src/LuauRenderer/nodes/statements/renderReturnStatement.ts b/src/LuauRenderer/nodes/statements/renderReturnStatement.ts deleted file mode 100644 index 44ba2fb..0000000 --- a/src/LuauRenderer/nodes/statements/renderReturnStatement.ts +++ /dev/null @@ -1,9 +0,0 @@ -import luau from "../../../LuauAST"; -import { render, RenderState } from "../../../LuauRenderer"; - -export function renderReturnStatement(state: RenderState, node: luau.ReturnStatement) { - const expStr = luau.list.isList(node.expression) - ? luau.list.mapToArray(node.expression, exp => render(state, exp)).join(", ") - : render(state, node.expression); - return state.line(`return ${expStr}`); -} diff --git a/src/LuauRenderer/nodes/statements/renderVariableDeclaration.ts b/src/LuauRenderer/nodes/statements/renderVariableDeclaration.ts deleted file mode 100644 index 379fcd7..0000000 --- a/src/LuauRenderer/nodes/statements/renderVariableDeclaration.ts +++ /dev/null @@ -1,26 +0,0 @@ -import luau from "../../../LuauAST"; -import { assert } from "../../../LuauAST/util/assert"; -import { render, RenderState } from "../../../LuauRenderer"; - -export function renderVariableDeclaration(state: RenderState, node: luau.VariableDeclaration) { - let leftStr: string; - if (luau.list.isList(node.left)) { - assert(!luau.list.isEmpty(node.left)); - leftStr = luau.list.mapToArray(node.left, id => render(state, id)).join(", "); - } else { - leftStr = render(state, node.left); - } - - if (node.right) { - let rightStr: string; - if (luau.list.isList(node.right)) { - assert(!luau.list.isEmpty(node.right)); - rightStr = luau.list.mapToArray(node.right, expression => render(state, expression)).join(", "); - } else { - rightStr = render(state, node.right); - } - return state.line(`local ${leftStr} = ${rightStr}`, node); - } else { - return state.line(`local ${leftStr}`, node); - } -} diff --git a/src/LuauRenderer/nodes/statements/renderWhileStatement.ts b/src/LuauRenderer/nodes/statements/renderWhileStatement.ts deleted file mode 100644 index fe87e8a..0000000 --- a/src/LuauRenderer/nodes/statements/renderWhileStatement.ts +++ /dev/null @@ -1,11 +0,0 @@ -import luau from "../../../LuauAST"; -import { render, RenderState } from "../../../LuauRenderer"; -import { renderStatements } from "../../../LuauRenderer/util/renderStatements"; - -export function renderWhileStatement(state: RenderState, node: luau.WhileStatement) { - let result = ""; - result += state.line(`while ${render(state, node.condition)} do`); - result += state.block(() => renderStatements(state, node.statements)); - result += state.line(`end`); - return result; -} diff --git a/src/LuauRenderer/render.ts b/src/LuauRenderer/render.ts deleted file mode 100644 index 4d34f1b..0000000 --- a/src/LuauRenderer/render.ts +++ /dev/null @@ -1,135 +0,0 @@ -import luau from "../LuauAST"; -import { assert } from "../LuauAST/util/assert"; -import { getKindName } from "../LuauAST/util/getKindName"; -import { renderCallExpression } from "../LuauRenderer/nodes/expressions/indexable/renderCallExpression"; -import { renderComputedIndexExpression } from "../LuauRenderer/nodes/expressions/indexable/renderComputedIndexExpression"; -import { renderIdentifier } from "../LuauRenderer/nodes/expressions/indexable/renderIdentifier"; -import { renderMethodCallExpression } from "../LuauRenderer/nodes/expressions/indexable/renderMethodCallExpression"; -import { renderParenthesizedExpression } from "../LuauRenderer/nodes/expressions/indexable/renderParenthesizedExpression"; -import { renderPropertyAccessExpression } from "../LuauRenderer/nodes/expressions/indexable/renderPropertyAccessExpression"; -import { renderTemporaryIdentifier } from "../LuauRenderer/nodes/expressions/indexable/renderTemporaryIdentifier"; -import { renderArray } from "../LuauRenderer/nodes/expressions/renderArray"; -import { renderBinaryExpression } from "../LuauRenderer/nodes/expressions/renderBinaryExpression"; -import { renderFunctionExpression } from "../LuauRenderer/nodes/expressions/renderFunctionExpression"; -import { renderIfExpression } from "../LuauRenderer/nodes/expressions/renderIfExpression"; -import { renderMap } from "../LuauRenderer/nodes/expressions/renderMap"; -import { renderMixedTable } from "../LuauRenderer/nodes/expressions/renderMixedTable"; -import { renderNumberLiteral } from "../LuauRenderer/nodes/expressions/renderNumberLiteral"; -import { renderSet } from "../LuauRenderer/nodes/expressions/renderSet"; -import { renderStringLiteral } from "../LuauRenderer/nodes/expressions/renderStringLiteral"; -import { renderUnaryExpression } from "../LuauRenderer/nodes/expressions/renderUnaryExpression"; -import { renderMapField } from "../LuauRenderer/nodes/fields/renderMapField"; -import { renderAssignment } from "../LuauRenderer/nodes/statements/renderAssignment"; -import { renderBreakStatement } from "../LuauRenderer/nodes/statements/renderBreakStatement"; -import { renderCallStatement } from "../LuauRenderer/nodes/statements/renderCallStatement"; -import { renderComment } from "../LuauRenderer/nodes/statements/renderComment"; -import { renderContinueStatement } from "../LuauRenderer/nodes/statements/renderContinueStatement"; -import { renderDoStatement } from "../LuauRenderer/nodes/statements/renderDoStatement"; -import { renderForStatement } from "../LuauRenderer/nodes/statements/renderForStatement"; -import { renderFunctionDeclaration } from "../LuauRenderer/nodes/statements/renderFunctionDeclaration"; -import { renderIfStatement } from "../LuauRenderer/nodes/statements/renderIfStatement"; -import { renderMethodDeclaration } from "../LuauRenderer/nodes/statements/renderMethodDeclaration"; -import { renderNumericForStatement } from "../LuauRenderer/nodes/statements/renderNumericForStatement"; -import { renderRepeatStatement } from "../LuauRenderer/nodes/statements/renderRepeatStatement"; -import { renderReturnStatement } from "../LuauRenderer/nodes/statements/renderReturnStatement"; -import { renderVariableDeclaration } from "../LuauRenderer/nodes/statements/renderVariableDeclaration"; -import { renderWhileStatement } from "../LuauRenderer/nodes/statements/renderWhileStatement"; -import { RenderState } from "../LuauRenderer/RenderState"; -import { solveTempIds } from "../LuauRenderer/solveTempIds"; -import { identity } from "../LuauRenderer/util/identity"; -import { renderStatements } from "../LuauRenderer/util/renderStatements"; -import { visit } from "../LuauRenderer/util/visit"; - -type Renderer = (state: RenderState, node: luau.NodeByKind[T]) => string; - -const KIND_TO_RENDERER = identity<{ [K in luau.SyntaxKind]: Renderer }>({ - // indexable expressions - [luau.SyntaxKind.Identifier]: renderIdentifier, - [luau.SyntaxKind.TemporaryIdentifier]: renderTemporaryIdentifier, - [luau.SyntaxKind.ComputedIndexExpression]: renderComputedIndexExpression, - [luau.SyntaxKind.PropertyAccessExpression]: renderPropertyAccessExpression, - [luau.SyntaxKind.CallExpression]: renderCallExpression, - [luau.SyntaxKind.MethodCallExpression]: renderMethodCallExpression, - [luau.SyntaxKind.ParenthesizedExpression]: renderParenthesizedExpression, - - // expressions - [luau.SyntaxKind.None]: () => assert(false, "Cannot render None"), - [luau.SyntaxKind.NilLiteral]: () => "nil", - [luau.SyntaxKind.FalseLiteral]: () => "false", - [luau.SyntaxKind.TrueLiteral]: () => "true", - [luau.SyntaxKind.NumberLiteral]: renderNumberLiteral, - [luau.SyntaxKind.StringLiteral]: renderStringLiteral, - [luau.SyntaxKind.VarArgsLiteral]: () => "...", - [luau.SyntaxKind.FunctionExpression]: renderFunctionExpression, - [luau.SyntaxKind.BinaryExpression]: renderBinaryExpression, - [luau.SyntaxKind.UnaryExpression]: renderUnaryExpression, - [luau.SyntaxKind.IfExpression]: renderIfExpression, - [luau.SyntaxKind.Array]: renderArray, - [luau.SyntaxKind.Map]: renderMap, - [luau.SyntaxKind.Set]: renderSet, - [luau.SyntaxKind.MixedTable]: renderMixedTable, - - // statements - [luau.SyntaxKind.Assignment]: renderAssignment, - [luau.SyntaxKind.BreakStatement]: renderBreakStatement, - [luau.SyntaxKind.CallStatement]: renderCallStatement, - [luau.SyntaxKind.ContinueStatement]: renderContinueStatement, - [luau.SyntaxKind.DoStatement]: renderDoStatement, - [luau.SyntaxKind.WhileStatement]: renderWhileStatement, - [luau.SyntaxKind.RepeatStatement]: renderRepeatStatement, - [luau.SyntaxKind.IfStatement]: renderIfStatement, - [luau.SyntaxKind.NumericForStatement]: renderNumericForStatement, - [luau.SyntaxKind.ForStatement]: renderForStatement, - [luau.SyntaxKind.FunctionDeclaration]: renderFunctionDeclaration, - [luau.SyntaxKind.MethodDeclaration]: renderMethodDeclaration, - [luau.SyntaxKind.VariableDeclaration]: renderVariableDeclaration, - [luau.SyntaxKind.ReturnStatement]: renderReturnStatement, - [luau.SyntaxKind.Comment]: renderComment, - - // fields - [luau.SyntaxKind.MapField]: renderMapField, -}); - -/** - * Returns a string that represents the given syntax node, `node`, as Luau code. - * Recursively called until the node is completely rendered. - * @param state The state of the current rendering process. - * @param node The node to render as Luau code. - */ -export function render(state: RenderState, node: luau.Node): string { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return KIND_TO_RENDERER[node.kind](state, node as any); -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -function debugAST(ast: luau.List) { - let indent = ""; - - const pushIndent = () => (indent += "\t"); - const popIndent = () => (indent = indent.substring(1)); - - visit(ast, { - before: node => { - // eslint-disable-next-line no-console - console.log(`${indent}${getKindName(node.kind)}`); - pushIndent(); - }, - after: () => { - popIndent(); - }, - }); -} - -/** - * Returns a string that represents the given syntax tree, `ast`, as Luau code. - */ -export function renderAST(ast: luau.List): string { - const state = new RenderState(); - - solveTempIds(state, ast); - - // useful for visualizing the Luau AST structure - // debugAST(ast); - - return renderStatements(state, ast); -} diff --git a/src/LuauRenderer/solveTempIds.ts b/src/LuauRenderer/solveTempIds.ts deleted file mode 100644 index d8f8263..0000000 --- a/src/LuauRenderer/solveTempIds.ts +++ /dev/null @@ -1,134 +0,0 @@ -import luau from "../LuauAST"; -import { assert } from "../LuauAST/util/assert"; -import { RenderState } from "../LuauRenderer"; -import { visit } from "../LuauRenderer/util/visit"; - -function isFullyScopedNode(node: luau.Node): boolean { - return luau.isForStatement(node) || luau.isNumericForStatement(node) || luau.isFunctionLike(node); -} - -function isScopeEdge(node: luau.Node, edge: "head" | "tail"): boolean { - if (node.parent) { - // is the first statement in a block that creates scope - if (luau.hasStatements(node.parent)) { - if (node === node.parent.statements[edge]?.value) { - return true; - } - } - - // non-list elseBody would have the elseBody itself as a parent, - // which would be a luau.IfStatement and handled above - if ( - luau.isIfStatement(node.parent) && - luau.list.isList(node.parent.elseBody) && - node === node.parent.elseBody[edge]?.value - ) { - return true; - } - } - - return false; -} - -const isScopeStart = (node: luau.Node) => isScopeEdge(node, "head"); -const isScopeEnd = (node: luau.Node) => isScopeEdge(node, "tail"); - -interface Scope { - ids: Set; - lastTry: Map; - parent?: Scope; -} - -function createScope(parent?: Scope): Scope { - return { - ids: new Set(), - lastTry: new Map(), - parent, - }; -} - -function scopeHasId(scope: Scope, id: string): boolean { - if (scope.ids.has(id)) { - return true; - } - if (scope.parent) { - return scopeHasId(scope.parent, id); - } - return false; -} - -export function solveTempIds(state: RenderState, ast: luau.List | luau.Node) { - const tempIdsToProcess = new Array(); - const nodesToScopes = new Map(); - - const scopeStack = [createScope()]; - - function pushScopeStack() { - scopeStack.push(createScope(peekScopeStack())); - } - - function popScopeStack() { - return scopeStack.pop(); - } - - function peekScopeStack() { - const scope = scopeStack[scopeStack.length - 1]; - assert(scope); - return scope; - } - - function registerId(name: string) { - peekScopeStack().ids.add(name); - } - - visit(ast, { - before: node => { - if (isFullyScopedNode(node)) pushScopeStack(); - if (isScopeStart(node)) pushScopeStack(); - - if (luau.isTemporaryIdentifier(node)) { - nodesToScopes.set(node, peekScopeStack()); - tempIdsToProcess.push(node); - } else if (luau.isVariableDeclaration(node)) { - if (luau.list.isList(node.left)) { - luau.list.forEach(node.left, node => { - if (luau.isIdentifier(node)) { - registerId(node.name); - } - }); - } else if (luau.isIdentifier(node.left)) { - registerId(node.left.name); - } - } else if (luau.isFunctionLike(node)) { - luau.list.forEach(node.parameters, node => { - if (luau.isIdentifier(node)) { - registerId(node.name); - } - }); - } - }, - after: node => { - if (isFullyScopedNode(node)) popScopeStack(); - if (isScopeEnd(node)) popScopeStack(); - }, - }); - - for (const tempId of tempIdsToProcess) { - if (state.seenTempNodes.get(tempId.id) === undefined) { - const scope = nodesToScopes.get(tempId); - assert(scope); - - const seperator = tempId.name === "" ? "" : "_"; - - let input = `_${tempId.name}`; - let i = scope.lastTry.get(input) ?? 1; - while (scopeHasId(scope, input)) { - input = `_${tempId.name}${seperator}${i++}`; - } - scope.lastTry.set(input, i); - scope.ids.add(input); - - state.seenTempNodes.set(tempId.id, input); - } - } -} diff --git a/src/LuauRenderer/util/getEnding.ts b/src/LuauRenderer/util/getEnding.ts deleted file mode 100644 index fb3dbbc..0000000 --- a/src/LuauRenderer/util/getEnding.ts +++ /dev/null @@ -1,101 +0,0 @@ -import luau from "../../LuauAST"; -import { assert } from "../../LuauAST/util/assert"; -import { RenderState } from "../../LuauRenderer"; - -function endsWithIndexableExpressionInner(node: luau.Expression): boolean { - if (luau.isIndexableExpression(node)) { - // `a` or `(a)` or `a.b` or `a[b]` or `a()` - return true; - } else if (luau.isBinaryExpression(node)) { - // `a + b` - return endsWithIndexableExpressionInner(node.right); - } else if (luau.isUnaryExpression(node)) { - // `-a` - return endsWithIndexableExpressionInner(node.expression); - } else if (luau.isIfExpression(node)) { - // `if a then b else c` - // `if a then b elseif c then d else e` - return endsWithIndexableExpressionInner(node.alternative); - } - return false; -} - -function endsWithIndexableExpression(node: luau.Statement) { - if (luau.isCallStatement(node)) { - // `a()` - return true; - } else if (luau.isVariableDeclaration(node) || luau.isAssignment(node)) { - // `local a = b` or `a = b` or `local a` or `local a, b` - let furthestRight: luau.Expression; - if (node.right) { - if (luau.list.isList(node.right)) { - assert(luau.list.isNonEmpty(node.right)); - furthestRight = node.right.tail.value; - } else { - furthestRight = node.right; - } - } else if (luau.list.isList(node.left)) { - assert(luau.list.isNonEmpty(node.left)); - furthestRight = node.left.tail.value; - } else { - furthestRight = node.left; - } - return endsWithIndexableExpressionInner(furthestRight); - } - return false; -} - -function startsWithParenthesisInner(node: luau.Expression): boolean { - if (luau.isParenthesizedExpression(node)) { - // `(a)` - return true; - } else if (luau.isCall(node) || luau.isPropertyAccessExpression(node) || luau.isComputedIndexExpression(node)) { - // `(a)()` or `(a):b()` or `(a).b` or `(a)[b]` - return startsWithParenthesisInner(node.expression); - } - return false; -} - -function startsWithParenthesis(node: luau.Statement) { - if (luau.isCallStatement(node)) { - // `(a)()` - return startsWithParenthesisInner(node.expression.expression); - } else if (luau.isAssignment(node)) { - if (luau.list.isList(node.left)) { - // `(a).b, c = d` - assert(luau.list.isNonEmpty(node.left)); - return startsWithParenthesisInner(node.left.head.value); - } else { - // `(a).b = c` - return startsWithParenthesisInner(node.left); - } - } - return false; -} - -function getNextNonComment(state: RenderState) { - let listNode = state.peekListNode()?.next; - while (listNode && luau.isComment(listNode.value)) { - listNode = listNode.next; - } - return listNode?.value; -} - -/** - * Resolves if the given statement needs to end with a `;` or not. - * - * Used to avoid "ambiguous syntax" errors in Luau. - * - * This is only necessary in statements which can end in an IndexableExpression: - * - CallStatement - * - VariableDeclaration - * - Assignment - */ -export function getEnding(state: RenderState, node: luau.Statement) { - const nextStatement = getNextNonComment(state); - if (nextStatement !== undefined && endsWithIndexableExpression(node) && startsWithParenthesis(nextStatement)) { - return ";"; - } else { - return ""; - } -} diff --git a/src/LuauRenderer/util/getOrSetDefault.ts b/src/LuauRenderer/util/getOrSetDefault.ts deleted file mode 100644 index f94d4eb..0000000 --- a/src/LuauRenderer/util/getOrSetDefault.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Attempts to `map.get(key)`. If the value is `undefined`, it will run `map.set(key, getDefaultValue())` and return that instead. - * @param map The map to get the value from. - * @param key A key that is possibly mapped to a value in `map`. - * @param getDefaultValue A function that returns a default value that `key` should be mapped to, if `key` is not already mapped to something. - */ -export function getOrSetDefault(map: Map, key: K, getDefaultValue: () => V) { - let value = map.get(key); - if (value === undefined) { - value = getDefaultValue(); - map.set(key, value); - } - return value; -} diff --git a/src/LuauRenderer/util/getSafeBracketEquals.ts b/src/LuauRenderer/util/getSafeBracketEquals.ts deleted file mode 100644 index b8d08aa..0000000 --- a/src/LuauRenderer/util/getSafeBracketEquals.ts +++ /dev/null @@ -1,7 +0,0 @@ -export function getSafeBracketEquals(str: string) { - let amtEquals = 0; - while (str.includes(`]${"=".repeat(amtEquals)}]`) || str.endsWith(`]${"=".repeat(amtEquals)}`)) { - amtEquals++; - } - return "=".repeat(amtEquals); -} diff --git a/src/LuauRenderer/util/identity.ts b/src/LuauRenderer/util/identity.ts deleted file mode 100644 index e8f9c9e..0000000 --- a/src/LuauRenderer/util/identity.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function identity(value: T): T { - return value; -} diff --git a/src/LuauRenderer/util/needsParentheses.ts b/src/LuauRenderer/util/needsParentheses.ts deleted file mode 100644 index 6b258fc..0000000 --- a/src/LuauRenderer/util/needsParentheses.ts +++ /dev/null @@ -1,65 +0,0 @@ -import luau from "../../LuauAST"; -import { assert } from "../../LuauAST/util/assert"; - -// https://www.lua.org/manual/5.1/manual.html#2.5.6 -/* - 1. or - 2. and - 3. < > <= >= ~= == - 4. .. - 5. + - - 6. * / % - 7. not # - (unary) - 8. ^ -*/ - -const IF_EXPRESSION_PRECEDENCE = 1; - -const UNARY_OPERATOR_PRECEDENCE: { [K in luau.UnaryOperator]: number } = { - not: 7, - "#": 7, - "-": 7, -}; - -const BINARY_OPERATOR_PRECEDENCE: { [K in luau.BinaryOperator]: number } = { - or: 1, - and: 2, - "<": 3, - ">": 3, - "<=": 3, - ">=": 3, - "~=": 3, - "==": 3, - "..": 4, - "+": 5, - "-": 5, - "*": 6, - "/": 6, - "%": 6, - "^": 8, -}; - -// are these all the expression types that need to be considered..? -function getPrecedence(node: luau.ExpressionWithPrecedence) { - if (luau.isIfExpression(node)) { - return IF_EXPRESSION_PRECEDENCE; - } else if (luau.isBinaryExpression(node)) { - return BINARY_OPERATOR_PRECEDENCE[node.operator]; - } else if (luau.isUnaryExpression(node)) { - return UNARY_OPERATOR_PRECEDENCE[node.operator]; - } - assert(false); -} - -export function needsParentheses(node: luau.ExpressionWithPrecedence) { - if (node.parent && luau.isExpressionWithPrecedence(node.parent)) { - const nodePrecedence = getPrecedence(node); - const parentPrecedence = getPrecedence(node.parent); - if (nodePrecedence < parentPrecedence) { - return true; - } else if (nodePrecedence === parentPrecedence) { - return luau.isBinaryExpression(node.parent) && node === node.parent.right; - } - } - return false; -} diff --git a/src/LuauRenderer/util/renderArguments.ts b/src/LuauRenderer/util/renderArguments.ts deleted file mode 100644 index 11473e8..0000000 --- a/src/LuauRenderer/util/renderArguments.ts +++ /dev/null @@ -1,7 +0,0 @@ -import luau from "../../LuauAST"; -import { render, RenderState } from "../../LuauRenderer"; - -/** Renders the given list of expressions into a string separated by commas */ -export function renderArguments(state: RenderState, expressions: luau.List) { - return luau.list.mapToArray(expressions, v => render(state, v)).join(", "); -} diff --git a/src/LuauRenderer/util/renderParameters.ts b/src/LuauRenderer/util/renderParameters.ts deleted file mode 100644 index 9325696..0000000 --- a/src/LuauRenderer/util/renderParameters.ts +++ /dev/null @@ -1,15 +0,0 @@ -import luau from "../../LuauAST"; -import { render, RenderState } from "../../LuauRenderer"; - -/** - * Renders the given list of identifiers inside of `node` into a string sepearted by commas - * - * Adds `...` onto the end if node.hasDotDotDot is true - */ -export function renderParameters(state: RenderState, node: luau.HasParameters) { - const paramStrs = luau.list.mapToArray(node.parameters, param => render(state, param)); - if (node.hasDotDotDot) { - paramStrs.push("..."); - } - return paramStrs.join(", "); -} diff --git a/src/LuauRenderer/util/renderStatements.ts b/src/LuauRenderer/util/renderStatements.ts deleted file mode 100644 index 7080948..0000000 --- a/src/LuauRenderer/util/renderStatements.ts +++ /dev/null @@ -1,30 +0,0 @@ -import luau from "../../LuauAST"; -import { assert } from "../../LuauAST/util/assert"; -import { render, RenderState } from "../../LuauRenderer"; - -/** - * Renders the given list of statements. - * - * Pushes each listNode onto the state.listNodesStack as it gets - * rendered to give context to other statements as they render. - * Useful for getting the next or previous sibling statement. - */ -export function renderStatements(state: RenderState, statements: luau.List) { - let result = ""; - let listNode = statements.head; - let hasFinalStatement = false; - while (listNode !== undefined) { - assert( - !hasFinalStatement || luau.isComment(listNode.value), - "Cannot render statement after break, continue, or return!", - ); - hasFinalStatement ||= luau.isFinalStatement(listNode.value); - - state.pushListNode(listNode); - result += render(state, listNode.value); - state.popListNode(); - - listNode = listNode.next; - } - return result; -} diff --git a/src/LuauRenderer/util/visit.ts b/src/LuauRenderer/util/visit.ts deleted file mode 100644 index efd3984..0000000 --- a/src/LuauRenderer/util/visit.ts +++ /dev/null @@ -1,162 +0,0 @@ -import luau from "../../LuauAST"; -import { identity } from "../../LuauRenderer/util/identity"; - -export interface Visitor { - before?: (node: luau.Node) => void; - after?: (node: luau.Node) => void; -} - -type VisitStrategy = (node: luau.NodeByKind[T], visitor: Visitor) => void; - -const NOOP = () => {}; - -const KIND_TO_VISITOR = identity<{ [K in luau.SyntaxKind]: VisitStrategy }>({ - // indexable expressions - [luau.SyntaxKind.Identifier]: NOOP, - [luau.SyntaxKind.TemporaryIdentifier]: NOOP, - [luau.SyntaxKind.ComputedIndexExpression]: (node, visitor) => { - visitNode(node.expression, visitor); - visitNode(node.index, visitor); - }, - [luau.SyntaxKind.PropertyAccessExpression]: (node, visitor) => visitNode(node.expression, visitor), - [luau.SyntaxKind.CallExpression]: (node, visitor) => { - visitNode(node.expression, visitor); - visitList(node.args, visitor); - }, - [luau.SyntaxKind.MethodCallExpression]: (node, visitor) => { - visitNode(node.expression, visitor); - visitList(node.args, visitor); - }, - [luau.SyntaxKind.ParenthesizedExpression]: (node, visitor) => visitNode(node.expression, visitor), - - // expressions - [luau.SyntaxKind.None]: NOOP, - [luau.SyntaxKind.NilLiteral]: NOOP, - [luau.SyntaxKind.FalseLiteral]: NOOP, - [luau.SyntaxKind.TrueLiteral]: NOOP, - [luau.SyntaxKind.NumberLiteral]: NOOP, - [luau.SyntaxKind.StringLiteral]: NOOP, - [luau.SyntaxKind.VarArgsLiteral]: NOOP, - [luau.SyntaxKind.FunctionExpression]: (node, visitor) => { - visitList(node.parameters, visitor); - visitList(node.statements, visitor); - }, - [luau.SyntaxKind.BinaryExpression]: (node, visitor) => { - visitNode(node.left, visitor); - visitNode(node.right, visitor); - }, - [luau.SyntaxKind.UnaryExpression]: (node, visitor) => visitNode(node.expression, visitor), - [luau.SyntaxKind.IfExpression]: (node, visitor) => { - visitNode(node.condition, visitor); - visitNode(node.expression, visitor); - visitNode(node.alternative, visitor); - }, - [luau.SyntaxKind.Array]: (node, visitor) => visitList(node.members, visitor), - [luau.SyntaxKind.Map]: (node, visitor) => visitList(node.fields, visitor), - [luau.SyntaxKind.Set]: (node, visitor) => visitList(node.members, visitor), - [luau.SyntaxKind.MixedTable]: (node, visitor) => visitList(node.fields, visitor), - - // statements - [luau.SyntaxKind.Assignment]: (node, visitor) => { - if (luau.list.isList(node.left)) { - visitList(node.left, visitor); - } else { - visitNode(node.left, visitor); - } - if (luau.list.isList(node.right)) { - visitList(node.right, visitor); - } else { - visitNode(node.right, visitor); - } - }, - [luau.SyntaxKind.BreakStatement]: NOOP, - [luau.SyntaxKind.CallStatement]: (node, visitor) => visitNode(node.expression, visitor), - [luau.SyntaxKind.ContinueStatement]: NOOP, - [luau.SyntaxKind.DoStatement]: (node, visitor) => visitList(node.statements, visitor), - [luau.SyntaxKind.WhileStatement]: (node, visitor) => { - visitNode(node.condition, visitor); - visitList(node.statements, visitor); - }, - [luau.SyntaxKind.RepeatStatement]: (node, visitor) => { - visitList(node.statements, visitor); - visitNode(node.condition, visitor); - }, - [luau.SyntaxKind.IfStatement]: (node, visitor) => { - visitNode(node.condition, visitor); - visitList(node.statements, visitor); - if (luau.list.isList(node.elseBody)) { - visitList(node.elseBody, visitor); - } else { - visitNode(node.elseBody, visitor); - } - }, - [luau.SyntaxKind.NumericForStatement]: (node, visitor) => { - visitNode(node.id, visitor); - visitNode(node.start, visitor); - visitNode(node.end, visitor); - if (node.step) { - visitNode(node.step, visitor); - } - visitList(node.statements, visitor); - }, - [luau.SyntaxKind.ForStatement]: (node, visitor) => { - visitList(node.ids, visitor); - visitList(node.statements, visitor); - }, - [luau.SyntaxKind.FunctionDeclaration]: (node, visitor) => { - visitNode(node.name, visitor); - visitList(node.parameters, visitor); - visitList(node.statements, visitor); - }, - [luau.SyntaxKind.MethodDeclaration]: (node, visitor) => { - visitNode(node.expression, visitor); - visitList(node.parameters, visitor); - visitList(node.statements, visitor); - }, - [luau.SyntaxKind.VariableDeclaration]: (node, visitor) => { - if (luau.list.isList(node.left)) { - visitList(node.left, visitor); - } else { - visitNode(node.left, visitor); - } - if (node.right) { - if (luau.list.isList(node.right)) { - visitList(node.right, visitor); - } else { - visitNode(node.right, visitor); - } - } - }, - [luau.SyntaxKind.ReturnStatement]: (node, visitor) => { - if (luau.list.isList(node.expression)) { - visitList(node.expression, visitor); - } else { - visitNode(node.expression, visitor); - } - }, - [luau.SyntaxKind.Comment]: NOOP, - - // fields - [luau.SyntaxKind.MapField]: (node, visitor) => { - visitNode(node.index, visitor); - visitNode(node.value, visitor); - }, -}); - -function visitNode(node: luau.Node, visitor: Visitor) { - visitor.before?.(node); - KIND_TO_VISITOR[node.kind](node as never, visitor); - visitor.after?.(node); -} - -function visitList(list: luau.List, visitor: Visitor) { - luau.list.forEach(list, v => visitNode(v, visitor)); -} - -export function visit(ast: luau.List | luau.Node, visitor: Visitor) { - if (luau.list.isList(ast)) { - visitList(ast, visitor); - } else { - visitNode(ast, visitor); - } -} diff --git a/src/__communication__/README.md b/src/__communication__/README.md deleted file mode 100644 index bc2de0a..0000000 --- a/src/__communication__/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Info -This folder is to be ignored as it holds just random files used to hold configuration and bindings -between Python and TS. \ No newline at end of file diff --git a/src/__communication__/cfg.pkl b/src/__communication__/cfg.pkl deleted file mode 100644 index 66161e8bfde85d98d7960ba48b85f1a458a028c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 98 zcmZo*nHtUj0ku;!df3xb^HPfvbEbeetm*mrX_ZqtdpJvq5|dMt5|gum9PY%@lKkZS t+=9%U)G3{%J&egf#XWo}m3fJ|naMesNd<`|8B=;#{POcsrxcg!0RXJ=B`p8| diff --git a/src/__communication__/ts.json b/src/__communication__/ts.json deleted file mode 100644 index e69de29..0000000 From 01328042321077e0edda8a49e4d9c6a36d309313 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sun, 20 Aug 2023 18:42:16 -0500 Subject: [PATCH 04/77] Remove junk --- src/sealang.cpp | 236 ------------------------------------------ src/sealang.h | 63 ----------- src/swiftparser.swift | 12 --- 3 files changed, 311 deletions(-) delete mode 100644 src/sealang.cpp delete mode 100644 src/sealang.h delete mode 100644 src/swiftparser.swift diff --git a/src/sealang.cpp b/src/sealang.cpp deleted file mode 100644 index fb86696..0000000 --- a/src/sealang.cpp +++ /dev/null @@ -1,236 +0,0 @@ -#include "sealang.h" - -#include "clang/AST/Stmt.h" -#include "clang/AST/Expr.h" -#include "clang/AST/ExprCXX.h" -#include "llvm/ADT/SmallString.h" - -/************************************************************************ - * Duplicated libclang functionality - * - * The following methods are duplicates of methods that are implemented - * in libclang, but aren't exposed as symbols that can be used by third- - * party libraries. - ************************************************************************/ - -namespace clang { - enum CXStringFlag { - /// CXString contains a 'const char *' that it doesn't own. - CXS_Unmanaged, - - /// CXString contains a 'const char *' that it allocated with malloc(). - CXS_Malloc, - - /// CXString contains a CXStringBuf that needs to be returned to the - /// CXStringPool. - CXS_StringBuf - }; - - const clang::Stmt *getCursorStmt(CXCursor cursor) { - if (cursor.kind == CXCursor_ObjCSuperClassRef || - cursor.kind == CXCursor_ObjCProtocolRef || - cursor.kind == CXCursor_ObjCClassRef) { - - return nullptr; - } - return static_cast(cursor.data[1]); - } - - const clang::Expr *getCursorExpr(CXCursor cursor) { - return clang::dyn_cast_or_null(getCursorStmt(cursor)); - } - - namespace cxstring { - CXString createEmpty() { - CXString str; - str.data = ""; - str.private_flags = CXS_Unmanaged; - return str; - } - - CXString createDup(StringRef string) { - CXString result; - char *spelling = static_cast(malloc(string.size() + 1)); - memmove(spelling, string.data(), string.size()); - spelling[string.size()] = 0; - result.data = spelling; - result.private_flags = (unsigned) CXS_Malloc; - return result; - } - - } -} - -/************************************************************************ - * New Sealang functionality - * - * The following methods expose useful features of the LLVM AST. They are - * all potentially candidates for inclusion upstream in libclang. - ************************************************************************/ - -CXString clang_Cursor_getOperatorString(CXCursor cursor) -{ - if (cursor.kind == CXCursor_BinaryOperator) { - clang::BinaryOperator *op = (clang::BinaryOperator *) clang::getCursorExpr(cursor); - return clang::cxstring::createDup(clang::BinaryOperator::getOpcodeStr(op->getOpcode())); - } - - if (cursor.kind == CXCursor_CompoundAssignOperator) { - clang::CompoundAssignOperator *op = (clang::CompoundAssignOperator*) clang::getCursorExpr(cursor); - return clang::cxstring::createDup(clang::BinaryOperator::getOpcodeStr(op->getOpcode())); - } - - if (cursor.kind == CXCursor_UnaryOperator) { - clang::UnaryOperator *op = (clang::UnaryOperator*) clang::getCursorExpr(cursor); - return clang::cxstring::createDup(clang::UnaryOperator::getOpcodeStr(op->getOpcode())); - } - - return clang::cxstring::createEmpty(); -} - -clang::BinaryOperatorKind clang_Cursor_getBinaryOpcode(CXCursor cursor) -{ - if (cursor.kind == CXCursor_BinaryOperator) { - clang::BinaryOperator *op = (clang::BinaryOperator *) clang::getCursorExpr(cursor); - return static_cast(op->getOpcode()); - } - - if (cursor.kind == CXCursor_CompoundAssignOperator) { - clang::CompoundAssignOperator *op = (clang::CompoundAssignOperator *) clang::getCursorExpr(cursor); - return static_cast(op->getOpcode()); - } - - return (clang::BinaryOperatorKind) 99999; -} - -clang::UnaryOperatorKind clang_Cursor_getUnaryOpcode(CXCursor cursor) -{ - if (cursor.kind == CXCursor_UnaryOperator) { - clang::UnaryOperator *op = (clang::UnaryOperator*) clang::getCursorExpr(cursor); - return static_cast(op->getOpcode()); - } - - return (clang::UnaryOperatorKind) 99999; -} - -CXString clang_Cursor_getLiteralString(CXCursor cursor) -{ - if (cursor.kind == CXCursor_IntegerLiteral) { - clang::IntegerLiteral *intLiteral = (clang::IntegerLiteral *) clang::getCursorExpr(cursor); - return clang::cxstring::createDup(intLiteral->getValue().toString(10, true)); - } - - if (cursor.kind == CXCursor_FloatingLiteral) { - clang::FloatingLiteral *floatLiteral = (clang::FloatingLiteral *) clang::getCursorExpr(cursor); - llvm::SmallString<1024> str; - floatLiteral->getValue().toString(str); - return clang::cxstring::createDup(str.c_str()); - } - - if (cursor.kind == CXCursor_CharacterLiteral) { - clang::CharacterLiteral *charLiteral = (clang::CharacterLiteral *) clang::getCursorExpr(cursor); - char c[2]; - c[0] = (char) charLiteral->getValue(); - c[1] = '\0'; - return clang::cxstring::createDup(c); - } - - if (cursor.kind == CXCursor_StringLiteral) { - clang::StringLiteral *stringLiteral = (clang::StringLiteral *) clang::getCursorExpr(cursor); - return clang::cxstring::createDup(stringLiteral->getBytes()); - } - - if (cursor.kind == CXCursor_CXXBoolLiteralExpr) { - clang::CXXBoolLiteralExpr *boolLiteral = (clang::CXXBoolLiteralExpr *) clang::getCursorExpr(cursor); - return clang::cxstring::createDup(boolLiteral->getValue() ? "true" : "false"); - } - - return clang::cxstring::createEmpty(); -} - -// CXCursor clang_getForStmtInit(CXCursor cursor) -// { -// if(cursor.kind!=CXCursor_ForStmt) return MakeCXCursorInvalid(CXCursor_InvalidCode); - -// ForStmt* Node=(ForStmt*)(cursor.data[1]); -// const CXTranslationUnit tu=(const CXTranslationUnit)(cursor.data[2]); - -// Stmt* init=Node->getInit(); -// if (init) return MakeCXCursor(init,0,tu); -// else return MakeCXCursorInvalid(CXCursor_NoDeclFound); -// } - -// CXCursor clang_getForStmtCond(CXCursor cursor) -// { -// if(cursor.kind!=CXCursor_ForStmt) return MakeCXCursorInvalid(CXCursor_InvalidCode); - -// ForStmt* Node=(ForStmt*)(cursor.data[1]); -// const CXTranslationUnit tu=(const CXTranslationUnit)(cursor.data[2]); - -// Stmt* cond=Node->getCond(); -// if (cond) return MakeCXCursor(cond,0,tu); -// else return MakeCXCursorInvalid(CXCursor_NoDeclFound); -// } - -// CXCursor clang_getForStmtInc(CXCursor cursor) -// { -// if(cursor.kind!=CXCursor_ForStmt) return MakeCXCursorInvalid(CXCursor_InvalidCode); - -// ForStmt* Node=(ForStmt*)(cursor.data[1]); -// const CXTranslationUnit tu=(const CXTranslationUnit)(cursor.data[2]); - -// Stmt* inc=Node->getInc(); -// if (inc) return MakeCXCursor(inc,0,tu); -// else return MakeCXCursorInvalid(CXCursor_NoDeclFound); -// } - -// CXCursor clang_getForStmtBody(CXCursor cursor) -// { -// if(cursor.kind!=CXCursor_ForStmt) return MakeCXCursorInvalid(CXCursor_InvalidCode); - -// ForStmt* Node=(ForStmt*)(cursor.data[1]); -// const CXTranslationUnit tu=(const CXTranslationUnit)(cursor.data[2]); - -// Stmt* body=Node->getBody(); -// if (body) return MakeCXCursor(body,0,tu); -// else return MakeCXCursorInvalid(CXCursor_NoDeclFound); -// } - -/************************************************************************ - * Python module definition - * - * This is a stub module definition; we aren't exposing any Python - * methods - we're just making the module .so easy to find. - ************************************************************************/ - -static PyMethodDef methods[] = { - {NULL, NULL, 0, NULL} -}; - -#if PY_MAJOR_VERSION <= 2 - -PyMODINIT_FUNC initsealang() -{ - (void) Py_InitModule("sealang", methods); -} - -#else - -static struct PyModuleDef sealangmodule = { - PyModuleDef_HEAD_INIT, - "sealang", - NULL, - -1, - methods, - NULL, - NULL, - NULL, - NULL -}; - -PyMODINIT_FUNC PyInit_sealang() -{ - return PyModule_Create(&sealangmodule); -} - -#endif \ No newline at end of file diff --git a/src/sealang.h b/src/sealang.h deleted file mode 100644 index c2df622..0000000 --- a/src/sealang.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef SEALANG_H -#define SEALANG_H - -#include "Python.h" - -#include "clang/AST/OperationKinds.h" -#include "clang-c/Index.h" -#include "clang-c/CXString.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief Returns string representation of unary and binary operators - */ -CXString clang_Cursor_getOperatorString(CXCursor cursor); - -/** - * \brief Returns Opcode of binary operator - */ -clang::BinaryOperatorKind clang_Cursor_getBinaryOpcode(CXCursor cursor); - -/** - * \brief Returns Opcode of unary operator - */ -clang::UnaryOperatorKind clang_Cursor_getUnaryOpcode(CXCursor cursor); - -/** - * \brief Returns string representation of literal cursor (1.f, 1000L, etc) - */ -CXString clang_Cursor_getLiteralString(CXCursor cursor); - -/** - * \brief Returns for-loop init cursor [for(init;cond;inc)], or CXCursor_NoDeclFound if there is no decl, - * or CXCursor_InvalidCode if C is not CXCursor_ForStmt - */ -// CXCursor clang_getForStmtInit(CXCursor C); - -/** - * \brief Returns for-loop condition cursor [for(init;cond;inc)], or CXCursor_NoDeclFound if there is no decl, - * or CXCursor_InvalidCode if C is not CXCursor_ForStmt - */ -// CXCursor clang_getForStmtCond(CXCursor C); - -/** - * \brief Returns for-loop increment cursor [for(init;cond;inc)], or CXCursor_NoDeclFound if there is no decl, - * or CXCursor_InvalidCode if C is not CXCursor_ForStmt - */ -// CXCursor clang_getForStmtInc(CXCursor C); - -/** - * \brief Returns for-loop body, or CXCursor_NoDeclFound if there is no decl, - * or CXCursor_InvalidCode if C is not CXCursor_ForStmt - */ -// CXCursor clang_getForStmtBody(CXCursor C); - - -#ifdef __cplusplus -} -#endif - -#endif \ No newline at end of file diff --git a/src/swiftparser.swift b/src/swiftparser.swift deleted file mode 100644 index d998ad4..0000000 --- a/src/swiftparser.swift +++ /dev/null @@ -1,12 +0,0 @@ -import SwiftSyntax - -let source = """ -func greet(name: String) { - print("Hello, \\(name)!") -} -""" - -let parser = SyntaxParser() -let syntax = try parser.parse(source: source) - -print(syntax) From 273967fdd81a6f2fa8050fb062dcf1785ce4f733 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sun, 20 Aug 2023 19:42:06 -0500 Subject: [PATCH 05/77] util --- src/util.py | 12 +---------- src2/README.md | 2 ++ src2/util.cobalt | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 src2/README.md create mode 100644 src2/util.cobalt diff --git a/src/util.py b/src/util.py index 0f1d9bd..703ae0f 100644 --- a/src/util.py +++ b/src/util.py @@ -17,16 +17,6 @@ def filtercompiledfolder(): for file in f: if not file.endswith(".lua"): os.remove(os.path.join(r, file)) - -def onNotFound(target): - currentcommand = sys.argv[2] - - allCLIS = configmanager.getconfig("general", "cli", []) - - # go through allCLIS and check if target and command matches - for i in allCLIS: - if i["target"] == target: - pass def lib(): # if /server and /client are found, then error if os.path.exists(os.path.join(os.getcwd(), "server")) and os.path.exists(os.path.join(os.getcwd(), "client")): @@ -54,4 +44,4 @@ def lib(): translator = pytranslator.Translator() f.write(translator.get_luainit(configmanager.getconfig("general", "luaext", []))) # Make a file called content.json in the dependencies folder - open(os.path.join(cwd, "dependencies", "content.json"), "x").close() + open(os.path.join(cwd, "dependencies", "content.json"), "x").close() \ No newline at end of file diff --git a/src2/README.md b/src2/README.md new file mode 100644 index 0000000..4a88605 --- /dev/null +++ b/src2/README.md @@ -0,0 +1,2 @@ +# src2 +Rewritting `/src` in `cobalt` rather than `python`. \ No newline at end of file diff --git a/src2/util.cobalt b/src2/util.cobalt new file mode 100644 index 0000000..0ca08df --- /dev/null +++ b/src2/util.cobalt @@ -0,0 +1,56 @@ +// util.cobalt - Utility functions for roblox-pyc + +/* Functions: +- backwardreplace(s, old, new, occurences) +- filerfolder(path) +- genLib() +- endswith(txt, end) +*/ + +file = file || import("file"); +filex = import("ext.file") + +function backwardreplace(s, old, new, occurences){ + // Example: + /* + backwardreplace("x.py.py", ".py", ".lua", 1) + > x.py.lua + */ + var newstring = s; + var i = 0; + while (i < occurences){ + newstring = string.sub(newstring, 1, #(newstring) - #(old)) .. new; + i++; + } + + return newstring; +} + +function endswith(txt, end){ + // Returns true if `txt` ends with `end` + return string.sub(txt, -#(end)) == end; +} + +function filterfolder(path, ext){ + // use file (LuaFileSystem) to get remove all . files inside of the folder relative to the cwd + var directory = file.currentdir() .. path; + var pfile + if (os.getenv("OS") == "Windows_NT"){ + pfile = io.popen('dir /b "'..directory..'"'); + }else{ + pfile = io.popen('ls -a "'..directory..'"'); + } + var rt = {}; + + for (filename in pfile->lines()) { + if (!(filename == "." || filename == "..") && endswith(filename, ext)){ + os.remove(directory .. "/" .. filename) + } + } + + pfile->close(); + + return true; +} + +print(filterfolder("", ".lua")) \ No newline at end of file From d89286e8d19257c87bee3bc242b3822dc39ad988 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sun, 20 Aug 2023 19:45:54 -0500 Subject: [PATCH 06/77] scripts --- README.md | 3 + src2/astclass.cobalt | 1 + src2/bridge/CMakeLists.txt | 19 ++ src2/bridge/luainpython.c | 596 ++++++++++++++++++++++++++++++++ src2/bridge/luainpython.h | 52 +++ src2/bridge/pythoninlua.c | 675 +++++++++++++++++++++++++++++++++++++ src2/bridge/pythoninlua.h | 44 +++ src2/generator.cobalt | 1 + src2/pyast.cobalt | 1 + 9 files changed, 1392 insertions(+) create mode 100644 src2/astclass.cobalt create mode 100644 src2/bridge/CMakeLists.txt create mode 100644 src2/bridge/luainpython.c create mode 100644 src2/bridge/luainpython.h create mode 100644 src2/bridge/pythoninlua.c create mode 100644 src2/bridge/pythoninlua.h create mode 100644 src2/generator.cobalt create mode 100644 src2/pyast.cobalt diff --git a/README.md b/README.md index 9eae669..57850d9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # Introduction +> **NOTICE:** +> This is the indev build +> credits to lunatic-python [![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#contributors-) diff --git a/src2/astclass.cobalt b/src2/astclass.cobalt new file mode 100644 index 0000000..1ae7c58 --- /dev/null +++ b/src2/astclass.cobalt @@ -0,0 +1 @@ +// AST Class diff --git a/src2/bridge/CMakeLists.txt b/src2/bridge/CMakeLists.txt new file mode 100644 index 0000000..38c58a9 --- /dev/null +++ b/src2/bridge/CMakeLists.txt @@ -0,0 +1,19 @@ +add_library(src OBJECT luainpython.c pythoninlua.c) +set_target_properties(src PROPERTIES + POSITION_INDEPENDENT_CODE TRUE) + +target_include_directories(src PRIVATE ${LUA_INCLUDE_DIR} ${PYTHON_INCLUDE_DIR}) + +target_compile_definitions(src PRIVATE LUA_LIB) +if (WIN32) + target_compile_definitions(src PRIVATE LUA_BUILD_AS_DLL) +endif (WIN32) + +if (UNIX) + get_filename_component(PYTHON_LIBRT ${PYTHON_LIBRARIES} NAME) + target_compile_definitions(src PRIVATE PYTHON_LIBRT=${PYTHON_LIBRT}) +endif (UNIX) + +if (CMAKE_COMPILER_IS_GNUCC) + target_compile_options(src PUBLIC -Wall -pedantic -std=c99) +endif (CMAKE_COMPILER_IS_GNUCC) diff --git a/src2/bridge/luainpython.c b/src2/bridge/luainpython.c new file mode 100644 index 0000000..c55c6ab --- /dev/null +++ b/src2/bridge/luainpython.c @@ -0,0 +1,596 @@ +/* + + Lunatic Python + -------------- + + Copyright (c) 2002-2005 Gustavo Niemeyer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#include + +/* need this to build with Lua 5.2: enables lua_strlen() macro */ +#define LUA_COMPAT_ALL + +#include +#include +#include +#include + +#include "pythoninlua.h" +#include "luainpython.h" + +lua_State *LuaState = NULL; + +static PyObject *LuaObject_New(lua_State *L, int n) +{ + LuaObject *obj = PyObject_New(LuaObject, &LuaObject_Type); + if (obj) + { + lua_pushvalue(L, n); + obj->ref = luaL_ref(L, LUA_REGISTRYINDEX); + obj->refiter = 0; + } + return (PyObject*) obj; +} + +PyObject *LuaConvert(lua_State *L, int n) +{ + + PyObject *ret = NULL; + + switch (lua_type(L, n)) { + + case LUA_TNIL: + Py_INCREF(Py_None); + ret = Py_None; + break; + + case LUA_TSTRING: { + size_t len; + const char *s = lua_tolstring(L, n, &len); + ret = PyUnicode_FromStringAndSize(s, len); + if (!ret) + { + PyErr_Clear(); + ret = PyBytes_FromStringAndSize(s, len); + } + break; + } + + case LUA_TNUMBER: { + lua_Number num = lua_tonumber(L, n); + if (num != (long)num) { + ret = PyFloat_FromDouble(num); + } else { + ret = PyLong_FromLong((long)num); + } + break; + } + + case LUA_TBOOLEAN: + ret = lua_toboolean(L, n) ? Py_True : Py_False; + Py_INCREF(ret); + break; + + case LUA_TUSERDATA: { + py_object *obj = luaPy_to_pobject(L, n); + + if (obj) { + Py_INCREF(obj->o); + ret = obj->o; + break; + } + + /* Otherwise go on and handle as custom. */ + } + + default: + ret = LuaObject_New(L, n); + break; + } + + return ret; +} + +static PyObject *LuaCall(lua_State *L, PyObject *args) +{ + PyObject *ret = NULL; + PyObject *arg; + int nargs, rc, i; + + if (!PyTuple_Check(args)) { + PyErr_SetString(PyExc_TypeError, "tuple expected"); + lua_settop(L, 0); + return NULL; + } + + nargs = PyTuple_Size(args); + for (i = 0; i != nargs; i++) { + arg = PyTuple_GetItem(args, i); + if (arg == NULL) { + PyErr_Format(PyExc_TypeError, + "failed to get tuple item #%d", i); + lua_settop(L, 0); + return NULL; + } + rc = py_convert(L, arg); + if (!rc) { + PyErr_Format(PyExc_TypeError, + "failed to convert argument #%d", i); + lua_settop(L, 0); + return NULL; + } + } + + if (lua_pcall(L, nargs, LUA_MULTRET, 0) != 0) { + PyErr_Format(PyExc_Exception, + "error: %s", lua_tostring(L, -1)); + return NULL; + } + + nargs = lua_gettop(L); + if (nargs == 1) { + ret = LuaConvert(L, 1); + if (!ret) { + PyErr_SetString(PyExc_TypeError, + "failed to convert return"); + lua_settop(L, 0); + Py_DECREF(ret); + return NULL; + } + } else if (nargs > 1) { + ret = PyTuple_New(nargs); + if (!ret) { + PyErr_SetString(PyExc_RuntimeError, + "failed to create return tuple"); + lua_settop(L, 0); + return NULL; + } + for (i = 0; i != nargs; i++) { + arg = LuaConvert(L, i+1); + if (!arg) { + PyErr_Format(PyExc_TypeError, + "failed to convert return #%d", i); + lua_settop(L, 0); + Py_DECREF(ret); + return NULL; + } + PyTuple_SetItem(ret, i, arg); + } + } else { + Py_INCREF(Py_None); + ret = Py_None; + } + + lua_settop(L, 0); + + return ret; +} + +static void LuaObject_dealloc(LuaObject *self) +{ + luaL_unref(LuaState, LUA_REGISTRYINDEX, self->ref); + if (self->refiter) + luaL_unref(LuaState, LUA_REGISTRYINDEX, self->refiter); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static PyObject *LuaObject_getattr(PyObject *obj, PyObject *attr) +{ + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject*)obj)->ref); + if (lua_isnil(LuaState, -1)) { + lua_pop(LuaState, 1); + PyErr_SetString(PyExc_RuntimeError, "lost reference"); + return NULL; + } + + if (!lua_isstring(LuaState, -1) + && !lua_istable(LuaState, -1) + && !lua_isuserdata(LuaState, -1)) + { + lua_pop(LuaState, 1); + PyErr_SetString(PyExc_RuntimeError, "not an indexable value"); + return NULL; + } + + PyObject *ret = NULL; + int rc = py_convert(LuaState, attr); + if (rc) { + lua_gettable(LuaState, -2); + ret = LuaConvert(LuaState, -1); + } else { + PyErr_SetString(PyExc_ValueError, "can't convert attr/key"); + } + lua_settop(LuaState, 0); + return ret; +} + +static int LuaObject_setattr(PyObject *obj, PyObject *attr, PyObject *value) +{ + int ret = -1; + int rc; + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject*)obj)->ref); + if (lua_isnil(LuaState, -1)) { + lua_pop(LuaState, 1); + PyErr_SetString(PyExc_RuntimeError, "lost reference"); + return -1; + } + if (!lua_istable(LuaState, -1)) { + lua_pop(LuaState, -1); + PyErr_SetString(PyExc_TypeError, "Lua object is not a table"); + return -1; + } + rc = py_convert(LuaState, attr); + if (rc) { + if (NULL == value) { + lua_pushnil(LuaState); + rc = 1; + } else { + rc = py_convert(LuaState, value); + } + + if (rc) { + lua_settable(LuaState, -3); + ret = 0; + } else { + PyErr_SetString(PyExc_ValueError, + "can't convert value"); + } + } else { + PyErr_SetString(PyExc_ValueError, "can't convert key/attr"); + } + lua_settop(LuaState, 0); + return ret; +} + +static PyObject *LuaObject_str(PyObject *obj) +{ + PyObject *ret = NULL; + const char *s; + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject*)obj)->ref); + if (luaL_callmeta(LuaState, -1, "__tostring")) { + s = lua_tostring(LuaState, -1); + lua_pop(LuaState, 1); + if (s) ret = PyUnicode_FromString(s); + } + if (!ret) { + int type = lua_type(LuaState, -1); + switch (type) { + case LUA_TTABLE: + case LUA_TFUNCTION: + ret = PyUnicode_FromFormat("", + lua_typename(LuaState, type), + lua_topointer(LuaState, -1)); + break; + + case LUA_TUSERDATA: + case LUA_TLIGHTUSERDATA: + ret = PyUnicode_FromFormat("", + lua_typename(LuaState, type), + lua_touserdata(LuaState, -1)); + break; + + case LUA_TTHREAD: + ret = PyUnicode_FromFormat("", + lua_typename(LuaState, type), + (void*)lua_tothread(LuaState, -1)); + break; + + default: + ret = PyUnicode_FromFormat("", + lua_typename(LuaState, type)); + break; + + } + } + lua_pop(LuaState, 1); + return ret; +} + +#if LUA_VERSION_NUM == 501 +enum +{ + LUA_OK, LUA_OPEQ, LUA_OPLT, LUA_OPLE, +}; +static int lua_compare(lua_State *L, int lhs, int rhs, int op) +{ + switch(op) + { + case LUA_OPEQ: + return lua_equal(L, lhs, rhs); + case LUA_OPLT: + return lua_lessthan(L, lhs, rhs); + case LUA_OPLE: + return lua_lessthan(L, lhs, rhs) || lua_equal(L, lhs, rhs); + } + return 0; +} +#endif +static int LuaObject_pcmp(lua_State *L) +{ + int op = lua_tointeger(L, -3); + switch(op) + { + case Py_EQ: + lua_pushboolean(L, lua_compare(L, -2, -1, LUA_OPEQ)); + break; + case Py_NE: + lua_pushboolean(L, !lua_compare(L, -2, -1, LUA_OPEQ)); + break; + case Py_GT: + lua_insert(LuaState, -2); + case Py_LT: + lua_pushboolean(L, lua_compare(L, -2, -1, LUA_OPLT)); + break; + case Py_GE: + lua_insert(LuaState, -2); + case Py_LE: + lua_pushboolean(L, lua_compare(L, -2, -1, LUA_OPLE)); + } + + return 1; +} + +static PyObject* LuaObject_richcmp(PyObject *lhs, PyObject *rhs, int op) +{ + if (!LuaObject_Check(rhs)) return Py_False; + + lua_pushcfunction(LuaState, LuaObject_pcmp); + lua_pushinteger(LuaState, op); + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject *)lhs)->ref); + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject *)rhs)->ref); + if (lua_pcall(LuaState, 3, 1, 0) != LUA_OK) + { + PyErr_SetString(PyExc_RuntimeError, lua_tostring(LuaState, -1)); + return NULL; + } + return lua_toboolean(LuaState, -1) ? Py_True : Py_False; +} + +static PyObject *LuaObject_call(PyObject *obj, PyObject *args) +{ + lua_settop(LuaState, 0); + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject*)obj)->ref); + return LuaCall(LuaState, args); +} + +static PyObject *LuaObject_iternext(LuaObject *obj) +{ + PyObject *ret = NULL; + + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject*)obj)->ref); + + if (obj->refiter == 0) + lua_pushnil(LuaState); + else + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, obj->refiter); + + if (lua_next(LuaState, -2) != 0) { + /* Remove value. */ + lua_pop(LuaState, 1); + ret = LuaConvert(LuaState, -1); + /* Save key for next iteration. */ + if (!obj->refiter) + obj->refiter = luaL_ref(LuaState, LUA_REGISTRYINDEX); + else + lua_rawseti(LuaState, LUA_REGISTRYINDEX, obj->refiter); + } else if (obj->refiter) { + luaL_unref(LuaState, LUA_REGISTRYINDEX, obj->refiter); + obj->refiter = 0; + } + + return ret; +} + +static int LuaObject_length(LuaObject *obj) +{ + int len; + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject*)obj)->ref); + len = luaL_len(LuaState, -1); + lua_settop(LuaState, 0); + return len; +} + +static PyObject *LuaObject_subscript(PyObject *obj, PyObject *key) +{ + return LuaObject_getattr(obj, key); +} + +static int LuaObject_ass_subscript(PyObject *obj, + PyObject *key, PyObject *value) +{ + return LuaObject_setattr(obj, key, value); +} + +static PyMappingMethods LuaObject_as_mapping = { +#if PY_VERSION_HEX >= 0x02050000 + (lenfunc)LuaObject_length, /*mp_length*/ +#else + (inquiry)LuaObject_length, /*mp_length*/ +#endif + (binaryfunc)LuaObject_subscript,/*mp_subscript*/ + (objobjargproc)LuaObject_ass_subscript,/*mp_ass_subscript*/ +}; + +PyTypeObject LuaObject_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "lua.custom", /*tp_name*/ + sizeof(LuaObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)LuaObject_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + LuaObject_str, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + &LuaObject_as_mapping, /*tp_as_mapping*/ + 0, /*tp_hash*/ + (ternaryfunc)LuaObject_call, /*tp_call*/ + LuaObject_str, /*tp_str*/ + LuaObject_getattr, /*tp_getattro*/ + LuaObject_setattr, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "custom lua object", /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + LuaObject_richcmp, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + PyObject_SelfIter, /*tp_iter*/ + (iternextfunc)LuaObject_iternext, /*tp_iternext*/ + 0, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + PyType_GenericAlloc, /*tp_alloc*/ + PyType_GenericNew, /*tp_new*/ + PyObject_Del, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + + +PyObject *Lua_run(PyObject *args, int eval) +{ + PyObject *ret; + char *buf = NULL; + char *s; + int len; + + if (!PyArg_ParseTuple(args, "s#", &s, &len)) + return NULL; + + if (eval) { + buf = (char *) malloc(strlen("return ")+len+1); + strcpy(buf, "return "); + strncat(buf, s, len); + s = buf; + len = strlen("return ")+len; + } + + if (luaL_loadbuffer(LuaState, s, len, "") != 0) { + PyErr_Format(PyExc_RuntimeError, + "error loading code: %s", + lua_tostring(LuaState, -1)); + free(buf); + return NULL; + } + + free(buf); + + if (lua_pcall(LuaState, 0, 1, 0) != 0) { + PyErr_Format(PyExc_RuntimeError, + "error executing code: %s", + lua_tostring(LuaState, -1)); + return NULL; + } + + ret = LuaConvert(LuaState, -1); + lua_settop(LuaState, 0); + return ret; +} + +PyObject *Lua_execute(PyObject *self, PyObject *args) +{ + return Lua_run(args, 0); +} + +PyObject *Lua_eval(PyObject *self, PyObject *args) +{ + return Lua_run(args, 1); +} + +PyObject *Lua_globals(PyObject *self, PyObject *args) +{ + PyObject *ret = NULL; + lua_getglobal(LuaState, "_G"); + if (lua_isnil(LuaState, -1)) { + PyErr_SetString(PyExc_RuntimeError, + "lost globals reference"); + lua_pop(LuaState, 1); + return NULL; + } + ret = LuaConvert(LuaState, -1); + if (!ret) + PyErr_Format(PyExc_TypeError, + "failed to convert globals table"); + lua_settop(LuaState, 0); + return ret; +} + +static PyObject *Lua_require(PyObject *self, PyObject *args) +{ + lua_getglobal(LuaState, "require"); + if (lua_isnil(LuaState, -1)) { + lua_pop(LuaState, 1); + PyErr_SetString(PyExc_RuntimeError, "require is not defined"); + return NULL; + } + return LuaCall(LuaState, args); +} + +static PyMethodDef lua_methods[] = +{ + {"execute", Lua_execute, METH_VARARGS, NULL}, + {"eval", Lua_eval, METH_VARARGS, NULL}, + {"globals", Lua_globals, METH_NOARGS, NULL}, + {"require", Lua_require, METH_VARARGS, NULL}, + {NULL, NULL} +}; + +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef lua_module = +{ + PyModuleDef_HEAD_INIT, + "lua", + "Lunatic-Python Python-Lua bridge", + -1, + lua_methods +}; +#endif + +PyMODINIT_FUNC PyInit_lua(void) +{ + PyObject *m; + if (PyType_Ready(&LuaObject_Type) < 0 || +#if PY_MAJOR_VERSION >= 3 + (m = PyModule_Create(&lua_module)) == NULL) + return NULL; +#else + (m = Py_InitModule3("lua", lua_methods, + "Lunatic-Python Python-Lua bridge")) == NULL) + return; +#endif + + if (!LuaState) + { + LuaState = luaL_newstate(); + luaL_openlibs(LuaState); + luaopen_python(LuaState); + lua_settop(LuaState, 0); + } + +#if PY_MAJOR_VERSION >= 3 + return m; +#endif +} diff --git a/src2/bridge/luainpython.h b/src2/bridge/luainpython.h new file mode 100644 index 0000000..73ea65c --- /dev/null +++ b/src2/bridge/luainpython.h @@ -0,0 +1,52 @@ +/* + + Lunatic Python + -------------- + + Copyright (c) 2002-2005 Gustavo Niemeyer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#ifndef LUAINPYTHON_H +#define LUAINPYTHON_H + +#if LUA_VERSION_NUM == 501 + #define luaL_len lua_objlen + #define luaL_setfuncs(L, l, nup) luaL_register(L, NULL, (l)) + #define luaL_newlib(L, l) (lua_newtable(L), luaL_register(L, NULL, (l))) +#endif + +typedef struct +{ + PyObject_HEAD + int ref; + int refiter; +} LuaObject; + +extern PyTypeObject LuaObject_Type; + +#define LuaObject_Check(op) PyObject_TypeCheck(op, &LuaObject_Type) + +PyObject* LuaConvert(lua_State *L, int n); + +extern lua_State *LuaState; + +#if PY_MAJOR_VERSION < 3 +# define PyInit_lua initlua +#endif +PyMODINIT_FUNC PyInit_lua(void); + +#endif diff --git a/src2/bridge/pythoninlua.c b/src2/bridge/pythoninlua.c new file mode 100644 index 0000000..cbfc433 --- /dev/null +++ b/src2/bridge/pythoninlua.c @@ -0,0 +1,675 @@ +/* + + Lunatic Python + -------------- + + Copyright (c) 2002-2005 Gustavo Niemeyer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#include +#if defined(__linux__) +# include +#endif + +/* need this to build with Lua 5.2: defines luaL_register() macro */ +#define LUA_COMPAT_MODULE + +#include +#include + +#include "pythoninlua.h" +#include "luainpython.h" + +static int py_asfunc_call(lua_State *); +static int py_eval(lua_State *); + +static int py_convert_custom(lua_State *L, PyObject *o, int asindx) +{ + py_object *obj = (py_object*) lua_newuserdata(L, sizeof(py_object)); + if (!obj) + luaL_error(L, "failed to allocate userdata object"); + + Py_INCREF(o); + obj->o = o; + obj->asindx = asindx; + luaL_getmetatable(L, POBJECT); + lua_setmetatable(L, -2); + + return 1; +} + +int py_convert(lua_State *L, PyObject *o) +{ + int ret = 0; + if (o == Py_None) + { + /* Not really needed, but this way we may check + * for errors with ret == 0. */ + lua_pushnil(L); + ret = 1; + } else if (o == Py_True) { + lua_pushboolean(L, 1); + ret = 1; + } else if (o == Py_False) { + lua_pushboolean(L, 0); + ret = 1; + } else if (PyUnicode_Check(o) || PyBytes_Check(o)) { + PyObject *bstr = PyUnicode_AsEncodedString(o, "utf-8", NULL); + Py_ssize_t len; + char *s; + + PyErr_Clear(); + PyBytes_AsStringAndSize(bstr ? bstr : o, &s, &len); + lua_pushlstring(L, s, len); + if (bstr) Py_DECREF(bstr); + ret = 1; +#if PY_MAJOR_VERSION < 3 + } else if (PyInt_Check(o)) { + lua_pushnumber(L, (lua_Number)PyInt_AsLong(o)); + ret = 1; +#endif + } else if (PyLong_Check(o)) { + lua_pushnumber(L, (lua_Number)PyLong_AsLong(o)); + ret = 1; + } else if (PyFloat_Check(o)) { + lua_pushnumber(L, (lua_Number)PyFloat_AsDouble(o)); + ret = 1; + } else if (LuaObject_Check(o)) { + lua_rawgeti(L, LUA_REGISTRYINDEX, ((LuaObject*)o)->ref); + ret = 1; + } else { + int asindx = PyDict_Check(o) || PyList_Check(o) || PyTuple_Check(o); + ret = py_convert_custom(L, o, asindx); + } + return ret; +} + +static int py_object_call(lua_State *L) +{ + PyObject *args; + PyObject *value; + PyObject *pKywdArgs = NULL; + int nargs = lua_gettop(L)-1; + int ret = 0; + int i; + py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); + assert(obj); + + if (!PyCallable_Check(obj->o)) + return luaL_error(L, "object is not callable"); + + // passing a single table forces named keyword call style, e.g. plt.plot{x, y, c='red'} + if (nargs==1 && lua_istable(L, 2)) { + lua_pushnil(L); /* first key */ + nargs=0; + while (lua_next(L, 2) != 0) { + if (lua_isnumber(L, -2)) { + int i = lua_tointeger(L, -2); + nargs = i < nargs ? nargs : i; + } + lua_pop(L, 1); + } + args = PyTuple_New(nargs); + if (!args) { + PyErr_Print(); + return luaL_error(L, "failed to create arguments tuple"); + } + pKywdArgs = PyDict_New(); + if (!pKywdArgs) { + Py_DECREF(args); + PyErr_Print(); + return luaL_error(L, "failed to create arguments dictionary"); + } + lua_pushnil(L); /* first key */ + while (lua_next(L, 2) != 0) { + if (lua_isnumber(L, -2)) { + PyObject *arg = LuaConvert(L, -1); + if (!arg) { + Py_DECREF(args); + Py_DECREF(pKywdArgs); + return luaL_error(L, "failed to convert argument #%d", lua_tointeger(L, -2)); + } + PyTuple_SetItem(args, lua_tointeger(L, -2)-1, arg); + } + else if (lua_isstring(L, -2)) { + PyObject *arg = LuaConvert(L, -1); + if (!arg) { + Py_DECREF(args); + Py_DECREF(pKywdArgs); + return luaL_error(L, "failed to convert argument '%s'", lua_tostring(L, -2)); + } + PyDict_SetItemString(pKywdArgs, lua_tostring(L, -2), arg); + Py_DECREF(arg); + } + lua_pop(L, 1); + } + } + // regular call style e.g. plt.plot(x, y) + else { + args = PyTuple_New(nargs); + if (!args) { + PyErr_Print(); + return luaL_error(L, "failed to create arguments tuple"); + } + for (i = 0; i != nargs; i++) { + PyObject *arg = LuaConvert(L, i+2); + if (!arg) { + Py_DECREF(args); + return luaL_error(L, "failed to convert argument #%d", i+1); + } + PyTuple_SetItem(args, i, arg); + } + } + + value = PyObject_Call(obj->o, args, pKywdArgs); + Py_DECREF(args); + if (pKywdArgs) Py_DECREF(pKywdArgs); + + if (value) { + ret = py_convert(L, value); + Py_DECREF(value); + } else { + PyErr_Print(); + luaL_error(L, "error calling python function"); + } + + return ret; +} + +static int _p_object_newindex_set(lua_State *L, py_object *obj, + int keyn, int valuen) +{ + PyObject *value; + PyObject *key = LuaConvert(L, keyn); + + if (!key) { + return luaL_argerror(L, 1, "failed to convert key"); + } + + if (!lua_isnil(L, valuen)) { + value = LuaConvert(L, valuen); + if (!value) { + Py_DECREF(key); + return luaL_argerror(L, 1, "failed to convert value"); + } + + if (PyObject_SetItem(obj->o, key, value) == -1) { + PyErr_Print(); + luaL_error(L, "failed to set item"); + } + + Py_DECREF(value); + } else { + if (PyObject_DelItem(obj->o, key) == -1) { + PyErr_Print(); + luaL_error(L, "failed to delete item"); + } + } + + Py_DECREF(key); + return 0; +} + +static int py_object_newindex_set(lua_State *L) +{ + py_object *obj = (py_object*) luaL_checkudata(L, lua_upvalueindex(1), POBJECT); + if (lua_gettop(L) != 2) + return luaL_error(L, "invalid arguments"); + + return _p_object_newindex_set(L, obj, 1, 2); +} + +static int py_object_newindex(lua_State *L) +{ + PyObject *value; + const char *attr; + py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); + assert(obj); + + if (obj->asindx || lua_type(L, 2) != LUA_TSTRING) + return _p_object_newindex_set(L, obj, 2, 3); + + attr = luaL_checkstring(L, 2); + assert(attr); + + value = LuaConvert(L, 3); + if (!value) { + return luaL_argerror(L, 1, "failed to convert value"); + } + + if (PyObject_SetAttrString(obj->o, attr, value) == -1) { + Py_DECREF(value); + PyErr_Print(); + return luaL_error(L, "failed to set value"); + } + + Py_DECREF(value); + return 0; +} + +static int _p_object_index_get(lua_State *L, py_object *obj, int keyn) +{ + PyObject *key = LuaConvert(L, keyn); + PyObject *item; + int ret = 0; + + if (!key) { + return luaL_argerror(L, 1, "failed to convert key"); + } + + item = PyObject_GetItem(obj->o, key); + if (!item) { + item = PyObject_GetAttr(obj->o, key); + } + + Py_DECREF(key); + + if (item) { + ret = py_convert(L, item); + Py_DECREF(item); + } else { + PyErr_Clear(); + if (lua_gettop(L) > keyn) { + lua_pushvalue(L, keyn+1); + ret = 1; + } + } + + return ret; +} + +static int py_object_index_get(lua_State *L) +{ + py_object *obj = (py_object*) luaL_checkudata(L, lua_upvalueindex(1), POBJECT); + int top = lua_gettop(L); + if (top < 1 || top > 2) + return luaL_error(L, "invalid arguments"); + + return _p_object_index_get(L, obj, 1); +} + +static int py_object_index(lua_State *L) +{ + int ret = 0; + PyObject *value; + const char *attr; + py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); + assert(obj); + + if (obj->asindx || lua_type(L, 2) != LUA_TSTRING) + return _p_object_index_get(L, obj, 2); + + attr = luaL_checkstring(L, 2); + assert(attr); + + if (strcmp(attr, "__get") == 0) { + lua_pushvalue(L, 1); + lua_pushcclosure(L, py_object_index_get, 1); + return 1; + } else if (strcmp(attr, "__set") == 0) { + lua_pushvalue(L, 1); + lua_pushcclosure(L, py_object_newindex_set, 1); + return 1; + } + + + value = PyObject_GetAttrString(obj->o, attr); + if (value) { + ret = py_convert(L, value); + Py_DECREF(value); + } else { + PyErr_Clear(); + lua_pushnil(L); + ret = 1; + } + + return ret; +} + +static int py_object_gc(lua_State *L) +{ + py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); + assert(obj); + + Py_DECREF(obj->o); + return 0; +} + +static int py_object_tostring(lua_State *L) +{ + py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); + assert(obj); + + PyObject *repr = PyObject_Str(obj->o); + if (!repr) + { + char buf[256]; + snprintf(buf, 256, "python object: %p", (void *)obj->o); + lua_pushstring(L, buf); + PyErr_Clear(); + } + else + { + py_convert(L, repr); + assert(lua_type(L, -1) == LUA_TSTRING); + Py_DECREF(repr); + } + return 1; +} + +static int py_operator_lambda(lua_State *L, const char *op) +{ + static char script_buff[] = "lambda a, b: a b"; + static size_t len = sizeof(script_buff) / sizeof(script_buff[0]); + snprintf(script_buff, len, "lambda a, b: a %s b", op); + + lua_pushcfunction(L, py_eval); + lua_pushlstring(L, script_buff, len); + lua_call(L, 1, 1); + return luaL_ref(L, LUA_REGISTRYINDEX); +} + +#define make_pyoperator(opname, opliteral) \ + static int py_object_ ## opname(lua_State *L) \ + { \ + static int op_ref = LUA_REFNIL; \ + if (op_ref == LUA_REFNIL) op_ref = py_operator_lambda(L, opliteral); \ + \ + lua_rawgeti(L, LUA_REGISTRYINDEX, op_ref); \ + lua_insert(L, 1); \ + return py_object_call(L); \ + } \ + struct opname ## __LINE__ // force semi + +make_pyoperator(_pow, "**"); +make_pyoperator(_mul, "*"); +make_pyoperator(_div, "/"); +make_pyoperator(_add, "+"); +make_pyoperator(_sub, "-"); + +static const luaL_Reg py_object_mt[] = +{ + {"__call", py_object_call}, + {"__index", py_object_index}, + {"__newindex", py_object_newindex}, + {"__gc", py_object_gc}, + {"__tostring", py_object_tostring}, + {"__pow", py_object__pow}, + {"__mul", py_object__mul}, + {"__div", py_object__div}, + {"__add", py_object__add}, + {"__sub", py_object__sub}, + {NULL, NULL} +}; + +static int py_run(lua_State *L, int eval) +{ + const char *s = luaL_checkstring(L, 1); + PyObject *m, *d, *o; + int ret = 0; + + lua_settop(L, 1); + + if (!eval) + { + lua_pushliteral(L, "\n"); + lua_concat(L, 2); + + s = luaL_checkstring(L, 1); + } + + m = PyImport_AddModule("__main__"); + if (!m) + return luaL_error(L, "Can't get __main__ module"); + + d = PyModule_GetDict(m); + + o = PyRun_String(s, eval ? Py_eval_input : Py_file_input, + d, d); + if (!o) + { + PyErr_Print(); + return 0; + } + + if (py_convert(L, o)) + ret = 1; + + Py_DECREF(o); + +#if PY_MAJOR_VERSION < 3 + if (Py_FlushLine()) +#endif + PyErr_Clear(); + + return ret; +} + +static int py_execute(lua_State *L) +{ + return py_run(L, 0); +} + +static int py_eval(lua_State *L) +{ + return py_run(L, 1); +} + +static int py_asindx(lua_State *L) +{ + py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); + assert(obj); + + return py_convert_custom(L, obj->o, 1); +} + +static int py_asattr(lua_State *L) +{ + py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); + assert(obj); + + return py_convert_custom(L, obj->o, 0); +} + +static int py_asfunc_call(lua_State *L) +{ + lua_pushvalue(L, lua_upvalueindex(1)); + lua_insert(L, 1); + return py_object_call(L); +} + +static int py_asfunc(lua_State *L) +{ + py_object *obj = luaL_checkudata(L, 1, POBJECT); + if (!PyCallable_Check(obj->o)) + return luaL_error(L, "object is not callable"); + + lua_settop(L, 1); + lua_pushcclosure(L, py_asfunc_call, 1); + + return 1; +} + +static int py_globals(lua_State *L) +{ + PyObject *globals; + + if (lua_gettop(L) != 0) { + return luaL_error(L, "invalid arguments"); + } + + globals = PyEval_GetGlobals(); + if (!globals) { + PyObject *module = PyImport_AddModule("__main__"); + if (!module) { + return luaL_error(L, "Can't get __main__ module"); + } + globals = PyModule_GetDict(module); + } + + if (!globals) { + PyErr_Print(); + return luaL_error(L, "can't get globals"); + } + + return py_convert_custom(L, globals, 1); +} + +static int py_locals(lua_State *L) +{ + PyObject *locals; + + if (lua_gettop(L) != 0) { + return luaL_error(L, "invalid arguments"); + } + + locals = PyEval_GetLocals(); + if (!locals) + return py_globals(L); + + return py_convert_custom(L, locals, 1); +} + +static int py_builtins(lua_State *L) +{ + PyObject *builtins; + + if (lua_gettop(L) != 0) { + return luaL_error(L, "invalid arguments"); + } + + builtins = PyEval_GetBuiltins(); + if (!builtins) { + PyErr_Print(); + return luaL_error(L, "failed to get builtins"); + } + + return py_convert_custom(L, builtins, 1); +} + +static int py_import(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + PyObject *module = PyImport_ImportModule((char*)name); + int ret; + + if (!module) + { + PyErr_Print(); + return luaL_error(L, "failed importing '%s'", name); + } + + ret = py_convert_custom(L, module, 0); + Py_DECREF(module); + return ret; +} + +py_object* luaPy_to_pobject(lua_State *L, int n) +{ + if(!lua_getmetatable(L, n)) return NULL; + luaL_getmetatable(L, POBJECT); + int is_pobject = lua_rawequal(L, -1, -2); + lua_pop(L, 2); + + return is_pobject ? (py_object *) lua_touserdata(L, n) : NULL; +} + +static const luaL_Reg py_lib[] = +{ + {"execute", py_execute}, + {"eval", py_eval}, + {"asindx", py_asindx}, + {"asattr", py_asattr}, + {"asfunc", py_asfunc}, + {"locals", py_locals}, + {"globals", py_globals}, + {"builtins", py_builtins}, + {"import", py_import}, + {NULL, NULL} +}; + +LUA_API int luaopen_python(lua_State *L) +{ + int rc; + + /* Register module */ + luaL_newlib(L, py_lib); + + /* Register python object metatable */ + luaL_newmetatable(L, POBJECT); + luaL_setfuncs(L, py_object_mt, 0); + lua_pop(L, 1); + + /* Initialize Lua state in Python territory */ + if (!LuaState) LuaState = L; + + /* Initialize Python interpreter */ + if (!Py_IsInitialized()) + { + PyObject *luam, *mainm, *maind; +#if PY_MAJOR_VERSION >= 3 + wchar_t *argv[] = {L"", 0}; +#else + char *argv[] = {"", 0}; +#endif + Py_SetProgramName(argv[0]); + PyImport_AppendInittab("lua", PyInit_lua); + + /* Loading python library symbols so that dynamic extensions don't throw symbol not found error. + Ref Link: http://stackoverflow.com/questions/29880931/importerror-and-pyexc-systemerror-while-embedding-python-script-within-c-for-pam + */ +#if defined(__linux__) +# define STR(s) #s +# define PYLIB_STR(s) STR(s) +#if !defined(PYTHON_LIBRT) +# error PYTHON_LIBRT must be defined when building under Linux! +#endif + void *ok = dlopen(PYLIB_STR(PYTHON_LIBRT), RTLD_NOW | RTLD_GLOBAL); + assert(ok); (void) ok; +#endif + + Py_Initialize(); + PySys_SetArgv(1, argv); + /* Import 'lua' automatically. */ + luam = PyImport_ImportModule("lua"); + if (!luam) + return luaL_error(L, "Can't import lua module"); + + mainm = PyImport_AddModule("__main__"); + if (!mainm) + { + Py_DECREF(luam); + return luaL_error(L, "Can't get __main__ module"); + } + + maind = PyModule_GetDict(mainm); + PyDict_SetItemString(maind, "lua", luam); + Py_DECREF(luam); + } + + /* Register 'none' */ + rc = py_convert_custom(L, Py_None, 0); + if (!rc) + return luaL_error(L, "failed to convert none object"); + + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, "Py_None"); /* registry.Py_None */ + + lua_setfield(L, -2, "none"); /* python.none */ + + return 1; +} diff --git a/src2/bridge/pythoninlua.h b/src2/bridge/pythoninlua.h new file mode 100644 index 0000000..dada102 --- /dev/null +++ b/src2/bridge/pythoninlua.h @@ -0,0 +1,44 @@ +/* + + Lunatic Python + -------------- + + Copyright (c) 2002-2005 Gustavo Niemeyer + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +#ifndef PYTHONINLUA_H +#define PYTHONINLUA_H + +#define POBJECT "POBJECT" + +#if PY_MAJOR_VERSION < 3 + #define PyBytes_Check PyString_Check + #define PyBytes_AsStringAndSize PyString_AsStringAndSize +#endif + +int py_convert(lua_State *L, PyObject *o); + +typedef struct +{ + PyObject *o; + int asindx; +} py_object; + +py_object* luaPy_to_pobject(lua_State *L, int n); +LUA_API int luaopen_python(lua_State *L); + +#endif diff --git a/src2/generator.cobalt b/src2/generator.cobalt new file mode 100644 index 0000000..dd44939 --- /dev/null +++ b/src2/generator.cobalt @@ -0,0 +1 @@ +// Cobalt code generator. Generates Lua code from AST in astclass.cobalt. \ No newline at end of file diff --git a/src2/pyast.cobalt b/src2/pyast.cobalt new file mode 100644 index 0000000..cfccac5 --- /dev/null +++ b/src2/pyast.cobalt @@ -0,0 +1 @@ +// Takes in a python script and generates a AST to be used. \ No newline at end of file From c248fe968ae1d3f3115c1bb9acdad4b1aa9a27df Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sun, 20 Aug 2023 20:08:33 -0500 Subject: [PATCH 07/77] Update --- src2/API.md | 12 +++ src2/astclass.cobalt | 85 +++++++++++++++++++ src2/cli/main.c | 27 ++++++ src2/init.cobalt | 11 +++ src2/pyast.cobalt | 3 +- src2/template.cobalt | 6 ++ src2/template/.gitignore | 6 ++ src2/template/README.md | 25 ++++++ .../compiled/client/init.client.example | 1 + .../compiled/server/init.server.example | 1 + src2/template/compiled/shared/Hello.example | 3 + src2/template/default.project.json | 72 ++++++++++++++++ src2/template/src/client/init.client.lua | 1 + src2/template/src/server/init.server.lua | 1 + src2/template/src/shared/Hello.lua | 3 + src2/test.cobalt | 0 16 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 src2/API.md create mode 100644 src2/cli/main.c create mode 100644 src2/init.cobalt create mode 100644 src2/template.cobalt create mode 100644 src2/template/.gitignore create mode 100644 src2/template/README.md create mode 100644 src2/template/compiled/client/init.client.example create mode 100644 src2/template/compiled/server/init.server.example create mode 100644 src2/template/compiled/shared/Hello.example create mode 100644 src2/template/default.project.json create mode 100644 src2/template/src/client/init.client.lua create mode 100644 src2/template/src/server/init.server.lua create mode 100644 src2/template/src/shared/Hello.lua create mode 100644 src2/test.cobalt diff --git a/src2/API.md b/src2/API.md new file mode 100644 index 0000000..9e89fab --- /dev/null +++ b/src2/API.md @@ -0,0 +1,12 @@ +# API +Goal API +## init +```bash +$ rpyc init +``` +Clones template +## . +```bash +$ rpyc . # or any other path +``` +Compiles all files in the source folder to the compiled folder. diff --git a/src2/astclass.cobalt b/src2/astclass.cobalt index 1ae7c58..de6f31c 100644 --- a/src2/astclass.cobalt +++ b/src2/astclass.cobalt @@ -1 +1,86 @@ // AST Class + +/* Methods +- node->getChildren() - Get all children of node - Returns array of nodes +- node->getType() - Get type of node - Returns string +- node.parent - Get parent of node - Returns node +- node.name - Get name of node - Returns string +- node.properties - Get all properties of node - Returns array of strings + +- node->add(name, properties, type) - Create empty node inside of node - Returns node +- ast.head() - Create empty node head - Returns node +*/ + +// Define the AST class +var AST = {}; +AST.__index = AST; + +// Constructor function +function AST.new(node) { + var this = setmetatable({}, AST); + this.node = node; + return this; +} + +// Method to get the node type +function AST::getType() { + return this.node.type; +} + +// Method to get the node children +function AST::getChildren() { + var children = {}; + for( i, child in ipairs(this.node.children) ) { + table.insert(children, AST.new(child)); + } + return children; +} + +// Method to get the parent node +function AST::getParent() { + return AST.new(this.node.parent); +} + +// Method to get the node name +function AST::getName() { + return this.node.name; +} + +// Method to get the node properties +function AST::getProperties() { + return this.node.properties; +} + +// Method to add a child node +function AST::addChild(name, properties, type) { + var child = { + name = name, + properties = properties, + type = type, + children = {}, + parent = this.node + }; + table.insert(this.node.children, child); + return AST.new(child); +} + +// Static method to create a new head node +function AST.head() { + var head = { + name = "head", + properties = {}, + type = "head", + children = {}, + parent = null + }; + return AST.new(head); +} + +// Example usage +var head = AST.head(); +var child1 = head->addChild("child1", {"prop1", "prop2"}, "type1"); +var child2 = head->addChild("child2", {"prop3", "prop4"}, "type2"); +print(head->getType()); // prints "head" +print(child1->getType()); // prints "type1" +print(child2->getType()); // prints "type2" +print(child1->getParent()->getType()); // prints "head" diff --git a/src2/cli/main.c b/src2/cli/main.c new file mode 100644 index 0000000..2e0a96c --- /dev/null +++ b/src2/cli/main.c @@ -0,0 +1,27 @@ +#include +#include +#include + +int main(int argc, char *argv[]){ + // Mirror the arguments like so + // cobalt -e "import('rpyc').cli(ARG1, ARG2, ARG3, ...)" + + // use popen to run the command + + char command[256] = "cobalt -e \"import('rpyc').cli("; // 256 is arbitrary + for(int i = 1; i < argc; i++){ + strcat(command, argv[i]); + if(i != argc - 1){ + strcat(command, ", "); + } + } + strcat(command, ")\""); + + FILE *fp = popen(command, "r"); + char buff[256]; + while(fgets(buff, 256, fp) != NULL){ + printf("%s", buff); + } + pclose(fp); + return 0; +} \ No newline at end of file diff --git a/src2/init.cobalt b/src2/init.cobalt new file mode 100644 index 0000000..05d7b35 --- /dev/null +++ b/src2/init.cobalt @@ -0,0 +1,11 @@ +var self = {} + +function self.cli(...){ + var args = {...} + + if (#args != 1){ + error("Invalid number of arguments") + os.exit(1) + } +} +return self \ No newline at end of file diff --git a/src2/pyast.cobalt b/src2/pyast.cobalt index cfccac5..8d12172 100644 --- a/src2/pyast.cobalt +++ b/src2/pyast.cobalt @@ -1 +1,2 @@ -// Takes in a python script and generates a AST to be used. \ No newline at end of file +// Takes in a python script and generates a AST to be used. +ast = import("ast") diff --git a/src2/template.cobalt b/src2/template.cobalt new file mode 100644 index 0000000..55a0f34 --- /dev/null +++ b/src2/template.cobalt @@ -0,0 +1,6 @@ +// Template manager, allows you to duplicate template. +function new(format){ + // Duplicate `scriptpath`/template to cwd + var scriptpath = debug.getinfo(1).source->sub(2) + var scriptdir = scriptpath->match("(.*/)") +} \ No newline at end of file diff --git a/src2/template/.gitignore b/src2/template/.gitignore new file mode 100644 index 0000000..a6f495e --- /dev/null +++ b/src2/template/.gitignore @@ -0,0 +1,6 @@ +# Project place file +/template.rbxlx + +# Roblox Studio lock files +/*.rbxlx.lock +/*.rbxl.lock \ No newline at end of file diff --git a/src2/template/README.md b/src2/template/README.md new file mode 100644 index 0000000..f681d4f --- /dev/null +++ b/src2/template/README.md @@ -0,0 +1,25 @@ +# template +Generated by [Rojo](https://github.com/rojo-rbx/rojo) 7.3.0. Modified template +for roblox-pyc. + + +## Getting Started +To compile the `/src` folder, use: +```bash +rpyc . +# Quickly compiles all files in the source folder to the compiled folder. +``` +To build the place from scratch, use: + +```bash +rojo build -o "template.rbxlx" +# Generates a place file from the compiled folder. +``` + +Next, open `template.rbxlx` in Roblox Studio and start the Rojo server: + +```bash +rojo serve +``` + +For more help, check out [the Rojo documentation](https://rojo.space/docs). \ No newline at end of file diff --git a/src2/template/compiled/client/init.client.example b/src2/template/compiled/client/init.client.example new file mode 100644 index 0000000..692fe35 --- /dev/null +++ b/src2/template/compiled/client/init.client.example @@ -0,0 +1 @@ +print("Hello world, from client!") diff --git a/src2/template/compiled/server/init.server.example b/src2/template/compiled/server/init.server.example new file mode 100644 index 0000000..062823b --- /dev/null +++ b/src2/template/compiled/server/init.server.example @@ -0,0 +1 @@ +print("Hello world, from server!") diff --git a/src2/template/compiled/shared/Hello.example b/src2/template/compiled/shared/Hello.example new file mode 100644 index 0000000..bd356c1 --- /dev/null +++ b/src2/template/compiled/shared/Hello.example @@ -0,0 +1,3 @@ +return function() + print("Hello, world!") +end diff --git a/src2/template/default.project.json b/src2/template/default.project.json new file mode 100644 index 0000000..bd1e7fa --- /dev/null +++ b/src2/template/default.project.json @@ -0,0 +1,72 @@ +{ + "name": "template", + "tree": { + "$className": "DataModel", + + "ReplicatedStorage": { + "Shared": { + "$path": "compiled/shared" + } + }, + + "ServerScriptService": { + "Server": { + "$path": "compiled/server" + } + }, + + "StarterPlayer": { + "StarterPlayerScripts": { + "Client": { + "$path": "compiled/client" + } + } + }, + + "Workspace": { + "$properties": { + "FilteringEnabled": true + }, + "Baseplate": { + "$className": "Part", + "$properties": { + "Anchored": true, + "Color": [ + 0.38823, + 0.37254, + 0.38823 + ], + "Locked": true, + "Position": [ + 0, + -10, + 0 + ], + "Size": [ + 512, + 20, + 512 + ] + } + } + }, + "Lighting": { + "$properties": { + "Ambient": [ + 0, + 0, + 0 + ], + "Brightness": 2, + "GlobalShadows": true, + "Outlines": false, + "Technology": "Voxel" + } + }, + "SoundService": { + "$properties": { + "RespectFilteringEnabled": true + } + } + } +} \ No newline at end of file diff --git a/src2/template/src/client/init.client.lua b/src2/template/src/client/init.client.lua new file mode 100644 index 0000000..692fe35 --- /dev/null +++ b/src2/template/src/client/init.client.lua @@ -0,0 +1 @@ +print("Hello world, from client!") diff --git a/src2/template/src/server/init.server.lua b/src2/template/src/server/init.server.lua new file mode 100644 index 0000000..062823b --- /dev/null +++ b/src2/template/src/server/init.server.lua @@ -0,0 +1 @@ +print("Hello world, from server!") diff --git a/src2/template/src/shared/Hello.lua b/src2/template/src/shared/Hello.lua new file mode 100644 index 0000000..bd356c1 --- /dev/null +++ b/src2/template/src/shared/Hello.lua @@ -0,0 +1,3 @@ +return function() + print("Hello, world!") +end diff --git a/src2/test.cobalt b/src2/test.cobalt new file mode 100644 index 0000000..e69de29 From ca0926d0b4821a899133b3e6261686d231aab073 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sun, 20 Aug 2023 20:18:22 -0500 Subject: [PATCH 08/77] Expand --- src2/Floorplan.md | 96 ++++++++++++++++++++++++++++++++++++++++++++++ src2/cast.cobalt | 1 + src2/cppast.cobalt | 1 + src2/pyast.cobalt | 3 +- 4 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 src2/Floorplan.md create mode 100644 src2/cast.cobalt create mode 100644 src2/cppast.cobalt diff --git a/src2/Floorplan.md b/src2/Floorplan.md new file mode 100644 index 0000000..cbe12a5 --- /dev/null +++ b/src2/Floorplan.md @@ -0,0 +1,96 @@ +# Floorplan +A plan on how the code will be organize: +- util: Required by all code, provides basic functions +- interface: Core compiler, wrapped by init.cobalt with the API and CLI +- generator: Used by interface to generate code from the ast +- template: Provides a interface to duplicate the template for a inputted extension +- /bridge: provides bridging functions for Lua and Python +- astclass: provides a base class for ast to pyast to generate +- pyast: Uses bridge and uses the python library `ast` to generate a ast and convert it to astclass +- cast: Uses bridge and uses the python library `libclang` to generate a C99 ast and convert it to astclass +- cppast: Uses bridge and uses the python library `libclang` to generate a C++99 (base) ast and convert it to astclass + +## AST Reference +```lua +-- variable declarations +local a = 1 +local b = "hello" +local c = true + +-- if statement +if c then + print(b) +else + print("world") +end + +-- for loop with index +for i = 1, 5 do + print(i) +end + +-- for loop with table +local t = {1, 2, 3, 4, 5} +for i, v in ipairs(t) do + print(i, v) +end + +-- function definition +function add(x, y) + return x + y +end + +-- function call +local sum = add(a, 2) +print(sum) + +-- table indexing +local person = {name = "Alice", age = 30} +print(person.name) + +-- table new indexing +local mt = { + __newindex = function(t, k, v) + print("setting", k, "to", v) + rawset(t, k, v) + end +} +local t = {} +setmetatable(t, mt) +t.foo = "bar" +``` + +Equal AST: +```bash +- SOURCE +-- DECL A 1 NUMBER +-- DECL B "hello" STRING +-- DECL C true BOOLEAN +-- IF C +--- PRINT B +-- ELSE +--- PRINT "world" +-- ENDSTMT +-- FOR I 1 5 +--- PRINT I +-- ENDSTMT +-- DECL T {1, 2, 3, 4, 5} TABLE +-- FOR I V T +--- PRINT I V +-- ENDSTMT +-- DECL FUNCTION add X Y +--- RETURN X + Y +-- ENDSTMT +-- DECL SUM add A 2 +-- PRINT SUM +-- DECL PERSON {name = "Alice", age = 30} TABLE +-- PRINT PERSON.name +-- DECL MT {} +-- DECL MT __newindex FUNCTION T K V +--- PRINT "setting" K "to" V +--- RAWSET T K V +-- ENDSTMT +-- DECL T {} +-- CALL setmetatable T MT +-- SET T foo "bar" +``` diff --git a/src2/cast.cobalt b/src2/cast.cobalt new file mode 100644 index 0000000..75a135e --- /dev/null +++ b/src2/cast.cobalt @@ -0,0 +1 @@ +// C code to astclass \ No newline at end of file diff --git a/src2/cppast.cobalt b/src2/cppast.cobalt new file mode 100644 index 0000000..6650d72 --- /dev/null +++ b/src2/cppast.cobalt @@ -0,0 +1 @@ +// C++ AST to astclass \ No newline at end of file diff --git a/src2/pyast.cobalt b/src2/pyast.cobalt index 8d12172..d9a21fe 100644 --- a/src2/pyast.cobalt +++ b/src2/pyast.cobalt @@ -1,2 +1 @@ -// Takes in a python script and generates a AST to be used. -ast = import("ast") +// Python code to astclass \ No newline at end of file From f0da034475a88b3ed0c29a2395d9d8e549c70bd0 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sun, 20 Aug 2023 20:20:58 -0500 Subject: [PATCH 09/77] Rephrase --- src2/generator.cobalt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src2/generator.cobalt b/src2/generator.cobalt index dd44939..b2707e2 100644 --- a/src2/generator.cobalt +++ b/src2/generator.cobalt @@ -1 +1 @@ -// Cobalt code generator. Generates Lua code from AST in astclass.cobalt. \ No newline at end of file +// Luau code generator. Generates Luau code from AST in astclass.cobalt. \ No newline at end of file From 19a88f8f684c33375f00711ed39b5af9b75171a2 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sun, 20 Aug 2023 20:45:02 -0500 Subject: [PATCH 10/77] Visitor --- src2/Floorplan.md | 17 ++-- src2/astclass.cobalt | 13 ++- src2/generator.cobalt | 186 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 206 insertions(+), 10 deletions(-) diff --git a/src2/Floorplan.md b/src2/Floorplan.md index cbe12a5..dad0f62 100644 --- a/src2/Floorplan.md +++ b/src2/Floorplan.md @@ -66,29 +66,30 @@ Equal AST: -- DECL A 1 NUMBER -- DECL B "hello" STRING -- DECL C true BOOLEAN +-- ASSIGN B "hi" STRING global=true -- IF C ---- PRINT B +--- CALL PRINT B -- ELSE ---- PRINT "world" +--- CALL PRINT "world" -- ENDSTMT -- FOR I 1 5 ---- PRINT I +--- CALL PRINT I -- ENDSTMT -- DECL T {1, 2, 3, 4, 5} TABLE -- FOR I V T ---- PRINT I V +--- CALL PRINT I V -- ENDSTMT -- DECL FUNCTION add X Y --- RETURN X + Y -- ENDSTMT -- DECL SUM add A 2 --- PRINT SUM +-- CALL PRINT SUM -- DECL PERSON {name = "Alice", age = 30} TABLE --- PRINT PERSON.name +-- CALL PRINT PERSON.name -- DECL MT {} -- DECL MT __newindex FUNCTION T K V ---- PRINT "setting" K "to" V ---- RAWSET T K V +--- CALL PRINT "setting" K "to" V +--- CALL RAWSET T K V -- ENDSTMT -- DECL T {} -- CALL setmetatable T MT diff --git a/src2/astclass.cobalt b/src2/astclass.cobalt index de6f31c..04be17e 100644 --- a/src2/astclass.cobalt +++ b/src2/astclass.cobalt @@ -28,7 +28,7 @@ function AST::getType() { } // Method to get the node children -function AST::getChildren() { +function AST::getChildren() { var children = {}; for( i, child in ipairs(this.node.children) ) { table.insert(children, AST.new(child)); @@ -64,6 +64,17 @@ function AST::addChild(name, properties, type) { return AST.new(child); } +// Method to get the node depth (How many parents are above it) +function AST::getDepth() { + var depth = 0; + var parent = this.node.parent; + while(parent != null) { + depth++; + parent = parent.parent; + } + return depth; +} + // Static method to create a new head node function AST.head() { var head = { diff --git a/src2/generator.cobalt b/src2/generator.cobalt index b2707e2..7e8eab2 100644 --- a/src2/generator.cobalt +++ b/src2/generator.cobalt @@ -1 +1,185 @@ -// Luau code generator. Generates Luau code from AST in astclass.cobalt. \ No newline at end of file +// Luau code generator. Generates Luau code from AST in astclass.cobalt. +astlib = import("astclass") +source = "" +util = import("util") + +// VISITOR UTILS +function write(txt, depth){ + depthstr = "" + for (i = 1, (depth || 1)) { + depthstr = depthstr.." " + } + source = source.."\n"..(depthstr)..txt +} +var alreadyDefined = [] + +// VISITS +// Visitor supports: +// - Source init +// - Declaration and assignment +// - Function calls +// - Return statements +// - If statements +// - Else statements +// - Elseif statements +// - While statements +// - For numeric statements +// - For table statements +// - Function statements + +var visits = { + "SOURCE": function(node){ + write() // Newline init + }, + "DECL": function(node){ + var properties = node->getProperties() + + var name = properties["name"] + var value = properties["value"] + var type = properties["type"] // Ignore for now + + write("local "..name.." = "..value, node->getDepth()) + table.insert(alreadyDefined, name) + }, + "ASSIGN": function(node){ + var properties = node->getProperties() + + var name = properties["name"] + var value = properties["value"] + + if (!alreadyDefined[name] && !properties["global"]){ + write("local "..name.." = "..value, node->getDepth()) + }else{ + write(name.." = "..value, node->getDepth()) + } + }, + "CALL": function(node){ + var properties = node->getProperties() + + var name = properties["name"] + var args = properties["args"] + + var argString = "" + + for (i, v in pairs(args)){ + argString = argString..v + if (i != #args){ + argString = argString..", " + } + } + + write(name.."("..argString..")", node->getDepth()) + }, + "RETURN": function(node){ + var properties = node->getProperties() + + var value = properties["value"] + + write("return "..value, node->getDepth()) + }, + "IF": function(node){ + var properties = node->getProperties() + + var condition = properties["condition"] + + write("if "..condition.." then", node->getDepth()) + + return null, true + }, + "ELSE": function(node){ + // source should currently end with "end" + if (util.endsWith(source, "end")){ + source = util.backwardsReplace(source, "end", "", 1) + write("else", node->getDepth()) + }else{ + error("Tried to generate Luau code, AST is incorrectly formatted. (err: ELSE MISSING END)") + } + + return null, true + }, + "ELSEIF": function(node){ + var properties = node->getProperties() + + var condition = properties["condition"] + + if (util.endsWith(source, "end")){ + source = util.backwardsReplace(source, "end", "", 1) + write("elseif "..condition.." then", node->getDepth()) + }else{ + error("Tried to generate Luau code, AST is incorrectly formatted. (err: ELSEIF MISSING END)") + } + + return null, true + }, + "WHILE": function(node){ + var properties = node->getProperties() + + var condition = properties["condition"] + + write("while "..condition.." do", node->getDepth()) + + return null, true + }, + "FORN": function(node){ + var properties = node->getProperties() + + var name = properties["name"] + var start = properties["start"] + var end = properties["end"] + + write("for "..name.." = "..start..", "..end.." do", node->getDepth()) + + return null, true + }, + "FORT": function(node){ + var properties = node->getProperties() + + var name = properties["name"] + var table = properties["table"] + + write("for "..name.." in "..table.." do", node->getDepth()) + + return null, true + }, + "FUNCTION": function(node){ + var properties = node->getProperties() + + var name = properties["name"] + var args = properties["args"] + + var argString = "" + + for (i, v in pairs(args)){ + argString = argString..v + if (i != #args){ + argString = argString..", " + } + } + + write("function "..name.."("..argString..")", node->getDepth()) + + return null, true + }, +} + +// VISITOR +function visit(node){ + var ignore, addend = visits[node->getType()](node) + if (!ignore){ + for (i, v in pairs(node->getChildren())){ + visit(v) + } + } + if (addend){ + write("end") + } +} +function generate(ast){ + if (ast->getType() == "SOURCE"){ + source = [[--// Generated with roblox-pyc (v3) by @AsynchronousAI \\--]] + visit(ast) + }else{ + error("Tried to generate Luau code, AST is incorrectly formatted. (err: MISSING SOURCE))") + } + return source; +} \ No newline at end of file From 7ab9f0e8faf81179247a0f663db812a33224e24d Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sun, 17 Sep 2023 12:44:20 -0500 Subject: [PATCH 11/77] Clean --- {src2 => src}/API.md | 0 {src2 => src}/Floorplan.md | 0 {src2 => src}/README.md | 0 src/__init__.py | 0 {src2 => src}/astclass.cobalt | 0 src/basecompilers.py | 250 --- src/bindings.py | 12 - src/binopdesc.py | 66 - src/boolopdesc.py | 20 - {src2 => src}/cast.cobalt | 0 {src2 => src}/cli/main.c | 0 src/climaker.py | 233 --- src/cmpopdesc.py | 27 - src/cnodevisitor.py | 125 -- src/colortext.py | 37 - src/config.py | 30 - src/configmanager.py | 51 - src/context.py | 39 - src/cpAST.py | 18 - {src2 => src}/cppast.cobalt | 0 src/ctranslator.py | 18 - src/errormanager.py | 33 - {src2 => src}/generator.cobalt | 0 src/header.py | 49 - {src2 => src}/init.cobalt | 0 src/installationmanager.py | 86 - src/loader.py | 32 - src/loopcounter.py | 11 - src/luainit.py | 160 -- src/luainitlua.lua | 1240 ------------ src/model.py | 1718 ----------------- src/nameconstdesc.py | 11 - src/nodevisitor.py | 967 ---------- src/parser.py | 1711 ---------------- src/plugin.py | 43 - {src2 => src}/pyast.cobalt | 0 src/pytranslator.py | 78 - src/reporter.py | 67 - src/robloxpy.py | 1127 ----------- src/symbolsstack.py | 26 - {src2 => src}/template.cobalt | 0 {src2 => src}/template/.gitignore | 0 {src2 => src}/template/README.md | 0 .../compiled/client/init.client.example | 0 .../compiled/server/init.server.example | 0 .../template/compiled/shared/Hello.example | 0 {src2 => src}/template/default.project.json | 0 .../template/src/client/init.client.lua | 0 .../template/src/server/init.server.lua | 0 {src2 => src}/template/src/shared/Hello.lua | 0 {src2 => src}/test.cobalt | 0 src/textcompiler.py | 80 - src/tokenendmode.py | 8 - src/unaryopdesc.py | 28 - {src2 => src}/util.cobalt | 0 src/util.py | 47 - src/wally.py | 59 - src/writer.py | 60 - src2/bridge/CMakeLists.txt | 19 - src2/bridge/luainpython.c | 596 ------ src2/bridge/luainpython.h | 52 - src2/bridge/pythoninlua.c | 675 ------- src2/bridge/pythoninlua.h | 44 - 63 files changed, 9953 deletions(-) rename {src2 => src}/API.md (100%) rename {src2 => src}/Floorplan.md (100%) rename {src2 => src}/README.md (100%) delete mode 100644 src/__init__.py rename {src2 => src}/astclass.cobalt (100%) delete mode 100644 src/basecompilers.py delete mode 100644 src/bindings.py delete mode 100644 src/binopdesc.py delete mode 100644 src/boolopdesc.py rename {src2 => src}/cast.cobalt (100%) rename {src2 => src}/cli/main.c (100%) delete mode 100644 src/climaker.py delete mode 100644 src/cmpopdesc.py delete mode 100644 src/cnodevisitor.py delete mode 100644 src/colortext.py delete mode 100644 src/config.py delete mode 100644 src/configmanager.py delete mode 100644 src/context.py delete mode 100644 src/cpAST.py rename {src2 => src}/cppast.cobalt (100%) delete mode 100644 src/ctranslator.py delete mode 100644 src/errormanager.py rename {src2 => src}/generator.cobalt (100%) delete mode 100644 src/header.py rename {src2 => src}/init.cobalt (100%) delete mode 100644 src/installationmanager.py delete mode 100644 src/loader.py delete mode 100644 src/loopcounter.py delete mode 100644 src/luainit.py delete mode 100644 src/luainitlua.lua delete mode 100644 src/model.py delete mode 100644 src/nameconstdesc.py delete mode 100644 src/nodevisitor.py delete mode 100644 src/parser.py delete mode 100644 src/plugin.py rename {src2 => src}/pyast.cobalt (100%) delete mode 100644 src/pytranslator.py delete mode 100644 src/reporter.py delete mode 100644 src/robloxpy.py delete mode 100644 src/symbolsstack.py rename {src2 => src}/template.cobalt (100%) rename {src2 => src}/template/.gitignore (100%) rename {src2 => src}/template/README.md (100%) rename {src2 => src}/template/compiled/client/init.client.example (100%) rename {src2 => src}/template/compiled/server/init.server.example (100%) rename {src2 => src}/template/compiled/shared/Hello.example (100%) rename {src2 => src}/template/default.project.json (100%) rename {src2 => src}/template/src/client/init.client.lua (100%) rename {src2 => src}/template/src/server/init.server.lua (100%) rename {src2 => src}/template/src/shared/Hello.lua (100%) rename {src2 => src}/test.cobalt (100%) delete mode 100644 src/textcompiler.py delete mode 100644 src/tokenendmode.py delete mode 100644 src/unaryopdesc.py rename {src2 => src}/util.cobalt (100%) delete mode 100644 src/util.py delete mode 100644 src/wally.py delete mode 100644 src/writer.py delete mode 100644 src2/bridge/CMakeLists.txt delete mode 100644 src2/bridge/luainpython.c delete mode 100644 src2/bridge/luainpython.h delete mode 100644 src2/bridge/pythoninlua.c delete mode 100644 src2/bridge/pythoninlua.h diff --git a/src2/API.md b/src/API.md similarity index 100% rename from src2/API.md rename to src/API.md diff --git a/src2/Floorplan.md b/src/Floorplan.md similarity index 100% rename from src2/Floorplan.md rename to src/Floorplan.md diff --git a/src2/README.md b/src/README.md similarity index 100% rename from src2/README.md rename to src/README.md diff --git a/src/__init__.py b/src/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src2/astclass.cobalt b/src/astclass.cobalt similarity index 100% rename from src2/astclass.cobalt rename to src/astclass.cobalt diff --git a/src/basecompilers.py b/src/basecompilers.py deleted file mode 100644 index 9c9e194..0000000 --- a/src/basecompilers.py +++ /dev/null @@ -1,250 +0,0 @@ -import sys - -import os -if not (os.path.dirname(os.path.abspath(__file__)).startswith(sys.path[-1])): - import pytranslator, parser, header - from errormanager import * - from configmanager import * - from util import * -else: - from . import pytranslator, parser, header - from .errormanager import * - from .configmanager import * - from .util import * - - - -def cppcompile(r, file, pluscount=False): - if file.endswith(".cpp") and os.environ.get("ENABLE_BETA_RPYC") == True: - # compile the file to a file with the same name and path but .lua - try: - newctranslator = parser.CodeConverter(file, getconfig("c", "dynamiclibpath", "None")) - newctranslator.parse( - os.path.join(r, file), - # C not C++ - flags=[ - '-I%s' % inc for inc in [] - ] + [ - '-D%s' % define for define in [] - ] + [ - '-std=%s' % getconfig("cpp", "std", "c++20") - ] + [ - '-stdlib=%s' % getconfig("cpp", "stdlib", "libc++") - ] - ) - path = os.path.join(r, file) - newctranslator.diagnostics(sys.stderr) - relative_path = backwordreplace(path,".cpp", ".lua", 1) - with open(relative_path, 'w') as out: - newctranslator.output(relative_path, out) - - #print(colortext.green("Compiled "+os.path.join(r, file))) - if pluscount: - pluscount.update(1) - pluscount.current += 1 - #global count - #count += 1 - except Exception as e: - if "To provide a path to libclang use Config.set_library_path() or Config.set_library_file()" in str(e): - print(error("dylib not found, use `roblox-pyc config`, c++, dynamiclibpath, and set the path to the dynamic library.")) - print(error(f"Compile Error!\n\n "+str(e), f"{os.path.join(r, file)}")) - debug("Compiler error "+str(e)) - if pluscount: - #pluscount.error() - pluscount.update(1) - pluscount.current += 1 - #global count - #count += 1 - - return 0 - else: - print(error("C is not enabled, please enable it in the config.")) - if pluscount: - #pluscount.error() - pluscount.update(1) - pluscount.current += 1 - #global count - #count += 1 - return 0 -def ccompile(r, file, pluscount=False): - if file.endswith(".c") and os.environ.get("ENABLE_BETA_RPYC") == True: - # compile the file to a file with the same name and path but .lua - try: - newctranslator = parser.CodeConverter(file, getconfig("c", "dynamiclibpath", "None")) - newctranslator.parse( - os.path.join(r, file), - # C not C++ - flags=[ - '-I%s' % inc for inc in [] - ] + [ - '-D%s' % define for define in [] - ] + [ - '-std=%s' % getconfig("c", "std", "c11") - ] + [ - '-stdlib=%s' % getconfig("c", "stdlib", "libc") - ] - ) - path = os.path.join(r, file) - - newctranslator.diagnostics(sys.stderr) - relative_path = backwordreplace(path,".c", ".lua", 1) - - with open(relative_path, 'w') as out: - newctranslator.output(relative_path, out) - - #print(colortext.green("Compiled "+os.path.join(r, file))) - if pluscount: - pluscount.update(1) - pluscount.current += 1 - #global count - #count += 1 - except Exception as e: - if "To provide a path to libclang use Config.set_library_path() or Config.set_library_file()" in str(e): - print(error("dylib not found, use `roblox-pyc config`, c, dynamiclibpath, and set the path to the dynamic library.")) - print(error(f"Compile Error!\n\n "+str(e), f"{os.path.join(r, file)}")) - debug("Compile error at "+str(e)) - if pluscount: - #pluscount.error() - pluscount.update(1) - pluscount.current += 1 - #global count - #count += 1 - return 0 - else: - print(error("C is not enabled, please enable it in the config.")) - if pluscount: - #pluscount.error() - pluscount.update(1) - pluscount.current += 1 - #global count - #count += 1 - return 0 -def pycompile(r, file, pluscount=False): - # compile the file to a file with the same name and path but .lua - contents = "" - - try: - with open(os.path.join(r, file)) as rf: - contents = rf.read() - except Exception as e: - print(error(f"Failed to read {os.path.join(r, file)}!\n\n "+str(e))) - # do not compile the file if it cannot be read - return - - try: - translator = pytranslator.Translator() - lua_code = translator.translate(contents) - #print(colortext.green("Compiled "+os.path.join(r, file))) - # get the relative path of the file and replace .py with .lua - path = os.path.join(r, file) - - relative_path = backwordreplace(path,".py", ".lua", 1) - - if not os.path.exists(os.path.dirname(relative_path)): - os.makedirs(os.path.dirname(relative_path)) - - with open(relative_path, "w") as f: - f.write(lua_code) - - if pluscount: - #pluscount.error() - pluscount.update(1) - pluscount.current += 1 - #global count - #count += 1 - except Exception as e: - print(error(f"Compile Error!\n\n "+str(e), f"{os.path.join(r, file)}")) - debug("Compile error at "+str(e)) - if pluscount: - #pluscount.error() - pluscount.update(1) - pluscount.current += 1 - #global count - #count += 1 - return 0 -def lunarcompile(r, file, pluscount=False): - # compile the file to a file with the same name and path but .lua - # Run command and check if anything is outputted to stderr, stdout, or stdin - - process = subprocess.Popen(["moonc", os.path.join(r, file)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = process.communicate() - - if stdout or stderr: - if stdout: - print(error(f"Compile Error!\n\n "+str(stdout), f"{os.path.join(r, file)}")) - if pluscount: - #pluscount.error() - pluscount.update(1) - pluscount.current += 1 - #global count - #count += 1 - return 0 - else: - print(error(f"Compile Error!\n\n "+str(stderr), f"{os.path.join(r, file)}")) - if pluscount: - #pluscount.error() - pluscount.update(1) - pluscount.current += 1 - #global count - #count += 1 - return 0 - else: - try: - newheader = header.lunarheader([]) - - # check if the new file has been created - if os.path.exists(os.path.join(r, file.replace(".moon", ".lua"))): - #print(colortext.green("Compiled "+os.path.join(r, file))) - with open(os.path.join(r, file.replace(".moon", ".lua")), "r") as f: - contents = f.read() - contents = backwordreplace(contents, "return", "", 1) - - else: - print(error("File error for "+os.path.join(r, file)+"!")) - if pluscount: - pluscount.update(1) - pluscount.current += 1 - #global count - #count += 1 - except Exception as e: - print(error(f"Compile Error!\n\n "+str(e), f"{os.path.join(r, file)}")) - - if pluscount: - #pluscount.error() - pluscount.update(1) - pluscount.current += 1 - #global count - #count += 1 - return 0 -def robloxtscompile(r, file, pluscount=False): - # Just add to pluscount, add later - try: - print(warn("At the moment roblox-ts is not supported, please wait for a future update.")) - if pluscount: - #pluscount.error() - pluscount.update(1) - pluscount.current += 1 - #global count - #count += 1 - except Exception as e: - print(error(f"Compile Error!\n\n "+str(e), f"{os.path.join(r, file)}")) - if pluscount: - #pluscount.error() - pluscount.update(1) - pluscount.current += 1 - #global count - #count += 1 - return 0 -def allcompile(r, file, pluscount): - # Checks what file it is then redirects to the correct compiler - if file.endswith(".py"): - pycompile(r, file, pluscount) - elif file.endswith(".moon"): - lunarcompile(r, file, pluscount) - elif file.endswith(".c"): - ccompile(r, file, pluscount) - elif file.endswith(".cpp"): - cppcompile(r, file, pluscount) - elif file.endswith(".ts"): - robloxtscompile(r, file, pluscount) - \ No newline at end of file diff --git a/src/bindings.py b/src/bindings.py deleted file mode 100644 index bf35581..0000000 --- a/src/bindings.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Provides python bindings to LuauAST and LuauRenderer.""" -# LuauAST: /robloxpyc/LuauAST (Typescript) -# LuauRenderer: /robloxpyc/LuauRenderer (Typescript) -# Script Path /robloxpyc/bindings.py - -# @AsynchronousAI - 2023 -import subprocess - -class TypescriptBridge: - def __init__(path): - pass - #I have no idea how to do this, but I'll try to figure it out. Maybe use C and make a bridge, or use JSON files to communicate and subprocess. (Most likely). \ No newline at end of file diff --git a/src/binopdesc.py b/src/binopdesc.py deleted file mode 100644 index e24108c..0000000 --- a/src/binopdesc.py +++ /dev/null @@ -1,66 +0,0 @@ -"""Binary operation description""" -import ast - - -_DEFAULT_FORMAT = "{left} {operation} {right}" - -def addfunc(left, right): - # check if left and right have anything other than 0-9 - # left and right are name objects, so we need to get the id - if (not isinstance(left, ast.Num) or not isinstance(right, ast.Num)): - return "safeadd({left}, {right})" - else : - return "{left} + {right}" -class BinaryOperationDesc: - """Binary operation description""" - - OPERATION = { - ast.Add: { - "value": "+", - "function": addfunc - }, - ast.Sub: { - "value": "-", - "format": _DEFAULT_FORMAT, - }, - ast.Mult: { - "value": "*", - "format": _DEFAULT_FORMAT, - }, - ast.Div: { - "value": "/", - "format": _DEFAULT_FORMAT, - }, - ast.Mod: { - "value": "", - "format": "formatmod({left}, {right})", - }, - ast.Pow: { - "value": "", - "format": "math.pow({left}, {right})", - }, - ast.FloorDiv: { - "value": "/", - "format": "math.floor({left} {operation} {right})", - }, - ast.LShift: { - "value": "", - "format": "bit32.lshift({left}, {right})", - }, - ast.RShift: { - "value": "", - "format": "bit32.rshift({left}, {right})", - }, - ast.BitOr: { - "value": "", - "format": "bit32.bor({left}, {right})", - }, - ast.BitAnd: { - "value": "", - "format": "bit32.band({left}, {right})", - }, - ast.BitXor: { - "value": "", - "format": "bit32.bxor({left}, {right})", - }, - } diff --git a/src/boolopdesc.py b/src/boolopdesc.py deleted file mode 100644 index 665638e..0000000 --- a/src/boolopdesc.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Boolean operation description""" -import ast - - -_DEFAULT_FORMAT = "{left} {operation} {right}" - - -class BooleanOperationDesc: - """Binary operation description""" - - OPERATION = { - ast.And: { - "value": "and", - "format": _DEFAULT_FORMAT, - }, - ast.Or: { - "value": "or", - "format": _DEFAULT_FORMAT, - }, - } diff --git a/src2/cast.cobalt b/src/cast.cobalt similarity index 100% rename from src2/cast.cobalt rename to src/cast.cobalt diff --git a/src2/cli/main.c b/src/cli/main.c similarity index 100% rename from src2/cli/main.c rename to src/cli/main.c diff --git a/src/climaker.py b/src/climaker.py deleted file mode 100644 index f1ba9b9..0000000 --- a/src/climaker.py +++ /dev/null @@ -1,233 +0,0 @@ -# TODO - -import sys - -import os - -from platform import uname - -if not (os.path.dirname(os.path.abspath(__file__)).startswith(sys.path[-1])): - from loader import loader - from textcompiler import * - from configmanager import * - from util import * - from basecompilers import * -else: - from .loader import loader - from .textcompiler import * - from .configmanager import * - from .util import * - from .basecompilers import * -import threading, shutil - -def newIncli(basefile, func): - def incli(): - # Get all the files inside of the path, look for all of them which are .py and even check inside of folders. If this is happening in the same directory as the script, do it in the sub directory test - path = os.getcwd() - - #global count - #count = 0 - localcount = 0 - for r, d, f in os.walk(path): - for file in f: - if basefile.__class__ == str: - if file.endswith(basefile): - localcount += 1 - else: - for i in basefile: - if file.endswith(i): - localcount += 1 - newloader = loader(localcount) - - for r, d, f in os.walk(path): - for file in f: - if basefile.__class__ == str: - if file.endswith(basefile): - threading.Thread(target=func, args=(r, file, newloader)).start() - #pycompile(r, file) - else: - othercompile(r, file) - else: - found = False - for i in basefile: - if file.endswith(i): - threading.Thread(target=func, args=(r, file, newloader)).start() - #pycompile(r, file) - found = True - if not found: - othercompile(r, file) - - newloader.yielduntil() - - print(colortext.green("Compiled Files!")) - if getconfig("general", "autocompile", False): - action = input("") - if action == "exit": - exit(0) - else: - incli() - else: - exit(0) - - return incli - -def newIncli2(basefile, func): - def incli2(): - # Just like incli, but duplicates the direcotry with -compiled and compiles the files in there, also runs filtercompiledfolder() on the directory - path = os.getcwd()+"-compiled" - if os.path.exists(path): - # delete the folder and child files - shutil.rmtree(path) - shutil.copytree(os.getcwd(), path) - - #global count - #count = 0 - localcount = 0 - for r, d, f in os.walk(path): - for file in f: - # if basefile is a string just check for that, if it is a list, check for all of them - if basefile.__class__ == str: - if file.endswith(basefile): - localcount += 1 - else: - for i in basefile: - if file.endswith(i): - localcount += 1 - - newloader = loader(localcount) - - for r, d, f in os.walk(path): - for file in f: - found = False - for i in basefile: - if file.endswith(i): - threading.Thread(target=func, args=(r, file, newloader)).start() - #pycompile(r, file) - found = True - if not found: - othercompile(r, file) - newloader.yielduntil() - filtercompiledfolder() - print(colortext.green("Compiled Files!")) - if getconfig("general", "autocompile", False): - action = input("") - if action == "exit": - exit(0) - else: - incli2() - return incli2 - -def replaceLuaFiles(r, file, basefile, commentart, luafilecontents): - # create new file with same name but .py and write the lua file contents to it - open(os.path.join(r, file.replace(".lua", basefile)), "x").close() - # write the old file contents as a py comment - open(os.path.join(r, file.replace(".lua", basefile)), "w").write(commentart.format(luafilecontents)) - print(colortext.green("Converted to py "+os.path.join(r, file)+" as "+file.replace(".lua", basefile))) - -def newLanguage(basefile, func, commentart, p=None): - def w(): - try: - incli = newIncli(basefile, func) - incli2 = newIncli2(basefile, func) - - if sys.argv.__len__() >= 1: - if sys.argv[1] == "p": - if p: - p() - else: - print(error("Plugin is not supported for this language.")) - elif sys.argv[1] == "lib": - # sys.argv[2] is the path to the file, create a new file there with the name robloxpyc.lua, and write the library to it - lib() - elif sys.argv[1] == "c": - decreapted("c") - # Go through every lua descendant file in the current directory and delete it and create a new file with the same name but .py - confirm = input(warn("Are you sure? This will delete all .lua files and add a "+basefile+" file with the same name.\n\nType 'yes' to continue.")) - if confirm == "yes": - path = os.getcwd() - - for r, d, f in os.walk(path): - for file in f: - if '.lua' in file: - luafilecontents = "" - with open(os.path.join(r, file), "r") as f: - luafilecontents = f.read() - - os.remove(os.path.join(r, file)) - replaceLuaFiles(r, file, basefile, commentart, luafilecontents) - - elif sys.argv[1] == "cd": - # Duplicate the cwd directory, the original will be renamed to -compiled and the new one will be renamed to the original. For the new one, go through every lua descendant file in the current directory and delete it and create a new file with the same name but .py - confirm = input(warn("Are you sure? This will duplicate the current directory and compile the files in the new directory.\n\nType 'yes' to continue.")) - if confirm == "yes" and uname().system == "Windows": - # check if cwd/default.project.json exists, if it does, then use that as the default project file - #if os.path.exists(os.getcwd()+"/default.project.json"): - # formatdefaultproj(os.getcwd()+"/default.project.json") - # rename directory to -compiled - path_name = os.getcwd() - #os.rename(path_name, path_name+"-compiled") - # duplicate the directory, remove the -compiled from the end - shutil.copytree(path_name, path_name + "-compiled") - # now we have 2 identical directories, one with -compiled and one without. for the one without, go through every lua descendant file in the current directory and delete it and create a new file with the same name but .py - path = os.getcwd() - - for r, d, f in os.walk(backwordreplace(path, "-compiled", "", 1)): - for file in f: - if '.lua' in file: - luafilecontents = "" - with open(os.path.join(r, file), "r") as f: - luafilecontents = f.read() - - os.remove(os.path.join(r, file)) - replaceLuaFiles(r, file, basefile, commentart, luafilecontents) - # create a .rpyc file in the non -compiled directory - open(os.path.join(backwordreplace(path, "-compiled", "", 1), ".rpyc"), "x").close() - # set cwd to not -compiled - os.chdir(backwordreplace(path, "-compiled", "", 1)) - print(info("Completed! You may need to modify the default.package.json or any other equivalent file to make it use the -compiled directory rather than the original.")) - elif confirm == "yes": - # note by CataclysmicDev -> i left this old code here just to make sure its stable on unix but you can delete it and see if the above code works on unix too - path = os.path.join(os.getcwd(), "src") - # check if cwd/default.project.json exists, if it does, then use that as the default project file - #if os.path.exists(os.getcwd()+"/default.project.json"): - # formatdefaultproj(os.getcwd()+"/default.project.json") - # rename directory to -compiled - os.rename(path_name, path_name+"-compiled") - # duplicate the directory, remove the -compiled from the end - shutil.copytree(path_name + "-compiled", path_name) - # now we have 2 identical directories, one with -compiled and one without. for the one without, go through every lua descendant file in the current directory and delete it and create a new file with the same name but .py - path = os.getcwd() - - for r, d, f in os.walk(backwordreplace(path, "-compiled", "", 1)): - for file in f: - if '.lua' in file: - luafilecontents = "" - with open(os.path.join(r, file), "r") as f: - luafilecontents = f.read() - - os.remove(os.path.join(r, file)) - - # create new file with same name but .py and write the lua file contents to it - open(os.path.join(r, file.replace(".lua", basefile)), "x").close() - # write the old file contents as a C++ comment - open(os.path.join(r, file.replace(".lua", basefile)), "w").write("\"\"\"\n"+luafilecontents+"\n\"\"\"") - - print(colortext.green("Converted "+os.path.join(r, file)+" to "+file.replace(".lua", basefile))) - # create a .rpyc file in the non -compiled directory - open(os.path.join(backwordreplace(path, "-compiled", "", 1), ".rpyc"), "x").close() - # set cwd to not -compiled - os.chdir(backwordreplace(path, "-compiled", "", 1)) - print(info("Completed! You may need to modify the default.package.json or any other equivalent file to make it use the -compiled directory rather than the original.")) - elif sys.argv[1] == "w": - print(colortext.magenta("Ready to compile ", os.path.join(os.path.dirname(os.path.realpath(__file__)))+" ...\n Type 'exit' to exit, Press enter to compile.")) - incli() - elif sys.argv[1] == "d": - print(colortext.magenta("Ready to compile ", os.path.join(os.path.dirname(os.path.realpath(__file__)))+" ...\n Type 'exit' to exit, Press enter to compile.")) - incli2() - - except IndexError: - print(error("Invalid amount of arguments!", "roblox-py")) - except KeyboardInterrupt: - print(colortext.red("Aborted!")) - sys.exit(0) - return w diff --git a/src/cmpopdesc.py b/src/cmpopdesc.py deleted file mode 100644 index 06bdae5..0000000 --- a/src/cmpopdesc.py +++ /dev/null @@ -1,27 +0,0 @@ -"""Compare operation description""" -import ast - - -class CompareOperationDesc: - """Compare operation description""" - - OPERATION = { - ast.Eq: "==", - ast.NotEq: "~=", - ast.Lt: "<", - ast.LtE: "<=", - ast.Gt: ">", - ast.GtE: ">=", - ast.In: { - "format": "operator_in({left}, {right})", - }, - ast.NotIn: { - "format": "not operator_in({left}, {right})", - }, - ast.Is: { - "format": "({left} == {right})", - }, - ast.IsNot: { - "format": "not({left} == {right})", - }, - } diff --git a/src/cnodevisitor.py b/src/cnodevisitor.py deleted file mode 100644 index 4d32baf..0000000 --- a/src/cnodevisitor.py +++ /dev/null @@ -1,125 +0,0 @@ -# THIS IS OLD DO NOT USE - - -import clang.cindex -from . import colortext - -# TODO: -## Add depth/indentation to the code -## Add all possible node types - -class NodeVisitor: - def __init__(self): - self.lua_code = "" - - def TRANSLATION_UNIT(self, node, depth=0): - # Visit child nodes of the translation unit - for child in node.get_children(): - self.visit_node(child) - - def FUNCTION_DECL(self, node, depth=0): - # Process function declarations - function_name = node.spelling - self.lua_code += f"function {function_name}()\n" - self.visit_compound_stmt(node.get_children()) - - def VAR_DECL(self, node, depth=0): - # Process variable declarations - variable_name = node.spelling - self.lua_code += f"local {variable_name}\n" - - for child in node.get_children(): - self.visit_node(child) - - def COMPOUND_STMT(self, node, depth=0): - if type(node) == list: - all = node - else: - all = node.get_children() - - for child in all: - self.visit_node(child, depth+1) - - self.lua_code += "end\n" - - for child in node.get_children(): - self.visit_node(child) - - def DECL_STMT(self, node, depth=0): - self.visit_node(node.get_children(), depth+1) - - for child in node.get_children(): - self.visit_node(child) - - def BINARY_OPERATOR(self, node, depth=0): - # Process binary operators - operator = node.spelling - self.lua_code += f"{operator} " - - for child in node.get_children(): - self.visit_node(child) - - def UNARY_OPERATOR(self, node, depth=0): - # Process unary operators - operator = node.spelling - self.lua_code += f"{operator} " - - for child in node.get_children(): - self.visit_node(child) - - def DECL_REF_EXPR(self, node, depth=0): - # Process variable references - variable_name = node.spelling - self.lua_code += f"{variable_name} " - - for child in node.get_children(): - self.visit_node(child) - - def INTEGER_LITERAL(self, node, depth=0): - # Process integer literals - value = node.spelling - self.lua_code += f"{value} " - - for child in node.get_children(): - self.visit_node(child) - - def FLOATING_LITERAL(self, node, depth=0): - # Process floating point literals - value = node.spelling - self.lua_code += f"{value} " - - for child in node.get_children(): - self.visit_node(child) - - def STRING_LITERAL(self, node, depth=0): - # Process string literals - value = node.spelling - self.lua_code += f"{value} " - - for child in node.get_children(): - self.visit_node(child) - - def CHARACTER_LITERAL(self, node, depth=0): - # Process character literals - value = node.spelling - self.lua_code += f"{value} " - - for child in node.get_children(): - self.visit_node(child) - - - - - def visit_node(self, node, depth=0): - # Dispatch visitation based on node kind - if self[node.kind] is not None: - self[node.kind](node, depth+1) - else: - print(colortext.yellow(f"Warning: No visitor method for {node.kind}")) - - # Recursively visit child nodes - for child in node.get_children(): - self.visit_node(child) - - def get_lua_code(self): - return self.lua_code \ No newline at end of file diff --git a/src/colortext.py b/src/colortext.py deleted file mode 100644 index 1f3b167..0000000 --- a/src/colortext.py +++ /dev/null @@ -1,37 +0,0 @@ -import sys - -def red(text, styles = []): - return color(text, 31, styles) -def green(text, styles = []): - return color(text, 32, styles) -def yellow(text, styles = []): - return color(text, 33, styles) -def blue(text, styles = []): - return color(text, 34, styles) -def magenta(text, styles = []): - return color(text, 35, styles) -def cyan(text, styles = []): - return color(text, 36, styles) -def white(text, styles = []): - return color(text, 37, styles) -def rainbow_text(text, end = ""): - colors = ['\033[31m', '\033[33m', '\033[32m', '\033[36m', '\033[34m', '\033[35m'] - i = 0 - for char in text: - print(colors[i % len(colors)] + char, end='') - i += 1 - print('\033[0m', end=end) -def nil(text=""): - return -def color(text, color, styles = []): - style = "" - for s in styles: - if s == "bold": - style += "1;" - elif s == "underline": - style += "4;" - elif s == "reverse": - style += "7;" - elif s == "concealed": - style += "8;" - return "\033["+style+str(color)+"m"+text+"\033[0m" \ No newline at end of file diff --git a/src/config.py b/src/config.py deleted file mode 100644 index 3ea718c..0000000 --- a/src/config.py +++ /dev/null @@ -1,30 +0,0 @@ -"""Python to lua translator config class""" -import sys -import yaml - -class Config: - """Translator config.""" - def __init__(self, filename=None): - self.data = { - "class": { - "return_at_the_end": False, - }, - } - - if filename is not None: - self.load(filename) - - def load(self, filename): - """Load config from the file""" - try: - with open(filename, "r") as stream: - data = yaml.load(stream) - self.data.update(data) - except FileNotFoundError: - pass # Use a default config if the file not found - except yaml.YAMLError as ex: - print(ex) - - def __getitem__(self, key): - """Get data values""" - return self.data[key] diff --git a/src/configmanager.py b/src/configmanager.py deleted file mode 100644 index d665b8f..0000000 --- a/src/configmanager.py +++ /dev/null @@ -1,51 +0,0 @@ -# CONFIG -# CFG will be in the same directory as this file -import pickle, os -cfgPath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "__communication__/cfg.pkl") -def getconfig(arg1, arg2, default="None"): - try: - # Load the config file if it exists, or create a new one if it doesn't - if os.path.exists(cfgPath): - with open(cfgPath, "rb") as f: - cfg = pickle.load(f) - else: - cfg = {} - - # Get the value from the config file, or use the default value if it doesn't exist - value = cfg.get(arg1, {}).get(arg2, default) - - # Update the config file with the default value if it doesn't exist - if arg1 not in cfg: - cfg[arg1] = {} - if arg2 not in cfg[arg1]: - cfg[arg1][arg2] = default - with open(cfgPath, "wb") as f: - pickle.dump(cfg, f) - - return value - except EOFError: - # The file is empty, write a {} to it - with open(cfgPath, "wb") as f: - pickle.dump({}, f) - return getconfig(arg1, arg2, default) - -def setconfig(arg1, arg2, value, ignore=None): - try: - # Load the config file if it exists, or create a new one if it doesn't - if os.path.exists(cfgPath): - with open(cfgPath, "rb") as f: - cfg = pickle.load(f) - else: - cfg = {} - - # Set the value in the config file and save it - if arg1 not in cfg: - cfg[arg1] = {} - cfg[arg1][arg2] = value - with open(cfgPath, "wb") as f: - pickle.dump(cfg, f) - except EOFError: - # The file is empty, write a {} to it - with open(cfgPath, "wb") as f: - pickle.dump({}, f) - return setconfig(arg1, arg2, value, ignore=ignore) diff --git a/src/context.py b/src/context.py deleted file mode 100644 index 49b1efb..0000000 --- a/src/context.py +++ /dev/null @@ -1,39 +0,0 @@ -"""Class to store the python code context""" -import sys -import os -if not (os.path.dirname(os.path.abspath(__file__)).startswith(sys.path[-1])): - from symbolsstack import SymbolsStack - from tokenendmode import TokenEndMode -else: - from .symbolsstack import SymbolsStack - from .tokenendmode import TokenEndMode - - -class Context: - """Class to store the python code context""" - def __init__(self, values=None): - values = values if values is not None else { - "token_end_mode": TokenEndMode.LINE_FEED, - "class_name": "", - "locals": SymbolsStack(), - "globals": SymbolsStack(), # Not working yet - "loop_label_name": "", - "docstring": False, - } - - self.ctx_stack = [values] - - def last(self): - """Return actual context state""" - return self.ctx_stack[-1] - - def push(self, values): - """Push new context state with new values""" - value = self.ctx_stack[-1].copy() - value.update(values) - self.ctx_stack.append(value) - - def pop(self): - """Pop last context state""" - assert len(self.ctx_stack) > 1, "Pop context failed. This is a last context in the stack." - return self.ctx_stack.pop() diff --git a/src/cpAST.py b/src/cpAST.py deleted file mode 100644 index 08696b3..0000000 --- a/src/cpAST.py +++ /dev/null @@ -1,18 +0,0 @@ -# THIS IS OLD DO NOT USE - -""" -Dedicated to parsing C/C++ code into ASTs using libclang. -Pass the code on to the C(++) translator. -""" - -import clang.cindex as clang - -def get_ast(file_path): - index = clang.Index.create() - translation_unit = index.parse(file_path) - return translation_unit.cursor - -def print_ast(node, depth=0): - print(' ' * depth + str(node.kind) + ' : ' + node.spelling) - for child in node.get_children(): - print_ast(child, depth + 1) diff --git a/src2/cppast.cobalt b/src/cppast.cobalt similarity index 100% rename from src2/cppast.cobalt rename to src/cppast.cobalt diff --git a/src/ctranslator.py b/src/ctranslator.py deleted file mode 100644 index b13f638..0000000 --- a/src/ctranslator.py +++ /dev/null @@ -1,18 +0,0 @@ -# THIS IS OLD DO NOT USE - -from .cpAST import get_ast, print_ast -from .cnodevisitor import NodeVisitor - -def readast(node, depth, calc): - calc(node, depth) - for child in node.get_children(): - print_ast(child, depth + 1, calc) - -def translate(file_path): - ast = get_ast(file_path) - print_ast(ast) - - newNodeVisitor = NodeVisitor() - newNodeVisitor.visit_node(ast) - - print(newNodeVisitor.get_lua_code()) \ No newline at end of file diff --git a/src/errormanager.py b/src/errormanager.py deleted file mode 100644 index 4d5bd4a..0000000 --- a/src/errormanager.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Error system for robloxpyc""" -import sys -import os -if not (os.path.dirname(os.path.abspath(__file__)).startswith(sys.path[-1])): - import colortext, configmanager -else: - from . import colortext, configmanager -import subprocess, traceback - -def candcpperror(): - print(warn("C and C++ are not supported in this build, coming soon! \n\n contributions on github will be greatly appreciated!")) -def error(errormessage, source=""): - if source != "": - source = colortext.white(" ("+source+") ") - if configmanager.getconfig("general", "goofy", False): - subprocess.call(["say", errormessage]) - return(colortext.red("error ", ["bold"])+source+errormessage) -def warn(warnmessage, source=""): - if source != "": - source = colortext.white(" ("+source+") ") - return(colortext.yellow("warning ", ["bold"])+source+warnmessage) -def info(infomessage, source=""): - if source != "": - source = colortext.white(" ("+source+") ") - return(colortext.blue("info ", ["bold"])+source+infomessage) -def debug(infomessage): - if configmanager.getconfig("general", "traceback", False): - print(colortext.blue("debug ", ["bold"])+infomessage) - print(traceback.format_exc()) -def decreapted(source=""): - if source != "": - source = colortext.white(" ("+source+") ") - print(colortext.yellow("decreapted ", ["bold"])+source+"This feature is decreapted and will be removed in a future version of roblox-pyc") \ No newline at end of file diff --git a/src2/generator.cobalt b/src/generator.cobalt similarity index 100% rename from src2/generator.cobalt rename to src/generator.cobalt diff --git a/src/header.py b/src/header.py deleted file mode 100644 index 442e0ed..0000000 --- a/src/header.py +++ /dev/null @@ -1,49 +0,0 @@ -headertemplate = """--/ Compiled using roblox-pyc | Python compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local py, import, builtin = _G.pyc(script).py - -{} ------------------------------------------------------------------------------ -""" -lunarheadertemplate = """--/ Compiled using roblox-pyc | Lunar compiler \-- - - ------------------------------------- BUILT IN ------------------------------- -repeat task.wait() until _G.pyc -local import, builtin = _G.pyc(script).lunar - -{} ------------------------------------------------------------------------------ -""" - -pyfooter = """ - ------------------------------------- END ------------------------------------ -if script:IsA("ModuleScript") then - return getfenv() -else - repeat task.wait() until _G.pyc - _G.pyc.libs[script] = getfenv() -end ------------------------------------- END ------------------------------------ - -""" - -def header(functions): - code = "" - for i in functions: - code += "local "+i+" = builtin."+i+"\n" - - #print(code) - return headertemplate.format(code) - -def lunarheader(functions): - code = "" - for i in functions: - code += "local "+i+" = builtin."+i+"\n" - - #print(code) - return lunarheadertemplate.format(code) \ No newline at end of file diff --git a/src2/init.cobalt b/src/init.cobalt similarity index 100% rename from src2/init.cobalt rename to src/init.cobalt diff --git a/src/installationmanager.py b/src/installationmanager.py deleted file mode 100644 index a82fc3d..0000000 --- a/src/installationmanager.py +++ /dev/null @@ -1,86 +0,0 @@ -"""Manages updating and installing dependencies""" - -# PYPI -from packaging import version - -# FILES -import sys -import os -if not (os.path.dirname(os.path.abspath(__file__)).startswith(sys.path[-1])): - from errormanager import * -else: - from .errormanager import * - -# BUILTIN -import subprocess,requests,pkg_resources, os - -def check_luarocks(): - try: - subprocess.call(["luarocks", "--version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL) - return True - except FileNotFoundError: - return False - -def check_moonscript(): - try: - subprocess.call(["moonc", "--version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL) - return True - except FileNotFoundError: - return False - -def install_luarocks(): - print("Installing LuaRocks...") - subprocess.call(["apt-get", "install", "-y", "luarocks"]) - -def install_moonscript(): - print("Installing MoonScript...") - subprocess.call(["luarocks", "install", "moonscript", "--dev"]) - -def checkboth(): - if check_luarocks() == False: - print(warn("LuaRocks is not installed, installing...")) - install_luarocks() - if check_moonscript() == False: - print(warn("MoonScript is not installed, installing...")) - install_moonscript() - -def get_latest_version(): - url = f"https://pypi.org/pypi/roblox-pyc/json" - response = requests.get(url) - data = response.json() - return data["info"]["version"] - -def check_for_updates(): - current_version = pkg_resources.get_distribution("roblox-pyc").version - latest_version = get_latest_version() - if version.parse(latest_version) > version.parse(current_version): - print(info(f"Update available to {latest_version}, you are currently using {current_version}")) - choice = input("\t\tDo you want to update? (yes/no): ").lower() - if choice == "yes": - # Add the pip upgrade command here. - # Get cfg.pkl - script_dir = os.path.dirname(os.path.realpath(__file__)) - returnval = "" - try: - with open(os.path.join(script_dir, "__communication__/cfg.pkl"), "rb") as file: - returnval = file.read() - except: - print(error("Failed to safe-update, data is corrupted. Would you like to force-update, you may lose configuration data.", "auto-updater")) - choice = input("\t\tDo you want to force-update? (yes/no): ").lower() - if not choice == "yes": - sys.exit() - subprocess.run(["pip", "install", f"roblox-pyc=={latest_version}"]) - with open(os.path.join(script_dir, "__communication__/cfg.pkl"), "wb") as file: - file.write(returnval) - - -def check_npm(): - try: - subprocess.call(["npm", "--version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL) - return True - except FileNotFoundError: - return False -def install_npm(): - print("Installing NPM...") - subprocess.call(["apt-get", "install", "-y", "npm"]) - diff --git a/src/loader.py b/src/loader.py deleted file mode 100644 index da469bd..0000000 --- a/src/loader.py +++ /dev/null @@ -1,32 +0,0 @@ -# LOADING -import sys -import os -if not (os.path.dirname(os.path.abspath(__file__)).startswith(sys.path[-1])): - import colortext -else: - from . import colortext - -from time import sleep -from tqdm import tqdm - -class loader: - self = {} - def __init__(self, max): - print("\n\n") - self.max = max - self.current = 0 - self.tqdm = tqdm(total=max) - - def yielduntil(self): - global count - while self.max != self.current: - sleep(.5) - self.tqdm.update(self.max-self.current ) - self.tqdm.close() - def update(self, amount): - self.tqdm.update(amount) - def error(self): - self.tqdm.write(colortext.red("paused!", ["bold"])) - self.current = self.max - self.tqdm.close() - \ No newline at end of file diff --git a/src/loopcounter.py b/src/loopcounter.py deleted file mode 100644 index 9849d2d..0000000 --- a/src/loopcounter.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Label counter for the loops continue""" - -class LoopCounter: - """Loop counter""" - COUNTER = 0 - - @staticmethod - def get_next(): - """Return next loop continue label name""" - LoopCounter.COUNTER += 1 - return "continue {}".format(LoopCounter.COUNTER) diff --git a/src/luainit.py b/src/luainit.py deleted file mode 100644 index ff15a59..0000000 --- a/src/luainit.py +++ /dev/null @@ -1,160 +0,0 @@ -import os -import pip -import sys -import os -if not (os.path.dirname(os.path.abspath(__file__)).startswith(sys.path[-1])): - import colortext as colortext -else: - from . import colortext as colortext - -initcode = "" - -try: - # check for a luainitlua.lua file from the same directory as this file - with open(os.path.join(os.path.dirname(__file__), "luainitlua.lua")) as f: - initcode = f.read() -except FileNotFoundError: - print(colortext.yellow("warning", ["bold"])+" Due to a bug, lib will not work, please report this issue to the github repo, discord server, or the devforum post\nthanks!") -allfunctions = "safeadd, list, dict, python, staticmethod, classsmethod, class, range, __name__, len, abs, str, int, sum, max, min, reversed, split, round, all, any, ord, char, callable, zip, float, format, hex, id, map, bool, divmod, slice, operator_in, asynchronousfunction, match, anext, ascii, dir, getattr, globals, hasattr, input, isinstance, issubclass, iter, locals, oct, open, ord, pow, eval, exec, filter, frozenset, aiter, bin, complex, delattr, enumerate, breakpoint, bytearray, bytes, compile, help, memoryview, repr, sorted, vars, __import_, formatmod" -lunarfunctions = "" -robloxfunctions = """["assert"] = "function", - ["error"] = "function", - ["getfenv"] = "function", - ["getmetatable"] = "function", - ["ipairs"] = "function", - ["loadstring"] = "function", - ["newproxy"] = "function", - ["next"] = "function", - ["pairs"] = "function", - ["pcall"] = "function", - ["print"] = "function", - ["rawequal"] = "function", - ["rawget"] = "function", - ["rawlen"] = "function", - ["rawset"] = "function", - ["select"] = "function", - ["setfenv"] = "function", - ["setmetatable"] = "function", - ["tonumber"] = "function", - ["tostring"] = "function", - ["unpack"] = "function", - ["xpcall"] = "function", - ["collectgarbage"] = "function", - ["_G"] = "table", - ["_VERSION"] = "string", - ["bit32"] = "table", - ["coroutine"] = "table", - ["debug"] = "table", - ["math"] = "table", - ["os"] = "table", - ["string"] = "table", - ["table"] = "table", - ["utf8"] = "table", - ["DebuggerManager"] = "function", - ["delay"] = "function", - ["gcinfo"] = "function", - ["PluginManager"] = "function", - ["require"] = "function", - ["settings"] = "function", - ["spawn"] = "function", - ["tick"] = "function", - ["time"] = "function", - ["UserSettings"] = "function", - ["wait"] = "function", - ["warn"] = "function", - ["Delay"] = "function", - ["ElapsedTime"] = "function", - ["elapsedTime"] = "function", - ["printidentity"] = "function", - ["Spawn"] = "function", - ["Stats"] = "function", - ["stats"] = "function", - ["Version"] = "function", - ["version"] = "function", - ["Wait"] = "function", - ["ypcall"] = "function", - ["game"] = "Instance", - ["plugin"] = "Instance", - ["script"] = "Instance", - ["shared"] = "Instance", - ["workspace"] = "Instance", - ["Game"] = "Instance", - ["Workspace"] = "Instance", - ["Axes"] = "table", - ["BrickColor"] = "table", - ["CatalogSearchParams"] = "table", - ["CFrame"] = "table", - ["Color3"] = "table", - ["ColorSequence"] = "table", - ["ColorSequenceKeypoint"] = "table", - ["DateTime"] = "table", - ["DockWidgetPluginGuiInfo"] = "table", - ["Enum"] = "table", - ["Faces"] = "table", - ["FloatCurveKey"] = "table", - ["Font"] = "table", - ["Instance"] = "table", - ["NumberRange"] = "table", - ["NumberSequence"] = "table", - ["NumberSequenceKeypoint"] = "table", - ["OverlapParams"] = "table", - ["PathWaypoint"] = "table", - ["PhysicalProperties"] = "table", - ["Random"] = "table", - ["Ray"] = "table", - ["RaycastParams"] = "table", - ["Rect"] = "table", - ["Region3"] = "table", - ["Region3int16"] = "table", - ["RotationCurveKey"] = "table", - ["SharedTable"] = "table", - ["task"] = "table", - ["TweenInfo"] = "table", - ["UDim"] = "table", - ["UDim2"] = "table", - ["Vector2"] = "table", - ["Vector2int16"] = "table", - ["Vector3"] = "table", - ["Vector3int16"] = "table", -""" -allfunctions = allfunctions.split(", ") -lunarfunctions = lunarfunctions.split(", ") - -# Parse robloxfunctions to something that looks like allfunctions -robloxfunctions = robloxfunctions.replace("\n", "").replace("\t", "").replace(" ", "").replace('"', "").replace("'", "").replace("{", "").replace("}", "").replace("[", "").replace("]", "").split(",") -for i in range(len(robloxfunctions)): - item = robloxfunctions[i] - if item == "": - # Remove from list - robloxfunctions.pop(i) - else: - # Split by = and the first item is the name - robloxfunctions[i] = item.split("=")[0] - -def generatewithlibraries (libs): - # Every item in libs is a table with 3 values - # name (what to download by) - # data (source code) - # var (what variable is the source code stored in) - - # The initcode has 2 comments, --{SOURCECODEHERE}-- and --{ITEMSHERE}--. Replace SOURCECODEHERE with all of the source codes seprated by newlines - # and for ITEMSHERE, "" = - - currentcode = initcode - sources = [] - items = {} - - for i in range(len(libs)): - sources.append(libs[i]["data"]) - items[libs[i]["name"]] = libs[i]["var"] - - sourcestext = "\n".join(sources) - itemstext = "" - - for i in range(len(items)): - itemstext += f'["{list(items.keys())[i]}"] = {list(items.values())[i]}\n' - - currentcode = currentcode.replace("--{SOURCECODEGOESHERE}--", sourcestext) - currentcode = currentcode.replace("--{ITEMSGOHERE}--", itemstext) - - return currentcode diff --git a/src/luainitlua.lua b/src/luainitlua.lua deleted file mode 100644 index 1e0e79c..0000000 --- a/src/luainitlua.lua +++ /dev/null @@ -1,1240 +0,0 @@ ---// AsynchronousAI @Dev98799 \\-- ----------------------------------------------------------------------------------------- --- stdlib used to hold built in functions wrappers and libraries -- ----------------------------------------------------------------------------------------- - --- stdlib Version 2.5.9 -- - -local module = { } - --- Language used for gamewrapper (from Highlighter by boatbomber) -local language = { - -- Luau Functions - ["assert"] = "function", - ["error"] = "function", - ["getfenv"] = "function", - ["getmetatable"] = "function", - ["ipairs"] = "function", - ["loadstring"] = "function", - ["newproxy"] = "function", - ["next"] = "function", - ["pairs"] = "function", - ["pcall"] = "function", - ["print"] = "function", - ["rawequal"] = "function", - ["rawget"] = "function", - ["rawlen"] = "function", - ["rawset"] = "function", - ["select"] = "function", - ["setfenv"] = "function", - ["setmetatable"] = "function", - ["tonumber"] = "function", - ["tostring"] = "function", - ["unpack"] = "function", - ["xpcall"] = "function", - - -- Luau Functions (Deprecated) - ["collectgarbage"] = "function", - - -- Luau Variables - ["_G"] = "table", - ["_VERSION"] = "string", - - -- Luau Tables - ["bit32"] = "table", - ["coroutine"] = "table", - ["debug"] = "table", - ["math"] = "table", - ["os"] = "table", - ["string"] = "table", - ["table"] = "table", - ["utf8"] = "table", - - -- Roblox Functions - ["DebuggerManager"] = "function", - ["delay"] = "function", - ["gcinfo"] = "function", - ["PluginManager"] = "function", - ["require"] = "function", - ["settings"] = "function", - ["spawn"] = "function", - ["tick"] = "function", - ["time"] = "function", - ["UserSettings"] = "function", - ["wait"] = "function", - ["warn"] = "function", - - -- Roblox Functions (Deprecated) - ["Delay"] = "function", - ["ElapsedTime"] = "function", - ["elapsedTime"] = "function", - ["printidentity"] = "function", - ["Spawn"] = "function", - ["Stats"] = "function", - ["stats"] = "function", - ["Version"] = "function", - ["version"] = "function", - ["Wait"] = "function", - ["ypcall"] = "function", - - -- Roblox Variables - ["game"] = "Instance", - ["plugin"] = "Instance", - ["script"] = "Instance", - ["shared"] = "Instance", - ["workspace"] = "Instance", - - -- Roblox Variables (Deprecated) - ["Game"] = "Instance", - ["Workspace"] = "Instance", - - -- Roblox Tables - ["Axes"] = "table", - ["BrickColor"] = "table", - ["CatalogSearchParams"] = "table", - ["CFrame"] = "table", - ["Color3"] = "table", - ["ColorSequence"] = "table", - ["ColorSequenceKeypoint"] = "table", - ["DateTime"] = "table", - ["DockWidgetPluginGuiInfo"] = "table", - ["Enum"] = "table", - ["Faces"] = "table", - ["FloatCurveKey"] = "table", - ["Font"] = "table", - ["Instance"] = "table", - ["NumberRange"] = "table", - ["NumberSequence"] = "table", - ["NumberSequenceKeypoint"] = "table", - ["OverlapParams"] = "table", - ["PathWaypoint"] = "table", - ["PhysicalProperties"] = "table", - ["Random"] = "table", - ["Ray"] = "table", - ["RaycastParams"] = "table", - ["Rect"] = "table", - ["Region3"] = "table", - ["Region3int16"] = "table", - ["RotationCurveKey"] = "table", - ["SharedTable"] = "table", - ["task"] = "table", - ["TweenInfo"] = "table", - ["UDim"] = "table", - ["UDim2"] = "table", - ["Vector2"] = "table", - ["Vector2int16"] = "table", - ["Vector3"] = "table", - ["Vector3int16"] = "table", -} -function backwordreplace(s, old, new, occurrence) -- Lua implementation of the python function in robloxpy.py line ~600 - local li = {} - local i = 1 - while true do - local j = string.find(s, old, i, true) - if not j then - break - end - table.insert(li, string.sub(s, i, j - 1)) - i = j + #old - if #li == occurrence then - break - end - end - table.insert(li, string.sub(s, i)) - return table.concat(li, new) -end -local function parse_path(path, start_obj) - local obj = start_obj or game - local parts = string.split(path, "/") - for i, part in ipairs(parts) do - if part == "" then - obj = game - elseif part == "~" then - obj = game - elseif part == ".." then - obj = obj.Parent - else - if not obj:FindFirstChild(part) then - error("Object not found: " .. part .." in "..obj.Name.."\n\n\tDo not add .json, .txt, etc. to the end of file names, this version doesnt support them.") - return nil - end - obj = obj:FindFirstChild(part) - end - end - return obj -end - -local slicefun = function (seq, start, stop, step) - local sliced = {} - local len = #seq - start = start or 1 - stop = stop or len - step = step or 1 - if start < 0 then - start = len + start + 1 - end - if stop < 0 then - stop = len + stop + 1 - end - for i = start, stop - 1, step do - table.insert(sliced, seq[i]) - end - return sliced -end -function endswith(s, suffix) -- like string.endswith in python - return string.sub(s, -string.len(suffix)) == suffix -end -local wrappercache = setmetatable({}, {__mode = "k"}) -local wrap, unwrap -unwrap = function(wrapped) - if type(wrapped) == "table" then - local real = {} - for k,v in next,wrapped do - real[k] = unwrap(v) - end - return real - else - local real = wrappercache[wrapped] - if real == nil then - return wrapped - end - return real - end -end -wrap = function(real, functions) - functions = functions or {} - for w,r in next,wrappercache do - if r == real then - return w - end - end - - if type(real) == "userdata" then - local fake = newproxy(true) - local meta = getmetatable(fake) - - meta.__index = function(s,k) - - if table.find(functions, k) then - return functions[k] - end - if real[k] then - if typeof(real[k]) == "RBXScriptSignal" then - local newSignal = {} - setmetatable(newSignal, { - __call = function(func) - real:Connect(func) - end, - __index = function(index) return real[index] end - }) - return newSignal - elseif type(real[k]) == "function" then - return function(...) - local returnval - local items = unpack(table.pack(...)) - local s, e = pcall(function() - returnval = wrap(real[k](items)) - end) - if (not s) then - local rawcall = real[k](real, items) - returnval = wrap(rawcall) - elseif not s then - error(e) - end - return returnval - end - end - return wrap(real[k], functions) - end - end - - meta.__newindex = function(s,k,v) - real[k] = v - end - - meta.__tostring = function(s) - return tostring(real) - end - - wrappercache[fake] = real - return fake - - elseif type(real) == "function" then - return function(special, ...) - if special == selfcval then - return wrap(real(real, ...)) - end - return wrap(real(special, ...)) - end - - elseif type(real) == "table" then - local fake = {} - for k,v in next,real do - fake[k] = wrap(v) - end - return fake - - else - return real - end -end - -local function set_metatable(var, mt) - local var_type = typeof(var) - if var_type == "Instance" then - var:SetAttribute("__metatable", mt) - elseif var_type == "table" then - setmetatable(var, mt) - end - return var -end -local function set_fun_meta(f, rmt) - return function() - local returnval = f() - if returnval == nil then return end - if type(returnval) == "function" then - return set_fun_meta(returnval, rmt) - else - set_metatable(rmt) - end - end -end -local gtype -if not typeof then - gtype = function(obj) - local type = type(obj) - if type == "table" then - if obj._is_list then - return "list" - end - if obj._is_dict then - return "dict" - end - end - return type - end -else - gtype = typeof -end -local typeof = gtype - - - -function list(t) - local result = {} - - result._is_list = true - - result._data = {} - for _, v in ipairs(t) do - table.insert(result._data, v) - end - - local methods = {} - - methods.append = function(value) - table.insert(result._data, value) - end - - methods.extend = function(iterable) - for value in iterable do - table.insert(result._data, value) - end - end - - methods.insert = function(index, value) - table.insert(result._data, index, value) - end - - methods.remove = function(value) - for i, v in ipairs(result._data) do - if value == v then - table.remove(result._data, i) - break - end - end - end - - methods.pop = function(index) - index = index or #result._data - local value = result._data[index] - table.remove(result._data, index) - return value - end - - methods.clear = function() - result._data = {} - end - - methods.index = function(value, start, end_) - start = start or 1 - end_ = end_ or #result._data - - for i = start, end_, 1 do - if result._data[i] == value then - return i - end - end - - return nil - end - - methods.count = function(value) - local cnt = 0 - for _, v in ipairs(result._data) do - if v == value then - cnt = cnt + 1 - end - end - - return cnt - end - - methods.sort = function(key, reverse) - key = key or nil - reverse = reverse or false - - table.sort(result._data, function(a, b) - if reverse then - return a < b - end - - return a > b - end) - end - - methods.reverse = function() - local new_data = {} - for i = #result._data, 1, -1 do - table.insert(new_data, result._data[i]) - end - - result._data = new_data - end - - methods.copy = function() - return list(result._data) - end - - local iterator_index = nil - - setmetatable(result, { - __index = function(self, index) - if typeof(index) == "number" then - if index < 0 then - index = #result._data + index - end - return rawget(result._data, index + 1) - end - return methods[index] - end, - __newindex = function(self, index, value) - result._data[index] = value - end, - __call = function(self, _, idx) - if idx == nil and iterator_index ~= nil then - iterator_index = nil - end - - local v = nil - iterator_index, v = next(result._data, iterator_index) - - return v - end, - }) - - return result -end -function dict(t) - local result = {} - - result._is_dict = true - - result._data = {} - for k, v in pairs(t) do - result._data[k] = v - end - - local methods = {} - - local key_index = nil - - methods.clear = function() - result._data = {} - end - - methods.copy = function() - return dict(result._data) - end - - methods.get = function(key, default) - default = default or nil - if result._data[key] == nil then - return default - end - - return result._data[key] - end - - methods.items = function() - return pairs(result._data) - end - - methods.keys = function() - return function(self, idx, _) - if idx == nil and key_index ~= nil then - key_index = nil - end - - key_index, _ = next(result._data, key_index) - return key_index - end - end - - methods.pop = function(key, default) - default = default or nil - if result._data[key] ~= nil then - local value = result._data[key] - result._data[key] = nil - return key, value - end - - return key, default - end - - methods.popitem = function() - local key, value = next(result._data) - if key ~= nil then - result._data[key] = nil - end - - return key, value - end - - methods.setdefault = function(key, default) - if result._data[key] == nil then - result._data[key] = default - end - - return result._data[key] - end - - methods.update = function(t) - assert(t._is_dict) - - for k, v in t.items() do - result._data[k] = v - end - end - - methods.values = function() - return function(self, idx, _) - if idx == nil and key_index ~= nil then - key_index = nil - end - - key_index, value = next(result._data, key_index) - return value - end - end - - setmetatable(result, { - __index = function(self, index) - if typeof(index) == "string" then - -- If it starts with SLICE! then it is a slice, get the start, stop, and step values. Sometimes the 3rd value is not there, so we need to check for that - if string.sub(index, 1, 6) == "SLICE!" then - local start, stop, step = string.match(index, "SLICE!%((%d+), (%d+), (%d+)%)") - if (not stop) and (not step) and start then -- 1 value - start = string.match(index, "SLICE!%((%d+), (%d+)%)") - step = 1 - stop = -1 - elseif not step then -- 2 values - start, stop = string.match(index, "SLICE!%((%d+), (%d+)%)") - step = 1 - end - return slicefun(self, tonumber(start), tonumber(stop), tonumber(step)) - end - end - if result._data[index] ~= nil then - return result._data[index] - end - return methods[index] - end, - __newindex = function(self, index, value) - result._data[index] = value - end, - __call = function(self, _, idx) - if idx == nil and key_index ~= nil then - key_index = nil - end - - key_index, _ = next(result._data, key_index) - - return key_index - end, - }) - - return result -end ---{SOURCECODEGOESHERE}-- - -local libraries = { - ["example"] = function() print("Example library!") end, - --{ITEMSGOHERE}-- -} -local dependenciesfolder = script.Parent -local pythonBuiltIn = function(inScript) -- python built in - local items = { - list = list, dict = dict, -- class meta - staticmethod = function(old_fun) -- staticmethod - local wrapper = function(first, ...) - return old_fun(...) - end - - return wrapper - end, - classmethod = function(old_fun) -- classmethod - local wrapper = function(first, ...) - return old_fun(first, ...) - end - - return wrapper - end, - class = function(class_init, bases) -- class - bases = bases or {} - - local c = {} - - for _, base in ipairs(bases) do - for k, v in pairs(base) do - c[k] = v - end - end - - c._bases = bases - - c = class_init(c) - - local mt = getmetatable(c) or {} - mt.__call = function(_, ...) - local object = {} - - setmetatable(object, { - __index = function(tbl, idx) - local method = c[idx] - if typeof(method) == "function" then - return function(...) - return c[idx](object, ...) - end - end - - return method - end, - }) - - if typeof(object.__init__) == "function" then - object.__init__(...) - end - - return object - end - - setmetatable(c, mt) - - return c - end, - range = function(s, e) -- range() - local tb = {} - local a = 0 - local b = 0 - if not e then a=1 else a=s end - if not e then b=s else b=e end - for i = a, b do - tb[#tb+1] = i - end - return tb - end, - __name__ = if inScript:IsA("BaseScript") then "__main__" else inScript.Name , - len = function(x) return #x end, -- len() - abs = math.abs, -- abs() - str = tostring, -- str() - int = tonumber, -- int() - - sum = function(tbl) --sum() - local total = 0 - for _, v in ipairs(tbl) do - total = total + v - end - return total - end, - - -- Maximum value in a table - max = function(tbl) --max() - local maxValue = -math.huge - for _, v in ipairs(tbl) do - if v > maxValue then - maxValue = v - end - end - return maxValue - end, - - -- Minimum value in a table - min = function(tbl) --min() - local minValue = math.huge - for _, v in ipairs(tbl) do - if v < minValue then - minValue = v - end - end - return minValue - end, - - -- Reversed version of a table or string - reversed = function(seq) -- reversed() - local reversedSeq = {} - local length = #seq - for i = length, 1, -1 do - reversedSeq[length - i + 1] = seq[i] - end - return reversedSeq - end, - - -- Splitting a string into a table of substrings - split = function(str, sep) -- split - local substrings = {} - local pattern = string.format("([^%s]+)",sep or "%s") - for substring in string.gmatch(str, pattern) do - table.insert(substrings, substring) - end - return substrings - end, - - round = math.round, -- round() - - all = function (iter) -- all() - for i, v in iter do if not v then return false end end - - return true - end, - - any = function (iter) -- any() - for i, v in iter do - if v then return true end - end - return false - end, - - ord = string.byte, -- ord - chr = string.char, -- chr - - callable = function(fun) -- callable() - if rawget(fun) ~= fun then warn("At the momement Roblox.py's function callable() does not fully support metatables.") end - return typeof(rawget(fun)) == "function" - end, - float = tonumber, -- float() - super = function() - error("roblox-pyc does not has a Lua implementation of the function `super`. Use `self` instead") - end, - format = function(format, ...) -- format - local args = {...} - local num_args = select("#", ...) - - local formatted_string = string.gsub(format, "{(%d+)}", function(index) - index = tonumber(index) - if index >= 1 and index <= num_args then - return tostring(args[index]) - else - return "{" .. index .. "}" - end - end) - - return formatted_string - end, - - hex = function (value) -- hex - return string.format("%x", value) - end, - - id = function (obj) -- id - return print(tostring({obj}):gsub("table: ", ""):split(" ")[1]) - end, - map = function (func, ...) --map - local args = {...} - local result = {} - local num_args = select("#", ...) - - local shortest_length = math.huge - for i = 1, num_args do - local arg = args[i] - local arg_length = #arg - if arg_length < shortest_length then - shortest_length = arg_length - end - end - - for i = 1, shortest_length do - local mapped_args = {} - for j = 1, num_args do - local arg = args[j] - table.insert(mapped_args, arg[i]) - end - table.insert(result, func(unpack(mapped_args))) - end - - return result - end, - bool = function(x) -- bool - if x == false or x == nil or x == 0 then - return false - end - - if typeof(x) == "table" then - if x._is_list or x._is_dict then - return next(x._data) ~= nil - end - end - - return true - end, - divmod = function(a, b) -- divmod - local res = { math.floor(a / b), math.fmod(a, b) } - return unpack(res) - end, - slice = slicefun, - operator_in = function (item, items) -- operator_in() - if type(items) == "table" then - for v in items do - if v == item then - return true - end - end - elseif type(items) == "string" and type(item) == "string" then - return string.find(items, item, 1, true) ~= nil - end - - return false - end, - asynchronousfunction = function(func) -- asynchronousfunction - return function(...) - local all = {...} - coroutine.wrap(function() - func(unpack(all)) - end)() - end - end, - match = function(value, values) -- match - if values[value] then - return values[value]() - elseif values["default"] then - return values["default"]() - end - end, - - anext = function (iterator) -- anext - local status, value = pcall(iterator) - if status then - return value - end - end, - - ascii = function (obj) -- ascii - return string.format("%q", tostring(obj)) - end, - dir = function (obj) -- dir - local result = {} - for key, _ in pairs(obj) do - table.insert(result, key) - end - return result - end, - getattr = function (obj, name, default) -- getattr - local value = obj[name] - if value == nil then - return default - end - return value - end, - globals = function () -- globals - return _G - end, - hasattr = function (obj, name) --hasattr - return obj[name] ~= nil - end, - input = function (prompt) -- input - if not io then error("io is not enabled") end - io.write(prompt) - return io.read() - end, - isinstance = function (obj, class) -- isinstance - return type(obj) == class - end, - issubclass = function (cls, classinfo) -- issubclass - local mt = getmetatable(cls) - while mt do - if mt.__index == classinfo then - return true - end - mt = getmetatable(mt.__index) - end - return false - end, - iter = function (obj) -- iter - if type(obj) == "table" and obj.__iter__ ~= nil then - return obj.__iter__ - end - return nil - end, - locals = function () -- locals - return _G - end, - -- oct() - oct = function (num) --oct - return string.format("%o", num) - end, - - -- open() - open = function (path, mode) - local obj = parse_path(path, inScript) - if obj:IsA("ModuleScript") then - local source = fromFileImport(obj) - if mode == "r" then - return source.Contents or error("File is not in correct roblox-pyc format, cannot be read.") - elseif mode == "w" then - return { - write = function(self, data) - obj:SetSource(data) - end, - close = function(self) - --obj:SaveSource() - return -- do nothing - end - } - else - error("Invalid mode, at the moment only r and w are supported: " .. mode) - end - else - error("Object is not a LuaSourceContainer: " .. path) - end - end, - - - -- pow() - pow = function (base, exponent, modulo) --pow - if modulo ~= nil then - return math.pow(base, exponent) % modulo - else - return base ^ exponent - end - end, - - -- eval() - eval = function (expr, env) - return loadstring(expr)() - end, - - -- exec() - exec = loadstring, - - -- filter() - filter = function (predicate, iterable) - local result = {} - for _, value in ipairs(iterable) do - if predicate(value) then - table.insert(result, value) - end - end - return result - end, - - -- frozenset() - frozenset = function (...) - local elements = {...} - local frozenSet = {} - for _, element in ipairs(elements) do - frozenSet[element] = true - end - return frozenSet - end, - -- aiter() - aiter = function (iterable) -- aiter - return pairs(iterable) - end, - - -- bin() - bin = function(num: number) - local bits = {} - repeat - table.insert(bits, 1, num % 2) - num = math.floor(num / 2) - until num == 0 - return "0b" .. table.concat(bits) - end, - -- complex() - complex = function (real, imag) -- complex - return { real = real, imag = imag } - end, - - -- delattr() - deltaattr = function (object, attribute) -- delattr - object[attribute] = nil - end, - - -- enumerate() - enumerate = function (iterable) -- enumerate - local i = 0 - return function() - i = i + 1 - local value = iterable[i] - if value ~= nil then - return i, value - end - end - end, - - -- breakpoint() - breakpoint = function () -- breakpoint - debug.traceback("Breakpoint hit!") - - end, - - -- bytearray() - bytearray = function (arg) -- bytearray - if type(arg) == "string" then - local bytes = {} - for i = 1, #arg do - table.insert(bytes, string.byte(arg, i)) - end - return bytes - elseif type(arg) == "number" then - local bytes = {} - while arg > 0 do - table.insert(bytes, 1, arg % 256) - arg = math.floor(arg / 256) - end - return bytes - elseif type(arg) == "table" then - return arg -- Assuming it's already a bytearray table - else - error("Invalid argument type for bytearray()") - end - end, - - -- bytes() - bytes = function (arg) -- bytes - if type(arg) == "string" then - local bytes = {} - for i = 1, #arg do - table.insert(bytes, string.byte(arg, i)) - end - return bytes - elseif type(arg) == "table" then - return arg -- Assuming it's already a bytes table - else - error("Invalid argument type for bytes()") - end - end, - - -- compile() - compile = loadstring, - - - -- help() - help = function (object) -- help - -- This is a placeholder implementation and might not cover all possible use cases. - -- You would need to provide your own implementation based on your specific requirements. - -- Here's an example of displaying a help message for an object: - print("Help for object:", object) - print("Type:", type(object)) - print("Learn more in the official roblox documentation!") - end, - - -- memoryview() - memoryview = function (object) -- memoryview - -- This is a placeholder implementation and might not cover all possible use cases. - -- You would need to provide your own implementation based on your specific requirements. - -- Here's an example of creating a memory view object: - if type(object) == "table" then - local buffer = table.concat(object) - return { buffer = buffer, itemsize = 1 } - else - error("Invalid argument type for memoryview()") - end - end, - -- repr() - repr = function (object) -- repr - -- This is a placeholder implementation and might not cover all possible use cases. - -- You would need to provide your own implementation based on your specific requirements. - -- Here's an example of generating a representation of an object: - return tostring(object) - end, - - -- sorted() - sorted = function (iterable, cmp, key, reverse) -- sorted - -- This is a placeholder implementation and might not cover all possible use cases. - -- You would need to provide your own implementation based on your specific requirements. - -- Here's an example of sorting an iterable table: - local sortedTable = {} - for key, value in pairs(iterable) do - table.insert(sortedTable, { key = key, value = value }) - end - table.sort(sortedTable, function(a, b) - -- Compare logic based on cmp, key, reverse parameters - return a.key < b.key - end) - local i = 0 - return function() - i = i + 1 - local entry = sortedTable[i] - if entry then - return entry.key, entry.value - end - end - end, - - -- vars() - vars = function (object) -- vars - -- This is a placeholder implementation and might not cover all possible use cases. - -- You would need to provide your own implementation based on your specific requirements. - -- Here's an example of getting the attributes of an object: - local attributes = {} - for key, value in pairs(object) do - attributes[key] = value - end - return attributes - end, - - __import__ = require, - - formatmod = function (left, right) - if type(left) == "string" then - return string.format(left, right) - elseif type(left) == "table" then - local result = {} - for i, v in ipairs(left) do - result[i] = string.format(v, right) - end - return result - elseif type(left) == "number" then - return math.fmod(left, right) - else - error("Invalid argument type for %") - end - end, - - safeadd = function(left, right) - if type(left) ~= type(right) then - error("Cannot add values of different types") - end - local chosentype = type(left) - if chosentype == "string" then - return left .. right - elseif chosentype == "number" then - return left + right - elseif chosentype == "table" then - local result = {} - for i, v in ipairs(left) do - table.insert(result, v) - end - for i, v in ipairs(right) do - table.insert(result, v) - end - return result - elseif chosentype == "function" then - return function(...) - left(...) - right(...) - end - else - error("Invalid argument type for +") - end - end - } - - for i, v in items do - items[i] = wrap(v) - end - for i, v in language do - items[i] = wrap(getfenv()[i]) - end - return items -end -function fromFileImport(scriptin) - if scriptin == nil then error("Cannot import from nil.") end - if scriptin:IsA("ModuleScript") then - return require(scriptin) - else - return _G.pyc.data[scriptin] or error("Cannot import from script, it is not a module script and is not registered in _G") - end -end -local import = function(inscript) return function(index, sub) -- import - -- IMPORT METHODS: - -- Local libraries table - Added - -- _G.pyc.data[script], will return all of its return values, for non modulescripts - Added in fromFileImport - -- require(script), for modulescripts - Added in fromFileImport - -- (require/_G.pyc.data).Contents, for open, dont worry about this one - Added in open - -- Index is ., look for the script/module script from inscript's parent - Added - -- Index is /, look inside of inscript - Added - -- Index is . look for sub inside of inscripts parent - Added - -- Index is / look for sub inside of inscript - Added - -- Search in dependencies folder, dependencies folder should have a script called "content" use fromFileImport and that will have all of the dependencies - Added - - - if index == "." then - if sub then - return fromFileImport(inscript.Parent:FindFirstChild(sub)) - end - elseif index == "/" then - if sub then - return fromFileImport(inscript:FindFirstChild(sub)) - end - elseif index:sub(1,1) == "." then - if sub then - return fromFileImport(inscript.Parent:FindFirstChild(index:sub(2)))[sub] - end - elseif index:sub(1,1) == "/" then - if sub then - return fromFileImport(inscript:FindFirstChild(index:sub(2)))[sub] - end - elseif libraries[index] then -- Local libraries table - if sub then - return libraries[index][sub] - end - return libraries[index] - else - local dependencies = fromFileImport(dependenciesfolder:FindFirstChild("content")).Contents -- works just like open - if dependencies[index] then - if sub then - return dependencies[index][sub] - end - return dependencies[index] - end - end -end end -local include = function() - error("C and C++ are not complete yet") -end - -local module = function(scriptname) - return { - py = { - import(scriptname), - pythonBuiltIn(scriptname) - }, - lunar = { - import(scriptname), - {-- lunar built in: NOTHING - }, - }, - c = { - include, - {-- c and c++ built in - } - } - } -end - - - -_G.pyc = module -_G.pyc.libs = {} \ No newline at end of file diff --git a/src/model.py b/src/model.py deleted file mode 100644 index 4db8c5c..0000000 --- a/src/model.py +++ /dev/null @@ -1,1718 +0,0 @@ -########################################################################### -# Data model -# -# This is a transitional AST structure; it defines an object model that -# is structured like C++, but outputs Python. At the top of the tree is -# a Module definition. -########################################################################### -from __future__ import unicode_literals, print_function - -import sys - -from collections import OrderedDict - -from clang.cindex import TypeKind - -# Python 2 compatibility shims -if sys.version_info.major <= 2: - text = unicode -else: - text = str - - -__all__ = ( - 'CONSUMED', 'UNDEFINED', - 'Module', - 'Enumeration', 'EnumValue', - 'Function', 'Parameter', 'Variable', - 'Typedef', - 'Class', 'Struct', 'Union', - 'Attribute', 'Constructor', 'Destructor', 'Method', - 'Return', 'Block', 'If', 'Do', 'While', 'For', - 'Break', 'Continue', - 'VariableReference', 'TypeReference', 'PrimitiveTypeReference', 'AttributeReference', 'SelfReference', - 'Literal', 'ListLiteral', - 'UnaryOperation', 'BinaryOperation', 'ConditionalOperation', - 'Parentheses', 'ArraySubscript', - 'Cast', 'Invoke', 'New', -) - - -# A marker for token use during macro expansion -CONSUMED = object() - -# A marker for unknown values -UNDEFINED = object() - - -class Expression(object): - # An expression is the left node of the AST. Operations, - # literals, and references to attributes/members are all - # expresisons. Expressions don't have context - def __repr__(self): - return "<%s>" % (self.__class__.__name__) - - def clean_argument(self): - return self - - -class Declaration(Expression): - # A Declaration is a named expression. As they are named, - # They must belong to a context; that context provides the - # scope in which the declaration is valid. - # An anonymous declaration is a declaration without a - # discoverable name. - def __init__(self, context, name): - self._name = None - - self.context = context - self.name = name - - def __repr__(self): - try: - return "<%s %s>" % (self.__class__.__name__, self.full_name) - except: - return "<%s %s>" % (self.__class__.__name__, self.name) - - @property - def name(self): - return self._name - - @name.setter - def name(self, value): - # If there has been a change of name (usually due to an anonymous - # declaration being typedef'd) make sure the name dictionary is - # updated to reflect the new name. - if self.context and self._name: - del self.context.names[self._name] - - self._name = value - - if self.context and value: - self.context.names[value] = self - - @property - def full_name(self): - if self.context: - return '::'.join([self.context.full_name, self.name]) - return self.name - - @property - def root(self): - if self.context is None: - return self - else: - return self.context.root - - -class Context(Declaration): - # A context is a scope in for declaration names. Contexts - # are heirarchical - the can be part of other contexts. - # There can also be related contexts; these are alternate - # sources of names. - def __init__(self, context, name): - super(Context, self).__init__(context=context, name=name) - self.names = OrderedDict() - self.related_contexts = set() - - def __getitem__(self, name): - return self.__getitem(name, set()) - - def __getitem(self, name, looked): - if self in looked: - raise KeyError(name) - - looked.add(self) - - # The name we're looking for might be annotated with - # const, class, or any number of other descriptors. - # Remove them, and then remove any extra spaces so that - # we're left with a compact type name. - name = name.replace('const', '') - name = name.replace('class', '') - name = name.replace('virtual', '') - name = name.replace(' ', '') - - # If the name is scoped, do a lookup from the root node. - # Otherwise, just look up the name in the current context. - if '::' in name: - parts = name.split('::') - # print("LOOK FOR NAME", parts) - decl = self.root - for part in parts: - decl = decl[part] - return decl - else: - try: - # print("LOOK FOR NAME PART", name, "in", self.name, '->', self.names) - return self.names[name] - except KeyError: - if self.context: - try: - return self.context.__getitem(name, looked) - except KeyError: - pass - - for related in self.related_contexts: - # print("LOOK FOR NAME PART IN RELATED NAMESPACE", related) - try: - return related.__getitem(name, looked) - except KeyError: - pass - - raise - - -########################################################################### -# Modules -########################################################################### - -class Module(Context): - def __init__(self, name, context=None): - super(Module, self).__init__(context=context, name=name) - self.declarations = [] - self.classes = set() - - self.imports = {} - self.submodules = {} - self.module = self - self.using = None - - @property - def is_module(self): - return True - - def add_to_context(self, context): - context.add_submodule(self) - - def add_class(self, klass): - if klass not in self.classes: - self.declarations.append(klass) - self.classes.add(klass) - klass.add_imports(self) - - def add_struct(self, struct): - if struct not in self.classes: - self.declarations.append(struct) - self.classes.add(struct) - struct.add_imports(self) - - def add_union(self, union): - if union not in self.classes: - self.declarations.append(union) - self.classes.add(union) - union.add_imports(self) - - def add_function(self, function): - self.declarations.append(function) - function.add_imports(self) - - def add_enumeration(self, enum): - self.declarations.append(enum) - enum.add_imports(self) - - def add_class_attribute(self, attr): - # Class attributes might be added to this context because of - # the way they're declared, but they actually belong to - # the context in which they're declared (which should be - # a child of *this* module). - attr.context.add_class_attribute(attr) - attr.add_imports(self) - - def add_attribute(self, attr): - # Attributes might be added to this context because of - # the way they're declared, but they actually belong to - # the context in which they're declared (which should be - # a child of *this* module). - attr.context.add_attribute(attr) - attr.add_imports(self) - - def add_variable(self, var): - self.declarations.append(var) - var.add_imports(self) - - def add_statement(self, statement): - self.declarations.append(statement) - statement.add_imports(self) - - def add_import(self, path, symbol=None): - self.imports.setdefault(path, set()).add(symbol) - - def add_imports(self, context): - pass - - def add_submodule(self, module): - self.submodules[module.name] = module - - def add_using_decl(self, decl): - if not self.using: - self.using = Context(None, 'using-placeholder') - self.related_contexts.add(self.using) - - self.using.names[decl.name] = decl - - def output(self, out): - if self.imports: - for path in sorted(self.imports): - if self.imports[path]: - out.write('from %s import %s' % ( - path, - ', '.join(sorted(self.imports[path])) - )) - else: - out.write('import %s' % path) - out.clear_line() - - out.clear_major_block() - for decl in self.declarations: - # Ignore symbols that are known to be internal. - if self.context is None and decl.name in ( - 'ptrdiff_t', 'max_align_t', 'va_list', '__gnuc_va_list' - ): - continue - out.clear_minor_block() - decl.output(out) - out.clear_line() - - -########################################################################### -# Parent -########################################################################### - -class Parent(Context): - # Parent is a base class for all contexts that aren't modules. - def __init__(self, context, name): - super(Parent, self).__init__(context=context, name=name) - self.names = OrderedDict() - - @property - def module(self): - "Return the name of the module that contains the declaration of this type" - context = self.context - while not context.is_module: - context = context.context - return context - - @property - def module_name(self): - """Return the fully qualified name of this type within the module. - - For example, given: - - class Foo: - class Bar: - pass - - - class Foo has a module name of "Foo" - class Bar has a module name of "Foo.Bar" - """ - if not self.name: - return - - mod_name_parts = [self.name] - context = self.context - while not context.is_module: - mod_name_parts.append(context.name) - context = context.context - - # The module name is the name required to get to a declaration - # inside a module. - mod_name_parts.reverse() - return '.'.join(mod_name_parts) - - @property - def import_name(self): - """Return the name that must be imported to get access to this node. - - For example, given: - - class Foo: - class Bar: - pass - - the import name for both Foo and Bar is "Foo". - """ - if not self.name: - return - - context = self - while not context.context.is_module: - context = context.context - - return context.name - - @property - def is_module(self): - return False - - -########################################################################### -# Enumerated types -########################################################################### - -class Enumeration(Parent): - def __init__(self, context, name): - super(Enumeration, self).__init__(context=context, name=name) - self.enumerators = [] - - def add_enumerator(self, enumerator): - self.enumerators.append(enumerator) - self.context.names[enumerator.name] = enumerator - enumerator.enumeration = self - - def add_to_context(self, context): - context.add_enumeration(self) - - def add_imports(self, context): - context.module.add_import('enum', 'Enum') - - def output(self, out): - out.clear_major_block() - out.write("class %s(Enum):" % self.name) - out.start_block() - if self.enumerators: - for enumerator in self.enumerators: - out.clear_line() - out.write("%s = %s" % ( - enumerator.name, enumerator.value - )) - else: - out.clear_line() - out.write('pass') - out.end_block() - out.clear_major_block() - - -class EnumValue(Declaration): - # A value in an enumeration. - # EnumValues are slightly odd, becaues they are Declarations - # in the same context as the Enumeration they belong to. - def __init__(self, context, name, value): - super(EnumValue, self).__init__(context, name) - self.name = name - self.value = value - self.enumeration = None - - def add_imports(self, context): - if context.module != self.context.module: - context.module.add_import( - self.context.module.full_name.replace('::', '.'), - self.context.name - ) - - def output(self, out): - out.write('%s.%s' % (self.enumeration.module_name, self.name)) - - -########################################################################### -# Functions -########################################################################### - -class Function(Parent): - def __init__(self, context, name): - super(Function, self).__init__(context=context, name=name) - self.parameters = [] - self.statements = None - - def add_parameter(self, parameter): - self.parameters.append(parameter) - - def add_to_context(self, context): - context.add_function(self) - - def add_import(self, scope, name): - self.context.add_import(scope, name) - - def add_imports(self, context): - for param in self.parameters: - param.add_imports(context) - - def add_statement(self, statement): - self.statements.append(statement) - statement.add_imports(self) - - def output(self, out): - out.clear_major_block() - out.write('def %s(' % self.name) - for i, param in enumerate(self.parameters): - if i != 0: - out.write(', ') - param.output(out) - out.write('):') - out.start_block() - if self.statements: - for statement in self.statements: - out.clear_line() - statement.output(out) - else: - out.clear_line() - out.write('pass') - out.end_block() - out.clear_major_block() - - -class Parameter(Declaration): - def __init__(self, function, name, ctype, default): - super(Parameter, self).__init__(context=function, name=name) - self.ctype = ctype - self.default = default - - @property - def module_name(self): - return self.name - - def add_to_context(self, context): - context.add_parameter(self) - - def add_imports(self, context): - if self.default is not UNDEFINED and self.default is not None: - self.default.add_imports(context) - - def output(self, out): - out.write(self.name) - if self.default is None: - out.write('=None') - elif self.default is not UNDEFINED: - out.write('=') - self.default.output(out) - - -class Variable(Declaration): - def __init__(self, context, name, value): - super(Variable, self).__init__(context=context, name=name) - self.value = value - - @property - def module_name(self): - return self.name - - def add_to_context(self, context): - context.add_variable(self) - - def add_imports(self, context): - if self.value and self.value is not UNDEFINED: - self.value.add_imports(context) - - def output(self, out): - if self.value is not UNDEFINED: - out.write('%s = ' % self.name.replace('::', '.')) - if self.value: - self.value.output(out) - else: - out.write('None') - out.clear_line() - - -class Typedef(Declaration): - def __init__(self, context, name, typ): - super(Typedef, self).__init__(context=context, name=name) - self.type = typ - - @property - def module(self): - return self.context.module - - @property - def module_name(self): - return self.name - - def add_to_context(self, context): - context.add_variable(self) - - def add_imports(self, context): - self.type.add_imports(context) - - def output(self, out): - out.write('%s = ' % self.name) - self.type.output(out) - out.clear_line() - - -########################################################################### -# Structs -########################################################################### - -class Struct(Parent): - def __init__(self, context, name): - super(Struct, self).__init__(context=context, name=name) - self._superclass = None - self.constructors = {} - self.destructor = None - self.class_attributes = OrderedDict() - self.attributes = OrderedDict() - self.methods = OrderedDict() - self.classes = OrderedDict() - - @property - def superclass(self): - return self._superclass - - @superclass.setter - def superclass(self, ref): - ref.add_imports(self) - self._superclass = ref.type - self.related_contexts.add(ref.type) - - def add_imports(self, context): - for constructor in self.constructors.values(): - constructor.add_imports(context) - - for attr in self.class_attributes.values(): - attr.add_imports(context) - - for attr in self.attributes.values(): - attr.add_imports(context) - - for klass in self.classes.values(): - klass.add_imports(context) - - for method in self.methods.values(): - method.add_imports(context) - - def add_class(self, klass): - self.classes[klass.name] = klass - - def add_struct(self, struct): - self.classes[struct.name] = struct - - def add_union(self, union): - self.classes[union.name] = union - - def add_constructor(self, method): - print("Ignoring constructor for struct %s" % self.name, file=sys.stderr) - - def add_destructor(self, method): - if self.destructor: - if self.destructor.statements is None: - self.destructor = method - else: - raise Exception("Cannot handle multiple desructors") - else: - self.destructor = method - - def add_class_attribute(self, attr): - self.class_attributes[attr.name] = attr - - def add_attribute(self, attr): - self.attributes[attr.name] = attr - - def add_method(self, method): - self.methods[method.name] = method - - def add_variable(self, var): - self.class_attributes[var.name] = var - - def add_to_context(self, context): - context.add_struct(self) - - def output(self, out): - out.clear_major_block() - if self.superclass: - out.write("class %s(%s):" % (self.name, self.superclass.module_name)) - else: - out.write("class %s:" % self.name) - out.start_block() - - if self.class_attributes or self.attributes or self.destructor or self.classes or self.methods: - if self.class_attributes: - for name, variable in self.class_attributes.items(): - out.clear_line() - variable.output(out) - out.clear_minor_block() - - if self.attributes: - out.clear_line() - params = ''.join(', %s=None' % name for name in self.attributes.keys()) - out.write('def __init__(self%s):' % params) - out.start_block() - for name, attr in self.attributes.items(): - out.clear_line() - attr.output(out, init=True) - out.end_block() - - if self.destructor: - self.destructor.output(out) - - for name, klass in self.classes.items(): - klass.output(out) - - for name, method in self.methods.items(): - method.output(out) - else: - out.clear_line() - out.write('pass') - out.end_block() - out.clear_major_block() - - -########################################################################### -# Unions -########################################################################### - -class Union(Parent): - def __init__(self, context, name): - super(Union, self).__init__(context=context, name=name) - self._superclass = None - self.class_attributes = OrderedDict() - self.attributes = OrderedDict() - self.enumerations = OrderedDict() - self.methods = OrderedDict() - self.classes = OrderedDict() - - @property - def superclass(self): - return self._superclass - - @superclass.setter - def superclass(self, ref): - ref.add_imports(self) - self._superclass = ref.type - self.related_contexts.add(ref.type) - - def add_imports(self, context): - for attr in self.class_attributes.values(): - attr.add_imports(context) - - for attr in self.attributes.values(): - attr.add_imports(context) - - for klass in self.classes.values(): - klass.add_imports(context) - - for method in self.methods.values(): - method.add_imports(context) - - def add_class(self, klass): - self.classes[klass.name] = klass - - def add_struct(self, struct): - self.classes[struct.name] = struct - - def add_union(self, union): - self.classes[union.name] = union - - def add_class_attribute(self, var): - self.class_attributes[var.name] = var - - def add_attribute(self, attr): - self.attributes[attr.name] = attr - - def add_enumeration(self, enum): - self.enumerations[enum.name] = enum - - def add_method(self, method): - self.methods[method.name] = method - - def add_to_context(self, context): - context.add_union(self) - - def output(self, out): - out.clear_major_block() - out.write("class %s:" % self.name) - out.start_block() - if self.class_attributes or self.attributes or self.classes or self.methods: - if self.class_attributes: - for name, variable in self.class_attributes.items(): - out.clear_line() - variable.output(out) - out.clear_minor_block() - - if self.attributes: - params = ''.join(', %s=None' % name for name in self.attributes.keys()) - out.clear_line() - out.write('def __init__(self%s):' % params) - out.start_block() - for name, attr in self.attributes.items(): - out.clear_line() - attr.output(out, init=True) - out.end_block() - - for name, enum in self.enumerations.items(): - enum.output(out) - - for name, klass in self.classes.items(): - klass.output(out) - - for name, method in self.methods.items(): - method.output(out) - else: - out.clear_line() - out.write('pass') - - out.end_block() - out.end_block() - out.clear_major_block() - - -########################################################################### -# Classes -########################################################################### - -class Class(Parent): - def __init__(self, context, name): - super(Class, self).__init__(context=context, name=name) - self._superclass = None - self.constructors = {} - self.destructor = None - self.class_attributes = OrderedDict() - self.attributes = OrderedDict() - self.enumerations = OrderedDict() - self.methods = OrderedDict() - self.classes = OrderedDict() - - @property - def superclass(self): - return self._superclass - - @superclass.setter - def superclass(self, ref): - ref.add_imports(self) - self._superclass = ref.type - self.related_contexts.add(ref.type) - - def add_imports(self, context): - for constructor in self.constructors.values(): - constructor.add_imports(context) - - for attr in self.class_attributes.values(): - attr.add_imports(context) - - for attr in self.attributes.values(): - attr.add_imports(context) - - for enum in self.enumerations.values(): - enum.add_imports(context) - - for klass in self.classes.values(): - klass.add_imports(context) - - for method in self.methods.values(): - method.add_imports(context) - - def add_class(self, klass): - self.classes[klass.name] = klass - - def add_struct(self, struct): - self.classes[struct.name] = struct - - def add_union(self, union): - self.classes[union.name] = union - - def add_constructor(self, method): - signature = tuple(p.ctype for p in method.parameters) - self.constructors[signature] = method - - if len(self.constructors) > 1: - print("Multiple constructors for class %s (adding [%s])" % ( - self.name, - ','.join(s for s in signature), - ), - file=sys.stderr - ) - - def add_destructor(self, method): - if self.destructor: - if self.destructor.statements is None: - self.destructor = method - else: - raise Exception("Cannot handle multiple desructors") - else: - self.destructor = method - - def add_class_attribute(self, var): - self.class_attributes[var.name] = var - - def add_attribute(self, attr): - self.attributes[attr.name] = attr - - def add_method(self, method): - self.methods[method.name] = method - - def add_enumeration(self, enum): - self.enumerations[enum.name] = enum - - def add_variable(self, var): - self.class_attributes[var.name] = var - - def add_to_context(self, context): - context.add_class(self) - - def output(self, out): - out.clear_major_block() - if self.superclass: - out.write("class %s(%s):" % (self.name, self.superclass.module_name)) - else: - out.write("class %s:" % self.name) - out.start_block() - if self.class_attributes or self.attributes or self.constructors or self.destructor or self.enumerations or self.classes or self.methods: - if self.class_attributes: - for name, variable in self.class_attributes.items(): - out.clear_line() - variable.output(out) - out.clear_minor_block() - - for signature, constructor in sorted(self.constructors.items()): - constructor.output(out) - - if self.destructor: - self.destructor.output(out) - - for name, enum in self.enumerations.items(): - enum.output(out) - - for name, klass in self.classes.items(): - klass.output(out) - - for name, method in self.methods.items(): - method.output(out) - else: - out.clear_line() - out.write('pass') - out.end_block() - out.clear_major_block() - - -########################################################################### -# Class/Struct/Union components -########################################################################### - -class Attribute(Declaration): - def __init__(self, klass, name, value=None, static=False): - super(Attribute, self).__init__(context=klass, name=name) - self.value = value - self.static = static - - @property - def module(self): - return self.context.module - - @property - def module_name(self): - return self.context.name + '.' + self.name - - def add_to_context(self, context): - if self.static: - context.add_class_attribute(self) - else: - context.add_attribute(self) - - def add_imports(self, context): - if self.value: - self.value.add_imports(context) - - def output(self, out, init=False): - if not self.static: - out.write('self.') - out.write('%s = ' % self.name) - if init: - if self.value: - out.write('%s if %s else ' % (self.name, self.name)) - self.value.output(out) - else: - out.write(self.name) - else: - if self.value: - self.value.output(out) - else: - out.write('None') - out.clear_line() - - -class Constructor(Parent): - def __init__(self, klass): - super(Constructor, self).__init__(context=klass, name=None) - self.parameters = [] - self.statements = [] - - def __repr__(self): - return '' % self.context.full_name - - def add_parameter(self, parameter): - self.parameters.append(parameter) - - def add_to_context(self, klass): - self.context.add_constructor(self) - - def add_attribute(self, attr): - self.context.add_attribute(attr) - - def add_imports(self, context): - for param in self.parameters: - param.add_imports(context) - - def add_statement(self, statement): - self.statements.append(statement) - statement.add_imports(self.context) - - def output(self, out): - out.clear_minor_block() - out.write("def __init__(self") - if self.parameters: - for param in self.parameters: - out.write(', ') - param.output(out) - out.write('):') - out.start_block() - if self.context.attributes or self.statements: - has_init = False - for name, attr in self.context.attributes.items(): - if attr.value is not None: - out.clear_line() - attr.output(out) - has_init = True - - if self.statements: - for statement in self.statements: - out.clear_line() - statement.output(out) - elif not has_init: - out.clear_line() - out.write('pass') - - else: - out.clear_line() - out.write('pass') - out.end_block() - - -class Destructor(Parent): - def __init__(self, klass): - super(Destructor, self).__init__(context=klass, name=None) - self.parameters = [] - self.statements = None - - def add_to_context(self, klass): - self.context.add_destructor(self) - - def add_imports(self, context): - pass - - def add_statement(self, statement): - if self.statements: - self.statements.append(statement) - else: - self.statements = [statement] - statement.add_imports(self.context) - - def output(self, out): - out.clear_minor_block() - out.write("def __del__(self):") - out.start_block() - if self.statements: - for statement in self.statements: - out.clear_line() - statement.output(out) - else: - out.clear_line() - out.write('pass') - out.end_block() - - -class Method(Parent): - def __init__(self, klass, name, pure_virtual, static): - super(Method, self).__init__(context=klass, name=name) - self.parameters = [] - self.statements = None - self.pure_virtual = pure_virtual - self.static = static - - def add_parameter(self, parameter): - self.parameters.append(parameter) - - def add_to_context(self, context): - self.context.add_method(self) - - def add_imports(self, context): - for param in self.parameters: - param.add_imports(context) - - if self.statements: - for statement in self.statements: - statement.add_imports(context) - - def add_statement(self, statement): - if self.statements: - self.statements.append(statement) - else: - self.statements = [statement] - statement.add_imports(self.context) - - def output(self, out): - out.clear_minor_block() - if self.static: - out.write("@staticmethod") - out.clear_line() - out.write('def %s(' % self.name) - else: - out.write('def %s(self' % self.name) - - for i, param in enumerate(self.parameters): - if i != 0 or not self.static: - out.write(', ') - param.output(out) - out.write('):') - - out.start_block() - if self.statements: - for statement in self.statements: - out.clear_line() - statement.output(out) - elif self.pure_virtual: - out.clear_line() - out.write('raise NotImplementedError()') - else: - out.clear_line() - out.write('pass') - out.end_block() - - -########################################################################### -# Statements -########################################################################### - -class Block(Parent): - def __init__(self, context): - super(Block, self).__init__(context=context, name=None) - self.statements = [] - - def __repr__(self): - return '' - - def add_statement(self, statement): - self.statements.append(statement) - - def add_imports(self, context): - for statement in self.statements: - statement.add_imports(context) - - def output(self, out): - out.start_block() - if self.statements: - for statement in self.statements: - out.clear_line() - statement.output(out) - else: - out.clear_line() - out.write('pass') - out.end_block() - - -class Return(Expression): - def __init__(self): - self.value = None - - def add_imports(self, context): - if self.value: - self.value.add_imports(context) - - def add_expression(self, expr): - self.value = expr - - def output(self, out): - out.write('return') - if self.value: - out.write(' ') - self.value.output(out) - out.clear_line() - - -class If(Parent): - def __init__(self, condition, context): - super(If, self).__init__(context, name=None) - self.condition = condition - self.if_true = Block(self) - self.if_false = None - - def __repr__(self): - return '' % self.condition - - def add_imports(self, context): - self.condition.add_imports(context) - self.if_true.add_imports(context) - if self.if_false: - self.if_false.add_imports(context) - - def output(self, out, is_elif=False): - out.clear_line() - out.write('elif ' if is_elif else 'if ') - self.condition.output(out) - out.write(':') - - self.if_true.output(out) - - if self.if_false is not None: - if isinstance(self.if_false, If): - self.if_false.output(out, is_elif=True) - else: - out.clear_line() - out.write('else:') - if isinstance(self.if_false, Block): - self.if_false.output(out) - else: - out.clear_line() - out.start_block() - self.if_false.output(out) - out.end_block() - - -class Do(Parent): - def __init__(self, context): - super(Do, self).__init__(context, name=None) - self.condition = None - self.statements = Block(self) - - def __repr__(self): - return '' % self.condition - - def add_imports(self, context): - self.condition.add_imports(context) - self.statements.add_imports(context) - - def output(self, out): - out.clear_line() - out.write('while True:') - - if self.statements.statements: - self.statements.output(out) - - out.clear_line() - out.start_block() - out.write('if not (') # invert the condition - self.condition.output(out) - out.write('):') - - out.start_block() - out.clear_line() - out.write('break') - out.end_block() - out.end_block() - - out.end_block() - -class While(Parent): - def __init__(self, condition, context): - super(While, self).__init__(context, name=None) - self.condition = condition - self.statements = Block(self) - - def __repr__(self): - return '' % self.condition - - def add_imports(self, context): - self.condition.add_imports(context) - self.statements.add_imports(context) - - def output(self, out): - out.clear_line() - out.write('while ') - self.condition.output(out) - out.write(':') - - self.statements.output(out) - - -class For(Parent): - def __init__(self, init_stmt, expr_stmt, end_expr, context): - super(For, self).__init__(context, name=None) - self.init_stmt = init_stmt - self.expr_stmt = expr_stmt - self.end_expr = end_expr - self.statements = Block(self) - - def __repr__(self): - return '' % self.condition - - def add_imports(self, context): - if self.init_stmt: - self.init_stmt.add_imports(context) - if self.expr_stmt: - self.expr_stmt.add_imports(context) - if self.end_expr: - self.end_expr.add_imports(context) - - self.statements.add_imports(context) - - def output(self, out): - out.clear_line() - # TODO: convert simple expressions to a range statement - - if self.init_stmt: - self.init_stmt.output(out) - - if self.expr_stmt: - out.write('while ') - self.expr_stmt.output(out) - out.write(':') - else: - out.write('while True:') - - # suppress the extra pass - if self.statements.statements or not self.end_expr: - self.statements.output(out) - - if self.end_expr: - out.start_block() - out.clear_line() - self.end_expr.output(out) - out.end_block() - -class Break(object): - - def add_imports(self, context): - pass - - def output(self, out): - out.clear_line() - out.write('break') - out.clear_line() - - -class Continue(object): - - def __init__(self, end_expr): - self.end_expr = end_expr - - def add_imports(self, context): - pass - - def output(self, out): - out.clear_line() - # this exists for when a continue is used inside of a for - # loop that has an incrementing expression in it - if self.end_expr: - self.end_expr.output(out) - out.clear_line() - out.write('continue') - out.clear_line() - -########################################################################### -# References to variables and types -########################################################################### - -# A reference to a variable -class VariableReference(Expression): - def __init__(self, var, node): - self.var = var - self.node = node - - @property - def name(self): - return self.var.name - - @property - def module(self): - return self.var.context.module - - @property - def module_name(self): - return self.var.module_name - - @property - def import_name(self): - return self.var.module_name - - def add_imports(self, context): - # If the type being referenced isn't from the same module - # then an import will be required. - if context.module != self.module: - context.module.add_import( - self.module.full_name.replace('::', '.'), - self.import_name - ) - - def output(self, out): - out.write(self.module_name) - - -# A reference to a type -class TypeReference(Expression): - def __init__(self, typ): - self.type = typ - - @property - def name(self): - return self.type.name - - @property - def module(self): - return self.type.context.module - - @property - def module_name(self): - return self.type.module_name - - @property - def import_name(self): - return self.type.import_name - - def __repr__(self): - return '' % self.type - - def add_imports(self, context): - # If the type being referenced isn't from the same module - # then an import will be required. - if context.module != self.type.module: - context.module.add_import( - self.type.module.full_name.replace('::', '.'), - self.import_name - ) - - def output(self, out): - out.write(self.module_name) - - -# A reference to a primitive type -class PrimitiveTypeReference(Expression): - def __init__(self, name): - self.name = name - - def add_imports(self, context): - pass - - def output(self, out): - out.write(self.name) - - -# A reference to self. -class SelfReference(Expression): - def add_imports(self, context): - pass - - def output(self, out): - out.write('self') - - -# A reference to an attribute on a class -class AttributeReference(Expression): - def __init__(self, instance, attr): - self.instance = instance - self.name = attr - - # def add_to_context(self, context): - # pass - - def add_imports(self, context): - self.instance.add_imports(context) - - def output(self, out): - self.instance.output(out) - out.write('.%s' % self.name) - - -########################################################################### -# Literals -########################################################################### - -class Literal(Expression): - def __init__(self, value): - self.value = value - - def __repr__(self): - return "<%s %s>" % (self.__class__.__name__, self.value) - - def add_imports(self, context): - pass - - def output(self, out): - out.write(text(self.value)) - - -class ListLiteral(Expression): - def __init__(self): - self.value = [] - - def __repr__(self): - return "<%s %s>" % (self.__class__.__name__, self.value) - - def add_imports(self, context): - for value in self.value: - value.add_imports(context) - - def append(self, item): - self.value.append(item) - - def output(self, out): - out.write('[') - for i, item in enumerate(self.value): - if i != 0: - out.write(', ') - item.output(out) - out.write(']') - - -########################################################################### -# Expressions -########################################################################### - -class UnaryOperation(Expression): - def __init__(self, op, value): - self.name = op - self.value = value - - def add_imports(self, context): - self.value.add_imports(context) - - def output(self, out, depth=0): - out.write(' ' * depth) - python_op = { - '!': 'not ', - '~': '~', - }.get(self.name, self.name) - - # while this will often end up with incorrect python, - # better than silently failing on ++/-- (which don't do - # anything in python) - if python_op == '++': - self.value.output(out) - out.write(' += 1') - elif python_op == '--': - out.write(python_op) - out.write(' -= 1') - else: - out.write(python_op) - self.value.output(out) - - def clean_argument(self): - # Strip dereferencing operators - if self.name == '&': - return self.value.clean_argument() - elif self.name == '*': - return self.value.clean_argument() - else: - return self - - -class BinaryOperation(Expression): - def __init__(self, lvalue, op, rvalue): - self.lvalue = lvalue - self.name = op - self.rvalue = rvalue - - def __repr__(self): - return "<%s %s>" % (self.__class__.__name__, self.name) - - def add_imports(self, context): - self.lvalue.add_imports(context) - self.rvalue.add_imports(context) - - def add_to_context(self, context): - context.add_statement(self) - - def output(self, out, depth=0): - self.lvalue.output(out) - python_op = { - # Equality - '=': ' = ', - - # Arithmetic - '+': ' + ', - '-': ' - ', - '*': ' * ', - '/': ' / ', - '%': ' % ', - - # Comparison - '==': ' == ', - '!=': ' != ', - '>': ' > ', - '<': ' < ', - '>=': ' >= ', - '<=': ' <= ', - - # Bitwise - '&': ' & ', - '|': ' | ', - '^': ' ^ ', - '<<': ' << ', - '>>': ' >> ', - - # Assignment - '+=': ' += ', - '-=': ' -= ', - '*=': ' *= ', - '/=': ' /= ', - '%=': ' %= ', - - '&=': ' &= ', - '|=': ' |= ', - '^=': ' ^= ', - '<<=': ' <<= ', - '>>=': ' >>= ', - - # Logical - '&&': ' and ', - '||': ' or ', - - }.get(self.name, self.name) - - out.write(python_op) - self.rvalue.output(out) - - -class ConditionalOperation(Expression): - def __init__(self, condition, true_result, false_result): - self.condition = condition - self.true_result = true_result - self.false_result = false_result - - def add_imports(self, context): - self.condition.add_imports(context) - self.true_result.add_imports(context) - self.false_result.add_imports(context) - - def output(self, out): - self.true_result.output(out) - out.write(' if ') - self.condition.output(out) - out.write(' else ') - self.false_result.output(out) - - -class Parentheses(Expression): - def __init__(self, body): - self.body = body - - def add_imports(self, context): - self.body.add_imports(context) - - def output(self, out): - if isinstance(self.body, (BinaryOperation, ConditionalOperation)): - out.write('(') - self.body.output(out) - out.write(')') - else: - self.body.output(out) - - -class ArraySubscript(Expression): - def __init__(self, value, index): - self.value = value - self.index = index - - def add_imports(self, context): - self.value.add_imports(context) - self.index.add_imports(context) - - def output(self, out): - self.value.output(out) - out.write('[') - self.index.output(out) - out.write(']') - - def clean_argument(self): - return self - - -class Cast(Expression): - def __init__(self, typekind, value): - self.typekind = typekind - self.value = value - - def __repr__(self): - return "" % self.typekind - - def add_imports(self, context): - self.value.add_imports(context) - - def output(self, out): - # Primitive types are cast using Python casting. - # Other types are passed through as ducks. - if self.typekind == TypeKind.BOOL: - out.write('bool(') - self.value.output(out) - out.write(')') - elif self.typekind in ( - TypeKind.CHAR_U, - TypeKind.UCHAR, - TypeKind.CHAR16, - TypeKind.CHAR32, - TypeKind.CHAR_S, - TypeKind.SCHAR, - TypeKind.WCHAR, - ): - out.write('str(') - self.value.output(out) - out.write(')') - elif self.typekind in ( - TypeKind.USHORT, - TypeKind.UINT, - TypeKind.ULONG, - TypeKind.ULONGLONG, - TypeKind.UINT128, - TypeKind.SHORT, - TypeKind.INT, - TypeKind.LONG, - TypeKind.LONGLONG, - TypeKind.INT128, - ): - out.write('int(') - self.value.output(out) - out.write(')') - elif self.typekind in ( - TypeKind.FLOAT, - TypeKind.DOUBLE, - TypeKind.LONGDOUBLE - ): - out.write('float(') - self.value.output(out) - out.write(')') - else: - self.value.output(out) - - def clean_argument(self): - return self.value - - -class Invoke(Expression): - def __init__(self, fn): - self.fn = fn - self.arguments = [] - - def __repr__(self): - return "" % self.fn - - def add_argument(self, argument): - self.arguments.append(argument) - - def add_imports(self, context): - self.fn.add_imports(context) - for arg in self.arguments: - arg.add_imports(context) - - def output(self, out): - self.fn.output(out) - out.write('(') - if self.arguments: - self.arguments[0].output(out) - for arg in self.arguments[1:]: - out.write(', ') - arg.output(out) - out.write(')') - - -class New(Expression): - def __init__(self, typeref): - self.typeref = typeref - self.arguments = [] - - def __repr__(self): - return "" % self.typeref - - def add_argument(self, argument): - self.arguments.append(argument) - - def add_imports(self, context): - self.typeref.add_imports(context) - for arg in self.arguments: - arg.add_imports(context) - - def output(self, out): - self.typeref.output(out) - out.write('(') - if self.arguments: - self.arguments[0].output(out) - for arg in self.arguments[1:]: - out.write(', ') - arg.output(out) - out.write(')') \ No newline at end of file diff --git a/src/nameconstdesc.py b/src/nameconstdesc.py deleted file mode 100644 index ac95186..0000000 --- a/src/nameconstdesc.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Name constant description""" - - -class NameConstantDesc: - """Name constant description""" - - NAME = { - None: "nil", - True: "true", - False: "false", - } diff --git a/src/nodevisitor.py b/src/nodevisitor.py deleted file mode 100644 index 56efe6f..0000000 --- a/src/nodevisitor.py +++ /dev/null @@ -1,967 +0,0 @@ -"""Node visitor""" -import ast, sys - -import os -if not (os.path.dirname(os.path.abspath(__file__)).startswith(sys.path[-1])): - from binopdesc import BinaryOperationDesc - from boolopdesc import BooleanOperationDesc - from cmpopdesc import CompareOperationDesc - from nameconstdesc import NameConstantDesc - from unaryopdesc import UnaryOperationDesc - - from context import Context - from loopcounter import LoopCounter - from tokenendmode import TokenEndMode - from colortext import * -else: - from .binopdesc import BinaryOperationDesc - from .boolopdesc import BooleanOperationDesc - from .cmpopdesc import CompareOperationDesc - from .nameconstdesc import NameConstantDesc - from .unaryopdesc import UnaryOperationDesc - - from .context import Context - from .loopcounter import LoopCounter - from .tokenendmode import TokenEndMode - from .colortext import * - -class NodeVisitor(ast.NodeVisitor): - LUACODE = "[[lua]]" - - """Node visitor""" - def __init__(self, context=None, config=None): - self.context = context if context is not None else Context() - self.config = config - self.last_end_mode = TokenEndMode.LINE_FEED - self.output = [] - - def visit_Assign(self, node): - """Visit assign""" - target = self.visit_all(node.targets[0], inline=True) - value = self.visit_all(node.value, inline=True) - - local_keyword = "" - - last_ctx = self.context.last() - - if last_ctx["class_name"]: - target = ".".join([last_ctx["class_name"], target]) - - if ("." not in target) and (not last_ctx["locals"].exists(target)) and ("[" not in target) and ("(" not in target): - local_keyword = "local " - last_ctx["locals"].add_symbol(target) - - - self.emit("{local}{target} = {value}".format(local=local_keyword, - target=target, - value=value)) - - ### MATCHES ### - def visit_Match(self, node): - """Visit match""" - self.emit("match({0}, ".format(self.visit_all(node.subject, inline=True))+"{") - for case in node.cases: - if hasattr(case.pattern, "value"): - self.emit("[{0}] = function()".format(case.pattern.value.s)) - self.visit_all(case.body) - self.emit("end,") - else: - self.emit("[\"default\"] = function()") - self.visit_all(case.body) - self.emit("end,") - self.emit("})") - # example input: - # match x: - # case "10": - # print("x is 10") - # case "20": - # print("x is 20") - # case _: - # print("x is not 10 or 20") - # example output: - # match(x, { - # ["10"] = function() - # print("x is 10") - # end, - # ["20"] = function() - # print("x is 20") - # end, - # ["_"] = function() - # print("x is not 10 or 20") - # end - # }) - - def visit_MatchValue(self, node): - """Visit match value""" - return self.visit_all(node.value, inline=True) - - def visit_MatchCase(self, node): - """Visit match case""" - return self.visit_all(node.body) - - def visit_MatchPattern(self, node): - """Visit match pattern""" - return self.visit_all(node.pattern, inline=True) - - def visit_MatchSingleton(self, node): - """Visit match singleton""" - return self.visit_all(node.pattern, inline=True) - - def visit_MatchSequence(self, node): - """Visit match sequence""" - return self.visit_all(node.pattern, inline=True) - - def visit_MatchMapping(self, node): - """Visit match mapping""" - return self.visit_all(node.pattern, inline=True) - - def visit_MatchClass(self, node): - """Visit match class""" - return self.visit_all(node.pattern, inline=True) - - def visit_MatchAs(self, node): - """Visit match as""" - return self.visit_all(node.pattern, inline=True) - - def visit_MatchKeyword(self, node): - """Visit match keyword""" - return self.visit_all(node.pattern, inline=True) - - def visit_MatchStar(self, node): - """Visit match star""" - return self.visit_all(node.pattern, inline=True) - - def visit_MatchOr(self, node): - """Visit match or""" - return self.visit_all(node.pattern, inline=True) - - ### END MATCH ### - def visit_AnnAssign(self, node): - """Visit annassign""" - target = self.visit_all(node.target, inline=True) - value = self.visit_all(node.value, inline=True) - - local_keyword = "" - - last_ctx = self.context.last() - - if last_ctx["class_name"]: - target = ".".join([last_ctx["class_name"], target]) - - if "." not in target and not last_ctx["locals"].exists(target): - local_keyword = "local " - last_ctx["locals"].add_symbol(target) - - self.emit("{local}{target} = {value}".format(local=local_keyword, - target=target, - value=value, - type=self.visit_all(node.annotation, inline=True))) - # example input: - # a: int = 1 - # example output: - # local a = 1 - - def visit_AugAssign(self, node): - """Visit augassign""" - operation = BinaryOperationDesc.OPERATION[node.op.__class__] - - target = self.visit_all(node.target, inline=True) - - values = { - "left": target, - "right": self.visit_all(node.value, inline=True), - "operation": operation["value"], - } - - line = "({})".format(operation["format"]) - line = line.format(**values) - - self.emit("{target} = {line}".format(target=target, line=line)) - - def visit_Attribute(self, node): - """Visit attribute""" - line = "{object}.{attr}" - values = { - "object": self.visit_all(node.value, True), - "attr": node.attr, - } - self.emit(line.format(**values)) - - def visit_BinOp(self, node): - """Visit binary operation""" - operation = BinaryOperationDesc.OPERATION[node.op.__class__] - format = "" - if "format" in operation: - format = operation["format"] - else: - format = operation["function"](node.left, node.right) - line = "({})".format(format) - values = { - "left": self.visit_all(node.left, True), - "right": self.visit_all(node.right, True), - "operation": operation["value"], - } - self.emit(line.format(**values)) - - def visit_BoolOp(self, node): - """Visit boolean operation""" - operation = BooleanOperationDesc.OPERATION[node.op.__class__] - line = "({})".format(operation["format"]) - values = { - "left": self.visit_all(node.values[0], True), - "right": self.visit_all(node.values[1], True), - "operation": operation["value"], - } - - self.emit(line.format(**values)) - - def visit_Break(self, node): - """Visit break""" - self.emit("break") - - def visit_AsyncWith(self, node): - """Visit async with""" - """Visit with""" - self.emit("coroutine.wrap(function()") - - self.visit_all(node.body) - - body = self.output[-1] - lines = [] - for i in node.items: - line = "" - if i.optional_vars is not None: - line = "local {} = " - line = line.format(self.visit_all(i.optional_vars, - inline=True)) - line += self.visit_all(i.context_expr, inline=True) - lines.append(line) - - for line in lines: - body.insert(0, line) - - self.emit("end)()") - - def visit_Await(self, node): - """Visit await""" - self.emit("coroutine.yield({})".format(self.visit_all(node.value, True))) - - def visit_Slice(self, node): - """Visit slice""" - print(yellow("warning", ["bold"])+" syntax based slicing is not supported yet. Use slice(, , , ) instead. This node will not be translated.") - - - def visit_JoinedStr(self, node): - """Visit joined string""" - values = [self.visit_all(value, True) for value in node.values] - self.emit('.'.join(values)) - - def visit_Bytes(self, node): - """Visit bytes""" - #remove first letter from string - self.emit(str(node.s)[1:]) - - def visit_Try(self, node): - """Visit try""" - self.emit("local success, result = pcall(function()") - self.visit_all(node.body) - self.emit("end)") - - def visit_TryStar(self, node): - """Visit try""" - self.emit("local success, result = pcall(function()") - self.visit_all(node.body) - self.emit("end)") - - def visit_ImportFrom(self, node): - """Visit import from""" - module = node.module - if module is None: - module = "" - else: - module = module - - for name in node.names: - if name.asname is None: - self.emit("local {name} = import(\"{module}\", \"{name}\")".format( - name=name.name, - module=module, - )) - else: - if name.name == "*": - print("roblox-pyc: Importing all from a module is not supported yet. Issues will occur.") - self.emit("local {name} = import(\"{module}\", \"{realname}\")".format( - name=name.asname, - module=module, - realname=name.name, - )) - - def visit_Assert(self, node): - """Visit assert""" - self.emit("assert({})".format(self.visit_all(node.test, True))) - - def visit_Nonlocal(self, node): - """Visit nonlocal""" - for name in node.names: - self.context.last()["nonlocals"].add_symbol(name) - - - def visit_ExceptHandler(self, node): - """Visit except handler""" - self.emit("if not success then") - self.emit("local "+node.name + " = result") - self.visit_all(node.body) - self.emit("end") - - def visit_AsyncFunctionDef(self, node): - """Visit async function definition""" - line = "{local}{name} = asynchronousfunction(function({arguments})" - - last_ctx = self.context.last() - - name = node.name - if last_ctx["class_name"]: - name = ".".join([last_ctx["class_name"], name]) - - arguments = [arg.arg for arg in node.args.args] - - if node.args.vararg is not None: - arguments.append("...") - - local_keyword = "" - - if "." not in name and not last_ctx["locals"].exists(name): - local_keyword = "local " - last_ctx["locals"].add_symbol(name) - - function_def = line.format(local=local_keyword, - name=name, - arguments=", ".join(arguments)) - - self.emit(function_def) - - self.context.push({"class_name": ""}) - self.visit_all(node.body) - self.context.pop() - - body = self.output[-1] - - if node.args.vararg is not None: - line = "local {name} = list {{...}}".format(name=node.args.vararg.arg) - body.insert(0, line) - - arg_index = -1 - for i in reversed(node.args.defaults): - line = "{name} = {name} or {value}" - - arg = node.args.args[arg_index] - values = { - "name": arg.arg, - "value": self.visit_all(i, inline=True), - } - body.insert(0, line.format(**values)) - - arg_index -= 1 - - self.emit("end)") - - for decorator in reversed(node.decorator_list): - decorator_name = self.visit_all(decorator, inline=True) - values = { - "name": name, - "decorator": decorator_name, - } - line = "{name} = {decorator}({name})".format(**values) - self.emit(line) - def visit_AsyncFor(self, node): - """Visit async for""" - line = "asynchronousfunction(function() for {target} in {iter} do" - values = { - "target": self.visit_all(node.target, True), - "iter": self.visit_all(node.iter, True), - } - self.emit(line.format(**values)) - self.visit_all(node.body) - self.emit("end end)") - - def visit_Raise(self, node): - """Visit raise""" - self.emit("error(" + self.visit_all(node.exc, True) + ")") - - def visit_FormattedValue(self, node): - """Visit formatted value""" - if node.format_spec is None: - return self.visit_all(node.value, True) - else: - return self.visit_all(node.value, True) + ":" + self.visit_all(node.format_spec, True) - - def visit_Set(self, node): - """Visit set""" - values = [self.visit_all(value, True) for value in node.elts] - self.emit('{' + ', '.join(values) + '}') - - def visit_Call(self, node): - """Visit function call""" - line = "{name}({arguments})" - - name = self.visit_all(node.func, inline=True) - arguments = [self.visit_all(arg, inline=True) for arg in node.args] - - self.emit(line.format(name=name, arguments=", ".join(arguments))) - - def visit_ClassDef(self, node): - """Visit class definition""" - bases = [self.visit_all(base, inline=True) for base in node.bases] - - local_keyword = "" - last_ctx = self.context.last() - if not last_ctx["class_name"] and not last_ctx["locals"].exists(node.name): - local_keyword = "local " - last_ctx["locals"].add_symbol(node.name) - - name = node.name - if last_ctx["class_name"]: - name = ".".join([last_ctx["class_name"], name]) - - values = { - "local": local_keyword, - "name": name, - "node_name": node.name, - } - - self.emit("{local}{name} = class(function({node_name})".format(**values)) - - self.context.push({"class_name": node.name}) - self.visit_all(node.body) - self.context.pop() - - self.output[-1].append("return {node_name}".format(**values)) - - self.emit("end, {{{}}})".format(", ".join(bases))) - - # Return class object only in the top-level classes. - # Not in the nested classes. - if self.config["class"]["return_at_the_end"] and not last_ctx["class_name"]: - self.emit("return {}".format(name)) - - def visit_Compare(self, node): - """Visit compare""" - - line = "" - - left = self.visit_all(node.left, inline=True) - for i in range(len(node.ops)): - operation = node.ops[i] - operation = CompareOperationDesc.OPERATION[operation.__class__] - - right = self.visit_all(node.comparators[i], inline=True) - - values = { - "left": left, - "right": right, - } - - if isinstance(operation, str): - values["op"] = operation - line += "{left} {op} {right}".format(**values) - elif isinstance(operation, dict): - line += operation["format"].format(**values) - - if i < len(node.ops) - 1: - left = right - line += " and " - - self.emit("({})".format(line)) - - def visit_Continue(self, node): - """Visit continue""" - last_ctx = self.context.last() - line = "continue" - self.emit(line) - - def visit_Delete(self, node): - """Visit delete""" - targets = [self.visit_all(target, inline=True) for target in node.targets] - nils = ["nil" for _ in targets] - line = "{targets} = {nils}".format(targets=", ".join(targets), - nils=", ".join(nils)) - self.emit(line) - - def visit_Dict(self, node): - """Visit dictionary""" - keys = [] - - for key in node.keys: - value = self.visit_all(key, inline=True) - if isinstance(key, ast.Str): - value = "[{}]".format(value) - keys.append(value) - - values = [self.visit_all(item, inline=True) for item in node.values] - - elements = ["{} = {}".format(keys[i], values[i]) for i in range(len(keys))] - elements = ", ".join(elements) - self.emit("dict {{{}}}".format(elements)) - - def visit_DictComp(self, node): - """Visit dictionary comprehension""" - self.emit("(function()") - self.emit("local result = dict {}") - - ends_count = 0 - - for comp in node.generators: - line = "for {target} in {iterator} do" - values = { - "target": self.visit_all(comp.target, inline=True), - "iterator": self.visit_all(comp.iter, inline=True), - } - line = line.format(**values) - self.emit(line) - ends_count += 1 - - for if_ in comp.ifs: - line = "if {} then".format(self.visit_all(if_, inline=True)) - self.emit(line) - ends_count += 1 - - line = "result[{key}] = {value}" - values = { - "key": self.visit_all(node.key, inline=True), - "value": self.visit_all(node.value, inline=True), - } - self.emit(line.format(**values)) - - self.emit(" ".join(["end"] * ends_count)) - - self.emit("return result") - self.emit("end)()") - - def visit_Ellipsis(self, node): - """Visit ellipsis""" - self.emit("...") - - def visit_Expr(self, node): - """Visit expr""" - expr_is_docstring = False - if isinstance(node.value, ast.Str): - expr_is_docstring = True - - self.context.push({"docstring": expr_is_docstring}) - output = self.visit_all(node.value) - self.context.pop() - - self.output.append(output) - - def visit_FunctionDef(self, node): - """Visit function definition""" - line = "{local}function {name}({arguments})" - - last_ctx = self.context.last() - - name = node.name - if last_ctx["class_name"]: - name = ".".join([last_ctx["class_name"], name]) - - arguments = [arg.arg for arg in node.args.args] - - if node.args.vararg is not None: - arguments.append("...") - - local_keyword = "" - - if "." not in name and not last_ctx["locals"].exists(name): - local_keyword = "local " - last_ctx["locals"].add_symbol(name) - - function_def = line.format(local=local_keyword, - name=name, - arguments=", ".join(arguments)) - - self.emit(function_def) - - self.context.push({"class_name": ""}) - self.visit_all(node.body) - self.context.pop() - - body = self.output[-1] - - if node.args.vararg is not None: - line = "local {name} = list {{...}}".format(name=node.args.vararg.arg) - body.insert(0, line) - - arg_index = -1 - for i in reversed(node.args.defaults): - line = "{name} = {name} or {value}" - - arg = node.args.args[arg_index] - values = { - "name": arg.arg, - "value": self.visit_all(i, inline=True), - } - body.insert(0, line.format(**values)) - - arg_index -= 1 - - self.emit("end") - - for decorator in reversed(node.decorator_list): - decorator_name = self.visit_all(decorator, inline=True) - values = { - "name": name, - "decorator": decorator_name, - } - line = "{name} = {decorator}({name})".format(**values) - self.emit(line) - - def visit_For(self, node): - """Visit for loop""" - line = "for {target} in {iter} do" - - values = { - "target": self.visit_all(node.target, inline=True), - "iter": self.visit_all(node.iter, inline=True), - } - - self.emit(line.format(**values)) - - continue_label = LoopCounter.get_next() - self.context.push({ - "loop_label_name": continue_label, - }) - self.visit_all(node.body) - self.context.pop() - - #self.output[-1].append("::{}::".format(continue_label)) - - self.emit("end") - def visit_Global(self, node): - """Visit globals""" - last_ctx = self.context.last() - for name in node.names: - last_ctx["globals"].add_symbol(name) - - def visit_If(self, node): - """Visit if""" - test = self.visit_all(node.test, inline=True) - - line = "if {} then".format(test) - - self.emit(line) - self.visit_all(node.body) - - if node.orelse: - if isinstance(node.orelse[0], ast.If): - elseif = node.orelse[0] - elseif_test = self.visit_all(elseif.test, inline=True) - - line = "elseif {} then".format(elseif_test) - self.emit(line) - - output_length = len(self.output) - self.visit_If(node.orelse[0]) - - del self.output[output_length] - del self.output[-1] - else: - self.emit("else") - self.visit_all(node.orelse) - - self.emit("end") - - def visit_IfExp(self, node): - """Visit if expression""" - line = "{cond} and {true_cond} or {false_cond}" - values = { - "cond": self.visit_all(node.test, inline=True), - "true_cond": self.visit_all(node.body, inline=True), - "false_cond": self.visit_all(node.orelse, inline=True), - } - - self.emit(line.format(**values)) - - def visit_Import(self, node): - """Visit import""" - line = 'local {asname} = import("{name}")' - values = {"asname": "", "name": ""} - - if node.names[0].asname is None: - values["name"] = node.names[0].name - values["asname"] = values["name"] - values["asname"] = values["asname"].split(".")[-1] - else: - values["asname"] = node.names[0].asname - values["name"] = node.names[0].name - - self.emit(line.format(**values)) - - def visit_Index(self, node): - """Visit index""" - self.emit(self.visit_all(node.value, inline=True)) - - def visit_Lambda(self, node): - """Visit lambda""" - line = "function({arguments}) return" - - arguments = [arg.arg for arg in node.args.args] - - function_def = line.format(arguments=", ".join(arguments)) - - output = [] - output.append(function_def) - output.append(self.visit_all(node.body, inline=True)) - output.append("end") - - self.emit(" ".join(output)) - - def visit_List(self, node): - """Visit list""" - elements = [self.visit_all(item, inline=True) for item in node.elts] - line = "list {{{}}}".format(", ".join(elements)) - self.emit(line) - - def visit_ListComp(self, node): - """Visit list comprehension""" - self.emit("(function()") - self.emit("local result = list {}") - - ends_count = 0 - - for comp in node.generators: - line = "for {target} in {iterator} do" - values = { - "target": self.visit_all(comp.target, inline=True), - "iterator": self.visit_all(comp.iter, inline=True), - } - line = line.format(**values) - self.emit(line) - ends_count += 1 - - for if_ in comp.ifs: - line = "if {} then".format(self.visit_all(if_, inline=True)) - self.emit(line) - ends_count += 1 - - line = "result.append({})" - line = line.format(self.visit_all(node.elt, inline=True)) - self.emit(line) - - self.emit(" ".join(["end"] * ends_count)) - - self.emit("return result") - self.emit("end)()") - - def visit_Module(self, node): - """Visit module""" - self.visit_all(node.body) - self.output = self.output[0] - - def visit_Name(self, node): - """Visit name""" - self.emit(node.id) - - def visit_NameConstant(self, node): - """Visit name constant""" - self.emit(NameConstantDesc.NAME[node.value]) - - def visit_Num(self, node): - """Visit number""" - self.emit(str(node.n)) - - def visit_Pass(self, node): - """Visit pass""" - pass - - def visit_Return(self, node): - """Visit return""" - line = "return " - line += self.visit_all(node.value, inline=True) - self.emit(line) - - def visit_Starred(self, node): - """Visit starred object""" - value = self.visit_all(node.value, inline=True) - line = "unpack({})".format(value) - self.emit(line) - - def visit_Str(self, node): - """Visit str""" - value = node.s - if value.startswith(NodeVisitor.LUACODE): - value = value[len(NodeVisitor.LUACODE):] - self.emit(value) - elif self.context.last()["docstring"]: - self.emit('--[[ {} ]]'.format(node.s)) - else: - # Add to context - self.emit('"{}"'.format(node.s)) - - def visit_Subscript(self, node): - """Visit subscript""" - line = "{name}[{index}]" - values = { - "name": self.visit_all(node.value, inline=True), - "index": self.visit_all(node.slice, inline=True), - } - # append to context - ##self.context.last()["subscript"] = node - - self.emit(line.format(**values)) - - def visit_Tuple(self, node): - """Visit tuple""" - elements = [self.visit_all(item, inline=True) for item in node.elts] - self.emit(", ".join(elements)) - - def visit_UnaryOp(self, node): - """Visit unary operator""" - operation = UnaryOperationDesc.OPERATION[node.op.__class__] - value = self.visit_all(node.operand, inline=True) - - line = operation["format"] - values = { - "value": value, - "operation": operation["value"], - } - - self.emit(line.format(**values)) - - def visit_While(self, node): - """Visit while""" - test = self.visit_all(node.test, inline=True) - - self.emit("while {} do".format(test)) - - continue_label = LoopCounter.get_next() - self.context.push({ - "loop_label_name": continue_label, - }) - self.visit_all(node.body) - self.context.pop() - - #self.output[-1].append("::{}::".format(continue_label)) - - self.emit("end") - - def visit_Yield(self, node): - """Visit yield""" - self.emit("coroutine.yield({})".format(self.visit_all(node.value, True))) - def visit_GeneratorExp(self, node): - """Visit generator expression""" - self.emit("(function()") - self.emit("local result = list {}") - - ends_count = 0 - - for comp in node.generators: - line = "for {target} in {iterator} do" - values = { - "target": self.visit_all(comp.target, inline=True), - "iterator": self.visit_all(comp.iter, inline=True), - } - line = line.format(**values) - self.emit(line) - ends_count += 1 - - for if_ in comp.ifs: - line = "if {} then".format(self.visit_all(if_, inline=True)) - self.emit(line) - ends_count += 1 - - line = "result.append({})" - line = line.format(self.visit_all(node.elt, inline=True)) - self.emit(line) - - self.emit(" ".join(["end"] * ends_count)) - - self.emit("return result") - self.emit("end)()") - def visit_YieldFrom(self, node): - """Visit yield from""" - self.emit("coroutine.yield({})".format(self.visit_all(node.value, True))) - def visit_With(self, node): - """Visit with""" - self.emit("do") - - self.visit_all(node.body) - - body = self.output[-1] - lines = [] - for i in node.items: - line = "" - if i.optional_vars is not None: - line = "local {} = " - line = line.format(self.visit_all(i.optional_vars, - inline=True)) - line += self.visit_all(i.context_expr, inline=True) - lines.append(line) - - for line in lines: - body.insert(0, line) - - self.emit("end") - def visit_SetComp(self, node): - """Visit set comprehension""" - self.emit("(function()") - self.emit("local result = set {}") - - ends_count = 0 - - for comp in node.generators: - line = "for {target} in {iterator} do" - values = { - "target": self.visit_all(comp.target, inline=True), - "iterator": self.visit_all(comp.iter, inline=True), - } - line = line.format(**values) - self.emit(line) - ends_count += 1 - - for if_ in comp.ifs: - line = "if {} then".format(self.visit_all(if_, inline=True)) - self.emit(line) - ends_count += 1 - - line = "result.add({})" - line = line.format(self.visit_all(node.elt, inline=True)) - self.emit(line) - - self.emit(" ".join(["end"] * ends_count)) - - self.emit("return result") - self.emit("end)()") - def generic_visit(self, node): - """Unknown nodes handler""" - if node is None: - return - raise RuntimeError("{} is unsupported".format(node)) - - def visit_all(self, nodes, inline=False): - """Visit all nodes in the given list""" - - if not inline: - last_ctx = self.context.last() - last_ctx["locals"].push() - - visitor = NodeVisitor(context=self.context, config=self.config) - - if isinstance(nodes, list): - for node in nodes: - visitor.visit(node) - if not inline: - self.output.append(visitor.output) - else: - visitor.visit(nodes) - if not inline: - self.output.extend(visitor.output) - - if not inline: - last_ctx = self.context.last() - last_ctx["locals"].pop() - - if inline: - return " ".join(visitor.output) - - def emit(self, value): - """Add translated value to the output""" - self.output.append(value) diff --git a/src/parser.py b/src/parser.py deleted file mode 100644 index ee7f2bf..0000000 --- a/src/parser.py +++ /dev/null @@ -1,1711 +0,0 @@ -########################################################################### -# Code Parser -# -# This uses the clang Python API to parse and traverse the AST for C++ -# code, producing a data model. -########################################################################### -from __future__ import unicode_literals, print_function - -import argparse -import os -import re -import sys - -from clang.cindex import ( - CursorKind, - Index, - StorageClass, - TranslationUnit, - TypeKind, - Config, -) - -import os -if not (os.path.dirname(os.path.abspath(__file__)).startswith(sys.path[-1])): - from model import * - from writer import CodeWriter -else: - from .model import * - from .writer import CodeWriter - - -# Python 2 compatibility shims -if sys.version_info.major <= 2: - text = unicode -else: - text = str - - -class BaseParser(object): - def __init__(self): - self.index = Index.create() - - def diagnostics(self, out): - for diag in self.tu.diagnostics: - print('%s %s (line %s, col %s) %s' % ( - { - 4: 'FATAL', - 3: 'ERROR', - 2: 'WARNING', - 1: 'NOTE', - 0: 'IGNORED', - }[diag.severity], - diag.location.file, - diag.location.line, - diag.location.column, - diag.spelling - ), file=out) - - -class CodeConverter(BaseParser): - def __init__(self, name, dylib, verbosity=0): - if dylib != 'None': - #print("Setting dylib to %s" % dylib) - Config.set_library_file(dylib) - super(CodeConverter, self).__init__() - # Tools for debugging. - self.verbosity = verbosity - self._depth = 0 - - self.root_module = Module(name) - self.filenames = set() - self.macros = {} - self.instantiated_macros = {} - - self.ignored_files = set() - self.last_decl = [] - - self.namespace = self.root_module - - def output(self, module, out): - module_path = module.split('.') - - mod = None - for i, mod_name in enumerate(module_path): - if mod is None: - mod = self.root_module - else: - mod = mod.submodules[mod_name] - - if mod_name != mod.name: - raise Exception("Unknown module '%s'" % '.'.join(module_path[:i+1])) - - if mod: - mod.output(CodeWriter(out)) - else: - raise Exception('No module name specified') - - def _output_module(self, mod, out): - out.write('===== %s.py ==================================================\n' % mod.full_name) - mod.output(CodeWriter(out)) - for submodule in mod.submodules.values(): - self._output_module(submodule, out) - - - def output_all(self, out): - self._output_module(self.root_module, out) - - def parse(self, filenames, flags): - abs_filenames = [os.path.abspath(f) for f in filenames] - self.filenames.update(abs_filenames) - - for filename in abs_filenames: - if os.path.splitext(filename)[1] != '.h': - self.tu = self.index.parse( - filename, - args=flags, - options=TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD - ) - self.handle(self.tu.cursor, self.root_module) - - def parse_text(self, content, flags): - for f, c in content: - abs_filename = os.path.abspath(f) - self.filenames.add(abs_filename) - - self.tu = self.index.parse( - f, - args=flags, - unsaved_files=[(f, c)], - options=TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD - ) - self.handle(self.tu.cursor, self.root_module) - - def localize_namespace(self, namespace): - """Strip any namespace parts that are implied by the current namespace. - - This takes into account the current namespace, and any `using` - declarations that are currently in effect. - """ - namespace = self.root_module.full_name + '::' + namespace.strip('::') - - # Strip the current namespace or using prefix - # if there is an overlap. - if namespace.startswith(self.namespace.full_name): - namespace = namespace[len(self.namespace.full_name) + 2:] - #else: - #for using_namespace in self.using: - # # TODO: handle USING statements. - # pass - - # If there's still a namespace, add a separator - # so it can prefixed onto the name to be used. - if namespace: - namespace += "::" - - return namespace - - def lookup(self, children, context): - """Utility function to lookup a namespaced object""" - child = next(children) - names = [] - try: - while child.kind == CursorKind.NAMESPACE_REF: - names.append(child.spelling) - child = next(children) - except StopIteration: - child = None - - if names: - context = self.root_module['::'.join(names)] - - return child, context - - - def handle(self, node, context=None): - if (node.location.file is None - or os.path.abspath(node.location.file.name) in self.filenames): - try: - if ((node.location.file or node.kind == CursorKind.TRANSLATION_UNIT) and self.verbosity > 0 - or node.location.file is None and self.verbosity > 1): - debug = [ - ' ' * self._depth, - context, - node.kind, - '(type:%s | result type:%s)' % (node.type.kind, node.result_type.kind), - node.spelling, - node.location.file - ] - if self.verbosity > 1: - debug.extend([ - '[line %s:%s%s-%s]' % ( - node.extent.start.line, node.extent.start.column, - ('line %s:' % node.extent.end.line) - if node.extent.start.line != node.extent.end.line - else '', - node.extent.end.column), - ]) - if self.verbosity > 2: - debug.extend([ - [t.spelling for t in node.get_tokens()] - ]) - print(*debug) - - handler = getattr(self, 'handle_%s' % node.kind.name.lower()) - - except AttributeError: - print( - "Ignoring node of type %s (%s)" % ( - node.kind, - ' '.join( - t.spelling for t in node.get_tokens()) - ), - file=sys.stderr - ) - handler = None - else: - if node.location.file.name.startswith('/usr/include'): - min_level = 2 - elif node.location.file.name.startswith('/usr/local'): - min_level = 2 - else: - min_level = 1 - - if node.location.file.name not in self.ignored_files: - if self.verbosity >= min_level: - print("Ignoring node in file %s" % node.location.file) - self.ignored_files.add(node.location.file.name) - handler = None - - if handler: - self._depth += 1 - result = handler(node, context) - self._depth -= 1 - - # Some definitions might be part of an inline typdef. - # Keep a track of the last type defined, just in case - # it needs to be referenced as part of a typedef. - if node.kind.name.lower() in ( - 'struct_decl', - 'union_decl', - ): - self.last_decl = result - else: - self.last_decl = None - - return result - - def handle_unexposed_decl(self, node, context): - # Ignore unexposed declarations (e.g., friend qualifiers) - pass - - def handle_struct_decl(self, node, context): - # If a struct is pre-declared, use the existing declaration, - # rather than overwriting the old one. - try: - struct = context[node.spelling] - except KeyError: - struct = Struct(context, node.spelling) - - for child in node.get_children(): - decl = self.handle(child, struct) - if decl: - decl.add_to_context(struct) - return struct - - def handle_union_decl(self, node, context): - # If a union is pre-declared, use the existing declaration, - # rather than overwriting the old one. - try: - union = context[node.spelling] - except KeyError: - union = Union(context, node.spelling) - - for child in node.get_children(): - decl = self.handle(child, union) - if decl: - decl.add_to_context(union) - return union - - def handle_class_decl(self, node, context): - # If a class is pre-declared, use the existing declaration, - # rather than overwriting the old one. - try: - klass = context[node.spelling] - except KeyError: - klass = Class(context, node.spelling) - - # To avoid forward declaration issues, run two passes - # over the class. - # - # The first pass picks up any names that might be referred - # to by inline methods (enums, fields, etc) - for child in node.get_children(): - if child.type.kind != TypeKind.FUNCTIONPROTO: - # Handle enums and fields first - decl = self.handle(child, klass) - if decl: - decl.add_to_context(klass) - - # The second pass parses the methods. - for child in node.get_children(): - if child.type.kind == TypeKind.FUNCTIONPROTO: - decl = self.handle(child, klass) - if decl: - decl.add_to_context(klass) - return klass - - def handle_enum_decl(self, node, context): - enum = Enumeration(context, node.spelling) - for child in node.get_children(): - enum.add_enumerator(self.handle(child, enum)) - return enum - - def handle_field_decl(self, node, context): - try: - is_static = node.storage_class == StorageClass.STATIC - - children = node.get_children() - child = next(children) - if child.kind == CursorKind.TYPE_REF: - value = None - else: - value = self.handle(child, context) - - # If the node is of type CONSTANTARRAY, then the field - # is an array; set the value to a tuple of that size. - # Otherwise, ignore the value; You need to be at C++11 - # to be using that feature, anyway. - if node.type.kind == TypeKind.CONSTANTARRAY: - value = Literal(text(tuple(None for i in range(0, int(value.value))))) - else: - value = None - - attr = Attribute(context, node.spelling, value=value, static=is_static) - except StopIteration: - attr = Attribute(context, node.spelling, value=None, static=is_static) - - # A field decl will have param children if the field - # is a function pointer. However, we don't care about - # the arguments; Python will duck type any call. - - return attr - - def handle_enum_constant_decl(self, node, context): - return EnumValue(context, node.spelling, node.enum_value) - - def handle_function_decl(self, node, context): - function = Function(context, node.spelling) - try: - # print("FUNCTION DECL") - children = node.get_children() - child = next(children) - - # If the node has any children, the first children will be the - # return type and namespace for the function declaration. These - # nodes can be ignored. - # print("FIRST CHILD", child.kind) - while child.kind == CursorKind.NAMESPACE_REF: - child = next(children) - # print("NS CHILD", child.kind) - while child.kind == CursorKind.TYPE_REF: - child = next(children) - # print("TYPE REF CHILD", child.kind) - - # Subsequent nodes will be the parameters for the function. - try: - while True: - decl = self.handle(child, function) - if decl: - decl.add_to_context(function) - child = next(children) - except StopIteration: - pass - - except StopIteration: - pass - - # Only return a node if we get function definition. The prototype - # can be ignored. - if function.statements is not None: - return function - - def handle_var_decl(self, node, context): - try: - # print("VAR DECL") - children = node.get_children() - prev_child = None - child = next(children) - # print("FIRST CHILD", child.kind) - - # If there are children, the first children will define the - # namespace and type for the variable being declared (or the context - # for the variable in the case of an assignment). Ignore these - # nodes, and go straight to the actual content. - while child.kind == CursorKind.NAMESPACE_REF: - prev_child = child - child = next(children) - # print("NS CHILD", child.kind, child.spelling) - while child.kind == CursorKind.TYPE_REF: - prev_child = child - child = next(children) - # print("TYPE REF CHILD", child.kind, child.spelling) - - # print("FINAL CHILD", child.kind, child.spelling) - # print("NAMESPACE", namespace) - value = self.handle(child, context) - if prev_child and child.type.kind == TypeKind.RECORD and child.kind == CursorKind.INIT_LIST_EXPR: - value = New(TypeReference(context[prev_child.type.spelling])) - for arg in child.get_children(): - value.add_argument(self.handle(arg, context)) - else: - # Array definitions put the array size first. - # If there is a child, discard the value and - # replace it with the list declaration. - try: - value = self.handle(next(children), context) - except StopIteration: - pass - - # If the current context is a module, then we are either defining a - # global variable, or setting a static constant. If the context is a - # class/struct/union, we're defining a static attribute. The typedef - # captured as the initial child nodes tells us the path to the variable - # being set. - if prev_child and isinstance(context, Module): - full_namespace = prev_child.spelling.split()[-1] - decl_context = context[full_namespace] - # print("DECL CONTEXT", decl_context) - namespace = self.localize_namespace(full_namespace) - else: - namespace = '' - decl_context = context - - if isinstance(context, (Class, Struct, Union)): - # print("ATTR DECL with value %s, %s, %s, %s" % (context, namespace, node.spelling, value)) - return Attribute(context, node.spelling, value=value, static=True) - else: - if isinstance(decl_context, (Class, Struct, Union)): - # print("ATTR ASSIGN with value %s, %s, %s, %s" % (context, namespace, node.spelling, value)) - return BinaryOperation( - AttributeReference(TypeReference(decl_context), node.spelling), - '=', value - ) - else: - # print("VAR DECL with value %s, %s, %s, %s" % (context, namespace, node.spelling, value)) - return Variable(decl_context, namespace + node.spelling, value) - - except StopIteration: - # No initial value for the variable. If the context is a module, - # class, struct or union (i.e., high level block structures) - # it still needs to be declared; use a value of None. - if isinstance(context, Module): - # print("VAR DECL no value %s, %s" % (context, node.spelling)) - return Variable(context, node.spelling, value=None) - elif isinstance(context, (Class, Struct, Union)): - # print("ATTR DECL no value %s, %s" % (context, node.spelling)) - is_static = node.storage_class == StorageClass.STATIC - return Attribute(context, node.spelling, value=None, static=is_static) - else: - # print("pre-decl no value %s, %s" % (context, node.spelling)) - return Variable(context, node.spelling, value=UNDEFINED) - - def handle_parm_decl(self, node, function): - try: - children = node.get_children() - child = next(children) - - # If there are any children, this will be a parameter - # with a default value. The children will be the reference - # to the default value. - # If the default value is a non-primitive type, there will - # be NAMESPACE_REF and TYPE_REF nodes; all but the last one - # can be ignored. - - # Any namespace nodes can be stripped - while child.kind in [CursorKind.NAMESPACE_REF, - CursorKind.TYPE_REF, - CursorKind.TEMPLATE_REF]: - child = next(children) - - # If there is a child, it is the default value of the parameter. - value = self.handle(child, function) - except StopIteration: - value = UNDEFINED - - param = Parameter(function, node.spelling, node.type.spelling, value) - - try: - value = self.handle(next(children), function) - raise Exception("Can't handle multiple children on parameter") - except StopIteration: - pass - - return param - - def handle_typedef_decl(self, node, context): - if self.last_decl is None: - c_type_name = node.underlying_typedef_type.spelling - try: - type_ref = PrimitiveTypeReference({ - 'unsigned': 'int', - 'unsigned byte': 'int', - 'unsigned short': 'int', - 'unsigned int': 'int', - 'unsigned long': 'int', - 'unsigned long long': 'int', - 'signed': 'int', - 'short': 'int', - 'long': 'int', - 'int': 'int', - 'long long': 'int', - 'double': 'float', - 'float': 'float', - }[c_type_name]) - except KeyError: - # Remove any template instantiation from the type. - type_name = re.sub('<.*>', '', c_type_name) - type_ref = TypeReference(context[type_name]) - - return Typedef(context, node.spelling, type_ref) - elif self.last_decl.name: - return Typedef(context, node.spelling, TypeReference(context[self.last_decl.name])) - else: - self.last_decl.name = node.spelling - - def handle_cxx_method(self, node, context): - # If this is an inline method, the context will be the - # enclosing class, and the inline declaration will double as the - # prototype. - # - # If it isn't inline, the context will be a module, and the - # prototype will be separate. In this case, the method will - # be found twice - once as the prototype, and once as the - # definition. Parameters are handled as part of the prototype; - # this handle method only returns a new node when it finds the - # prototype. When the body method is encountered, it finds the - # prototype method (which will be the TYPE_REF in the first - # child node), and adds the body definition. - if isinstance(context, (Class, Struct, Union)): - method = Method(context, node.spelling, node.is_pure_virtual_method(), node.is_static_method()) - is_prototype = True - # print("IS PROTOTYPE") - else: - method = None - # print("NOT PROTOTYPE") - is_prototype = False - - children = node.get_children() - try: - prev_child = None - child = next(children) - - # We can ignore override and final attributes on methods. - while child.kind == CursorKind.CXX_OVERRIDE_ATTR: - prev_child = child - child = next(children) - while child.kind == CursorKind.CXX_FINAL_ATTR: - prev_child = child - child = next(children) - - # Then comes the typedef for the return value. - while child.kind in [CursorKind.NAMESPACE_REF, - CursorKind.TYPE_REF, - CursorKind.TEMPLATE_REF]: - prev_child = child - child = next(children) - - if method is None: - ref = self.handle(prev_child, context) - method = ref.type.methods[node.spelling] - - p = 0 - while True: - # print("CHILD", child.kind, child.spelling) - decl = self.handle(child, method) - if decl: - if is_prototype or child.kind != CursorKind.PARM_DECL: - decl.add_to_context(method) - - # Take the parameter names from the implementation version. - if not is_prototype and child.kind == CursorKind.PARM_DECL: - method.parameters[p].name = decl.name - p += 1 - - child = next(children) - except StopIteration: - pass - - # Add a new node for the prototype. Definitions will - # build on the pre-existing node. - if is_prototype: - return method - - def handle_namespace(self, node, module): - # If the namespace already exists, add to it. - anonymous_ns = not node.spelling - if anonymous_ns: - submodule = module - else: - try: - submodule = module.submodules[node.spelling] - except KeyError: - submodule = Module(node.spelling, context=module) - - # Set the current namespace, and clone the current using list. - self.namespace = submodule - #using = self.using.copy() - - # Process the contents of the namespace - for child in node.get_children(): - decl = self.handle(child, submodule) - if decl: - decl.add_to_context(submodule) - - # Restore the previously active namespace and using list. - self.namespace = module - #self.using = using - - if not anonymous_ns: - return submodule - - # def handle_linkage_spec(self, node, context): - - def handle_constructor(self, node, context): - # If this is an inline constructor, the context will be the - # enclosing class, and the inline declaration will double as the - # prototype. - # - # If it isn't inline, the context will be a module, and the - # prototype will be separate. In this case, the constructor will - # be found twice - once as the prototype, and once as the - # definition. Parameters are handled as part of the prototype; - # this handle method only returns a new node when it finds the - # prototype. When the body method is encountered, it finds the - # prototype constructor (which will be the TYPE_REF in the first - # child node), and adds the body definition. - try: - children = node.get_children() - is_prototype = isinstance(context, (Class, Struct)) - if is_prototype: - constructor = Constructor(context) - - child = next(children) - while child.kind == CursorKind.PARM_DECL: - decl = self.handle(child, constructor) - constructor.add_parameter(decl) - child = next(children) - else: - parameters = [] - prev_child = None - child = next(children) - while child.kind == CursorKind.TYPE_REF: - prev_child = child - child = next(children) - - while child.kind == CursorKind.PARM_DECL: - decl = self.handle(child, context) - parameters.append(decl) - child = next(children) - - signature = tuple(p.ctype for p in parameters) - try: - ref = self.handle(prev_child, context) - constructor = ref.type.constructors[signature] - for cp, p in zip(constructor.parameters, parameters): - cp.name = p.name - except KeyError: - raise Exception("No match for constructor %s; options are %s" % ( - signature, ref.type.constructors.keys()) - ) - - member_ref = None - while True: - decl = self.handle(child, constructor) - if decl: - if child.kind == CursorKind.COMPOUND_STMT: - constructor.add_statement(decl) - elif child.kind == CursorKind.MEMBER_REF: - if member_ref is not None: - raise Exception("Unexpected member reference") - member_ref = decl - elif member_ref is not None: - constructor.add_statement( - BinaryOperation(member_ref, '=', decl) - ) - # Reset the member ref. - member_ref = None - elif child.kind not in (CursorKind.PARM_DECL, CursorKind.TYPE_REF): - # Parm decls have - raise Exception("Don't know how to handle %s in constructor." % child.kind) - child = next(children) - except StopIteration: - pass - - # Only add a new node for the prototype. - if is_prototype: - return constructor - - def handle_destructor(self, node, context): - # If this is an inline destructor, the context will be the - # enclosing class, and the inline declaration will double as the - # prototype. - # - # If it isn't inline, the context will be a module, and the - # prototype will be separate. In this case, the destructor will - # be found twice - once as the prototype, and once as the - # definition. Parameters are handled as part of the prototype; - # this handle method only returns a new node when it finds the - # prototype. When the body method is encountered, it finds the - # prototype destructor (which will be the TYPE_REF in the first - # child node), and adds the body definition. - try: - children = node.get_children() - is_prototype = isinstance(context, (Class, Struct)) - if is_prototype: - destructor = Destructor(context) - child = next(children) - else: - prev_child = None - child = next(children) - while child.kind == CursorKind.TYPE_REF: - prev_child = child - child = next(children) - - try: - ref = self.handle(prev_child, context) - destructor = ref.type.destructor - except KeyError: - raise Exception("No destructor declared on class") - - while True: - decl = self.handle(child, destructor) - if decl: - decl.add_to_context(destructor) - child = next(children) - - except StopIteration: - pass - - # Only add a new node for the prototype. - if is_prototype: - return destructor - - # def handle_conversion_function(self, node, context): - - def handle_template_type_parameter(self, node, context): - # Type paramteres can be ignored; templated types are duck typed - pass - - # def handle_template_non_type_parameter(self, node, context): - # def handle_template_template_parameter(self, node, context): - def handle_function_template(self, node, context): - # Treat a function template like any other function declaration. - # Templated types will be duck typed. - if isinstance(context, Class): - return self.handle_cxx_method(node, context) - else: - return self.handle_function_decl(node, context) - - def handle_class_template(self, node, context): - # Treat a class template like any other class declaration. - # Templated types will be duck typed. - return self.handle_class_decl(node, context) - - # def handle_class_template_partial_specialization(self, node, context): - # def handle_namespace_alias(self, node, context): - def handle_using_directive(self, node, context): - child, ns_context = self.lookup(node.get_children(), context) - if child is not None: - # We've got a node that isn't a namespace - raise Exception("Don't know how to handle node of type %s in using directive" % child.kind) - - context.related_contexts.add(ns_context) - - def handle_using_declaration(self, node, context): - children = node.get_children() - child, ns = self.lookup(children, context) - context.add_using_decl(ns[child.spelling]) - - # def handle_type_alias_decl(self, node, context): - - def handle_cxx_access_spec_decl(self, node, context): - # Ignore access specifiers; everything is public. - pass - - def handle_type_ref(self, node, context): - typename = node.spelling.split()[-1] - return TypeReference(context[typename]) - - def handle_cxx_base_specifier(self, node, context): - typename = node.spelling.split()[1] - context.superclass = TypeReference(context[typename]) - - def handle_template_ref(self, node, context): - typename = node.spelling.split()[-1] - return TypeReference(context[typename]) - - def handle_namespace_ref(self, node, context): - pass - - def handle_member_ref(self, node, context): - try: - children = node.get_children() - child = next(children) - ref = AttributeReference(self.handle(child, context), node.spelling) - except StopIteration: - # An implicit reference to `this` - ref = AttributeReference(SelfReference(), node.spelling) - - try: - next(children) - raise Exception("Member reference has > 1 child node.") - except StopIteration: - pass - return ref - - # def handle_label_ref(self, node, context): - # def handle_overloaded_decl_ref(self, node, context): - # def handle_variable_ref(self, node, context): - # def handle_invalid_file(self, node, context): - # def handle_no_decl_found(self, node, context): - # def handle_not_implemented(self, node, context): - # def handle_invalid_code(self, node, context): - - def handle_unexposed_expr(self, node, statement): - # Ignore unexposed nodes; pass whatever is the first - # (and should be only) child unaltered. - try: - children = node.get_children() - expr = self.handle(next(children), statement) - except StopIteration: - return None - - try: - next(children) - raise Exception("Unexposed expression has > 1 children.") - except StopIteration: - pass - - return expr - - def handle_decl_ref_expr(self, node, context): - children = node.get_children() - namespace = '' - try: - child = next(children) - while child.kind == CursorKind.NAMESPACE_REF: - namespace += child.spelling + '::' - child = next(children) - while child.kind == CursorKind.TYPE_REF: - namespace = child.spelling.split()[-1] + '::' - child = next(children) - except StopIteration: - pass - - try: - child = next(children) - raise Exception("Unexpected %s child node in declaration" % child.type) - except StopIteration: - pass - - var = context[namespace + node.spelling] - if isinstance(var, EnumValue): - return var - else: - return VariableReference(context[namespace + node.spelling], node) - - def handle_member_ref_expr(self, node, context): - try: - children = node.get_children() - first_child = next(children) - ref = AttributeReference(self.handle(first_child, context), node.spelling) - except StopIteration: - # An implicit reference to `this` - ref = AttributeReference(SelfReference(), node.spelling) - try: - next(children) - raise Exception("Member reference expression has > 1 children.") - except StopIteration: - pass - - return ref - - def handle_call_expr(self, node, context): - try: - namespace = "" - children = node.get_children() - child = next(children) - - while child.kind == CursorKind.NAMESPACE_REF: - namespace += child.spelling + '::' - child = next(children) - prev_child = child - while child.kind == CursorKind.TYPE_REF: - child = next(children) - if prev_child.type.kind == TypeKind.RECORD: - # Class typerefs in a call include their own - # namespace definition. - namespace = prev_child.spelling.split()[-1] + '::' - else: - namespace += prev_child.spelling + '::' - prev_child = child - - first_child = self.handle(child, context) - if ((isinstance(first_child, VariableReference) - and first_child.node.type.kind == TypeKind.FUNCTIONPROTO) - or isinstance(first_child, AttributeReference)): - fn = Invoke(first_child) - - for child in children: - arg = self.handle(child, context) - if arg: - fn.add_argument(arg.clean_argument()) - - return fn - else: - # Implicit cast, functional cast, or - # constructor with no args - return first_child - except StopIteration: - return Invoke(TypeReference(context[namespace + node.spelling])) - - # def handle_block_expr(self, node, context): - - def handle_integer_literal(self, node, context): - # In order to preserve source formatting, we need to do some - # special handling. The literal from the AST will be processed - # into a decimal integer, losing all formatting. However, - # the token will be unreliable as a way of getting the token. - # So - get both; if they're numerically equal, use the token - # because it will have preserved formatting. If they aren't - # equal, the token is probably wrong; use the literal. - try: - literal_value = node.literal - token_value = next(node.get_tokens()).spelling - - try: - if int(token_value) == int(literal_value): - value = token_value - else: - raise ValueError() - except ValueError: - try: - if int(token_value, 16) == int(literal_value): - value = token_value - else: - try: - # Try octals - if int(token_value, 8) == int(literal_value): - value = token_value - else: - raise ValueError() - except ValueError: - raise ValueError() - except ValueError: - value = literal_value - except StopIteration: - # No tokens - value = literal_value - - return Literal(value) - - def handle_floating_literal(self, node, context): - try: - literal_value = node.literal - token_value = next(node.get_tokens()).spelling - if float(token_value) == float(literal_value): - value = token_value - else: - value = literal_value - except (StopIteration, ValueError): - # No tokens - value = literal_value - - return Literal(value) - - # def handle_imaginary_literal(self, node, context): - - def handle_string_literal(self, node, context): - return Literal(node.literal) - - def handle_character_literal(self, node, context): - return Literal(node.literal) - - def handle_paren_expr(self, node, context): - try: - children = node.get_children() - parens = Parentheses(self.handle(next(children), context)) - except StopIteration: - raise Exception("Parentheses must contain an expression.") - - try: - next(children) - raise Exception("Parentheses can only contain one expression.") - except StopIteration: - pass - - return parens - - def handle_unary_operator(self, node, context): - try: - children = node.get_children() - child = next(children) - - op = node.unary_operator - - # Dereferencing operator is a pass through. - # All others must be processed as defined. - if op == False: # TODO: Replace false with DEREF - unaryop = self.handle(child, context) - else: - value = self.handle(child, context) - unaryop = UnaryOperation(node.operator, value) - - except StopIteration: - raise Exception("Unary expression requires 1 child node.") - - try: - next(children) - raise Exception("Unary expression has > 1 child node.") - except StopIteration: - pass - - return unaryop - - def handle_array_subscript_expr(self, node, context): - try: - children = node.get_children() - - subject = self.handle(next(children), context) - index = self.handle(next(children), context) - value = ArraySubscript(subject, index) - except StopIteration: - raise Exception("Array subscript requires 2 child nodes.") - - try: - next(children) - raise Exception("Array subscript has > 2 child nodes.") - except StopIteration: - pass - - return value - - def handle_binary_operator(self, node, context): - try: - children = node.get_children() - lnode = next(children) - lvalue = self.handle(lnode, context) - - op = node.operator - - rnode = next(children) - rvalue = self.handle(rnode, context) - - binop = BinaryOperation(lvalue, op, rvalue) - except StopIteration: - raise Exception("Binary operator requires 2 child nodes.") - - try: - next(children) - raise Exception("Binary operator has > 2 child nodes.") - except StopIteration: - pass - - return binop - - def handle_compound_assignment_operator(self, node, context): - try: - children = node.get_children() - - lnode = next(children) - lvalue = self.handle(lnode, context) - - op = node.operator - - rnode = next(children) - rvalue = self.handle(rnode, context) - - binop = BinaryOperation(lvalue, op, rvalue) - except StopIteration: - raise Exception("Binary operator requires 2 child nodes.") - - try: - next(children) - raise Exception("Binary operator has > 2 child nodes.") - except StopIteration: - pass - - return binop - - def handle_conditional_operator(self, node, context): - try: - children = node.get_children() - - condition = self.handle(next(children), context) - true_value = self.handle(next(children), context) - false_value = self.handle(next(children), context) - - condop = ConditionalOperation(condition, true_value, false_value) - except StopIteration: - raise Exception("Conditional operator requires 3 child nodes.") - - try: - next(children) - raise Exception("Conditional operator has > 3 child nodes.") - except StopIteration: - pass - - return condop - - def handle_cstyle_cast_expr(self, node, context): - try: - children = node.get_children() - child = next(children) - - # Consume any namespace or type nodes; they won't be - # used for casting. - while child.kind in (CursorKind.NAMESPACE_REF, CursorKind.TYPE_REF): - child = next(children) - - cast = Cast(node.type.kind, self.handle(child, context)) - except StopIteration: - raise Exception("Cast expression requires 1 child node.") - - try: - next(children) - raise Exception("Cast expression has > 1 child node.") - except StopIteration: - pass - - return cast - - def handle_init_list_expr(self, node, context): - children = node.get_children() - - value = ListLiteral() - for child in children: - value.append(self.handle(child, context)) - - return value - - # def handle_addr_label_expr(self, node, context): - # def handle_stmtexpr(self, node, context): - # def handle_generic_selection_expr(self, node, context): - - def handle_cxx_null_ptr_literal_expr(self, node, context): - return Literal(None) - - def handle_gnu_null_expr(self, node, context): - return Literal(None) - - def handle_cxx_static_cast_expr(self, node, context): - try: - children = node.get_children() - child = next(children) - - # Consume any namespace or type nodes; they won't be - # used for casting. - while child.kind in (CursorKind.NAMESPACE_REF, CursorKind.TYPE_REF): - child = next(children) - - cast = Cast(node.type.kind, self.handle(child, context)) - except StopIteration: - raise Exception("Static cast expression requires 1 child node.") - - try: - next(children) - raise Exception("Static cast expression has > 1 child node.") - except StopIteration: - pass - - return cast - - def handle_cxx_dynamic_cast_expr(self, node, context): - try: - children = node.get_children() - child = next(children) - - # Consume any namespace or type nodes; they won't be - # used for casting. - while child.kind in (CursorKind.NAMESPACE_REF, CursorKind.TYPE_REF): - child = next(children) - - cast = Cast(node.type.kind, self.handle(child, context)) - except StopIteration: - raise Exception("Cast expression requires 1 child node.") - - try: - next(children) - raise Exception("Cast expression has > 1 child node.") - except StopIteration: - pass - - return cast - - def handle_cxx_reinterpret_cast_expr(self, node, context): - try: - children = node.get_children() - child = next(children) - - # Consume any namespace or type nodes; they won't be - # used for casting. - while child.kind in (CursorKind.NAMESPACE_REF, CursorKind.TYPE_REF): - child = next(children) - - cast = Cast(node.type.kind, self.handle(child, context)) - except StopIteration: - raise Exception("Cast expression requires 1 child node.") - - try: - next(children) - raise Exception("Cast expression has > 1 child node.") - except StopIteration: - pass - - return cast - - def handle_cxx_const_cast_expr(self, node, context): - try: - children = node.get_children() - child = next(children) - - # Consume any namespace or type nodes; they won't be - # used for casting. - while child.kind in (CursorKind.NAMESPACE_REF, CursorKind.TYPE_REF): - child = next(children) - - cast = Cast(node.type.kind, self.handle(child, context)) - except StopIteration: - raise Exception("Cast expression requires 1 child node.") - - try: - next(children) - raise Exception("Cast expression has > 1 child node.") - except StopIteration: - pass - - return cast - - def handle_cxx_functional_cast_expr(self, node, context): - try: - children = node.get_children() - - if node.type.kind in ( - TypeKind.CHAR_U, - TypeKind.UCHAR, - TypeKind.CHAR16, - TypeKind.CHAR32, - TypeKind.CHAR_S, - TypeKind.SCHAR, - TypeKind.WCHAR, - TypeKind.USHORT, - TypeKind.UINT, - TypeKind.ULONG, - TypeKind.ULONGLONG, - TypeKind.UINT128, - TypeKind.SHORT, - TypeKind.INT, - TypeKind.LONG, - TypeKind.LONGLONG, - TypeKind.INT128, - TypeKind.FLOAT, - TypeKind.DOUBLE, - TypeKind.LONGDOUBLE, - ): - cast = Cast(node.type.kind, self.handle(next(children), context)) - else: - cast = Invoke(self.handle(next(children), context)) - cast.add_argument(self.handle(next(children), context)) - except StopIteration: - raise Exception("Functional cast requires 2 child nodes.") - - try: - next(children) - raise Exception("Functional cast has > 2 child nodes.") - except StopIteration: - pass - - return cast - - # def handle_cxx_typeid_expr(self, node, context): - def handle_cxx_bool_literal_expr(self, node, context): - content = node.literal - return Literal('True' if content == 'true' else 'False') - - def handle_cxx_this_expr(self, node, context): - return SelfReference() - - # def handle_cxx_throw_expr(self, node, context): - def handle_cxx_new_expr(self, node, context): - children = node.get_children() - - # If the class being instantiated is in a namespace, the - # first children will be namespace nodes; these can be ignored. - child = next(children) - # print("FIRST CHILD", child.kind) - while child.kind == CursorKind.NAMESPACE_REF: - child = next(children) - # print("NS CHILD", child.kind) - - # The next nodes will be typedefs or a template reference describing - # the path to the class being instantiated. In most cases this will be - # a single node; however, in the case of nested classes, there will be - # multiple typedef nodes describing the access path. - while child.kind in (CursorKind.TYPE_REF, CursorKind.TEMPLATE_REF): - type_ref = child - child = next(children) - # print("TYPE CHILD", child.kind) - - # print("FINAL CHILD", child.kind) - new = New(self.handle(type_ref, context)) - - for arg in child.get_children(): - new.add_argument(self.handle(arg, context)) - - return new - - def handle_cxx_delete_expr(self, node, context): - # Delete has no meaning. - pass - # def handle_cxx_unary_expr(self, node, context): - # def handle_pack_expansion_expr(self, node, context): - # def handle_size_of_pack_expr(self, node, context): - # def handle_lambda_expr(self, node, context): - # def handle_unexposed_stmt(self, node, context): - # def handle_label_stmt(self, node, context): - - def handle_compound_stmt(self, node, context): - if context.statements is None: - context.statements = [] - for child in node.get_children(): - statement = self.handle(child, context) - if statement: - context.add_statement(statement) - - def handle_if_stmt(self, node, context): - children = node.get_children() - condition = self.handle(next(children), context) - if_statement = If(condition, context) - - true_term = self.handle(next(children), if_statement.if_true) - if true_term: - if_statement.if_true.add_statement(true_term) - - try: - # There are three possibilities here: - # 1) No false condition (i.e., an if with no else). This - # is caught by the exception handler because there - # will be no child node. - # 2) A bucket 'else' clause. The context passed to the - # handler will have the statements added to it. - # The handler will return None; the block passed - # as context is used as the if_false clause on the If. - # 3) No false condition (i.e., an if with no else). The - # handler will process this as a "sub-if", and return - # the sub-if clause. This clause is used as the - # if_false clause on the If. - if_false = Block(if_statement) - false_term = self.handle(next(children), if_false) - if false_term: - if_statement.if_false = false_term - else: - if_statement.if_false = if_false - except StopIteration: - pass - - try: - next(children) - raise Exception("Unexpected content in if statement") - except StopIteration: - pass - - return if_statement - - # def handle_switch_stmt(self, node, context): - # def handle_case_stmt(self, node, context): - # def handle_default_stmt(self, node, context): - - def handle_while_stmt(self, node, context): - - children = node.get_children() - - - condition = self.handle(next(children), context) - while_statement = While(condition, context) - self.handle(next(children), while_statement.statements) - - try: - next(children) - raise Exception("Unexpected content in while statement") - except StopIteration: - pass - - return while_statement - - def handle_do_stmt(self, node, context): - - children = node.get_children() - - do_statement = Do(context) - self.handle(next(children), do_statement.statements) - do_statement.condition = self.handle(next(children), do_statement.statements) - - try: - next(children) - raise Exception("Unexpected content in do statement") - except StopIteration: - pass - - return do_statement - - def handle_for_stmt(self, node, context): - - children = node.get_children() - child = next(children) - - init_stmt = None - expr_stmt = None - end_stmt = None - - # initial statement - if child.kind == CursorKind.DECL_STMT: - init_stmt = self.handle(child, context) - child = next(children) - - if child.kind == CursorKind.BINARY_OPERATOR: - expr_stmt = self.handle(child, context) - child = next(children) - - if child.kind == CursorKind.UNARY_OPERATOR: - end_stmt = self.handle(child, context) - child = next(children) - - for_statement = For(init_stmt, expr_stmt, end_stmt, context) - - # content - self.handle(child, for_statement.statements) - - try: - next(children) - raise Exception("Unexpected content in for statement") - except StopIteration: - pass - - return for_statement - - - # decl - # end - # increment - # content - - # def handle_goto_stmt(self, node, context): - # def handle_indirect_goto_stmt(self, node, context): - - def handle_continue_stmt(self, node, context): - # for loop cares if there's a continue - end_expr = None - parent = context - while parent and not (isinstance(parent, (For, While, Do))): - parent = parent.context - - if isinstance(parent, For): - end_expr = parent.end_expr - - - return Continue(end_expr) - - def handle_break_stmt(self, node, context): - return Break() - - def handle_return_stmt(self, node, context): - retval = Return() - try: - retval.value = self.handle(next(node.get_children()), context) - except StopIteration: - pass - - return retval - - # def handle_asm_stmt(self, node, context): - # def handle_cxx_catch_stmt(self, node, context): - # def handle_cxx_try_stmt(self, node, context): - # def handle_cxx_for_range_stmt(self, node, context): - # def handle_seh_try_stmt(self, node, context): - # def handle_seh_except_stmt(self, node, context): - # def handle_seh_finally_stmt(self, node, context): - # def handle_ms_asm_stmt(self, node, context): - - def handle_null_stmt(self, node, context): - pass - - def handle_decl_stmt(self, node, context): - try: - children = node.get_children() - statement = self.handle(next(children), context) - except StopIteration: - pass - - try: - self.handle(next(children), context) - raise Exception("Don't know how to handle multiple statements") - except StopIteration: - pass - - return statement - - def handle_translation_unit(self, node, tu): - for child in node.get_children(): - decl = self.handle(child, tu) - if decl: - decl.add_to_context(tu) - - # def handle_unexposed_attr(self, node, context): - # def handle_ib_action_attr(self, node, context): - # def handle_ib_outlet_attr(self, node, context): - # def handle_ib_outlet_collection_attr(self, node, context): - def handle_cxx_final_attr(self, node, context): - # No need to handle final declarations - pass - - def handle_cxx_override_attr(self, node, context): - # No need to handle override declarations - pass - - # def handle_annotate_attr(self, node, context): - # def handle_asm_label_attr(self, node, context): - # def handle_packed_attr(self, node, context): - # def handle_pure_attr(self, node, context): - # def handle_const_attr(self, node, context): - # def handle_noduplicate_attr(self, node, context): - # def handle_cudaconstant_attr(self, node, context): - # def handle_cudadevice_attr(self, node, context): - # def handle_cudaglobal_attr(self, node, context): - # def handle_cudahost_attr(self, node, context): - # def handle_cudashared_attr(self, node, context): - # def handle_visibility_attr(self, node, context): - # def handle_dllexport_attr(self, node, context): - # def handle_dllimport_attr(self, node, context): - # def handle_preprocessing_directive(self, node, context): - - def handle_macro_definition(self, node, context): - pass - - def handle_macro_instantiation(self, node, context): - # self.instantiated_macros[ - # (node.location.file.name, node.location.line, node.location.column) - # ] = self.macros.get(node.spelling, '') - pass - - def handle_inclusion_directive(self, node, context): - # Ignore inclusion directives - pass - - # def handle_module_import_decl(self, node, context): - # def handle_type_alias_template_decl(self, node, context): - - ############################################################ - # Objective-C methods - # If an algorithm exists in Objective C, implementing these - # methods will allow conversion of that code. - ############################################################ - # def handle_objc_synthesize_decl(self, node, context): - # def handle_objc_dynamic_decl(self, node, context): - # def handle_objc_super_class_ref(self, node, context): - # def handle_objc_protocol_ref(self, node, context): - # def handle_objc_class_ref(self, node, context): - # def handle_objc_message_expr(self, node, context): - # def handle_objc_string_literal(self, node, context): - # def handle_objc_encode_expr(self, node, context): - # def handle_objc_selector_expr(self, node, context): - # def handle_objc_protocol_expr(self, node, context): - # def handle_objc_bridge_cast_expr(self, node, context): - # def handle_obj_bool_literal_expr(self, node, context): - # def handle_obj_self_expr(self, node, context): - # def handle_objc_at_try_stmt(self, node, context): - # def handle_objc_at_catch_stmt(self, node, context): - # def handle_objc_at_finally_stmt(self, node, context): - # def handle_objc_at_throw_stmt(self, node, context): - # def handle_objc_at_synchronized_stmt(self, node, context): - # def handle_objc_autorelease_pool_stmt(self, node, context): - # def handle_objc_for_collection_stmt(self, node, context): - # def handle_objc_interface_decl(self, node, context): - # def handle_objc_category_decl(self, node, context): - # def handle_objc_protocol_decl(self, node, context): - # def handle_objc_property_decl(self, node, context): - # def handle_objc_ivar_decl(self, node, context): - # def handle_objc_instance_method_decl(self, node, context): - # def handle_objc_class_method_decl(self, node, context): - # def handle_objc_implementation_decl(self, node, context): - # def handle_objc_category_impl_decl(self, node, context): - - -# A simpler version of Parser that just -# dumps the tree structure. -class CodeDumper(BaseParser): - def __init__(self, verbosity): - super(CodeDumper, self).__init__() - self.verbosity = verbosity - self.filenames = set() - self.ignored_files = set() - - def parse(self, filenames, flags): - abs_filenames = [os.path.abspath(f) for f in filenames] - self.filenames.update(abs_filenames) - source_filenames = [f for f in abs_filenames if os.path.splitext(f)[1] != '.h'] - - self.tu = self.index.parse( - None, - args=source_filenames + flags, - options=TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD - ) - self.diagnostics(sys.stderr) - - self.handle(self.tu.cursor, 0) - - def handle(self, node, depth=0): - if (node.location.file is None - or os.path.abspath(node.location.file.name) in self.filenames): - - debug = [ - ' ' * depth, - node.kind, - '(type:%s | result type:%s)' % (node.type.kind, node.result_type.kind), - node.spelling, - node.location.file, - ] - if self.verbosity > 0: - debug.append([t.spelling for t in node.get_tokens()]) - print(*debug) - - for child in node.get_children(): - self.handle(child, depth + 1) - else: - if node.location.file.name.startswith('/usr/include'): - min_level = 2 - elif node.location.file.name.startswith('/usr/local'): - min_level = 2 - else: - min_level = 1 - - if node.location.file.name not in self.ignored_files: - if self.verbosity >= min_level: - print("Ignoring node in file %s" % node.location.file) - self.ignored_files.add(node.location.file.name) - - -if __name__ == '__main__': - opts = argparse.ArgumentParser( - description='Display AST structure for C++ file.', - ) - - opts.add_argument( - '-I', '--include', - dest='includes', - metavar='/path/to/includes', - help='A directory of includes', - action='append', - default=[] - ) - - opts.add_argument( - '-D', - dest='defines', - metavar='SYMBOL', - help='Preprocessor tokens to use', - action='append', - default=[] - ) - - opts.add_argument( - '-std', - help='The C/C++ standard to use (default: c++0x)', - default='c++0x' - ) - - opts.add_argument( - '-stdlib', - help='The standard library to use (default: libstdc++)', - default='libstdc++' - ) - - opts.add_argument( - '-v', '--verbosity', - action='count', - default=0 - ) - - opts.add_argument( - 'filename', - metavar='file.cpp', - help='The file(s) to dump.', - nargs="+" - ) - - args = opts.parse_args() - - dumper = CodeDumper(verbosity=args.verbosity) - dumper.parse( - args.filename, - flags=[ - '-I%s' % inc for inc in args.includes - ] + [ - '-D%s' % define for define in args.defines - ] + [ - '-std=%s' % args.std - ] + [ - '-stdlib=%s' % args.stdlib - ] - - ) \ No newline at end of file diff --git a/src/plugin.py b/src/plugin.py deleted file mode 100644 index fb0efe0..0000000 --- a/src/plugin.py +++ /dev/null @@ -1,43 +0,0 @@ -import pyflakes.api as api -import requests -from flask import Flask, request -import sys -import os -if not (os.path.dirname(os.path.abspath(__file__)).startswith(sys.path[-1])): - from reporter import Reporter - import pytranslator -else: - from .reporter import Reporter - from . import pytranslator - -def p(): - app = Flask(__name__) - print("The plugin is decreapted. Please use the CLI alongside a Studio+VSCode sync plugin.") - @app.route('/', methods=["GET", "POST"]) - def base_page(): - code = (request.data).decode() - try: - translator = pytranslator.Translator() - lua_code = translator.translate(code) - except Exception as e: - return "CompileError!:"+str(e) - - return lua_code - - @app.route('/err', methods=["GET", "POST"]) - def debug(): - code = (request.data).decode() - rep = Reporter() - num = str(api.check(code, "roblox.py", rep)) - print(num) - return rep.diagnostics - - @app.route("/lib", methods=["GET"]) - def library(): - translator = pytranslator.Translator() - return translator.get_luainit([]) - - app.run( - host='0.0.0.0', - port=5555 - ) \ No newline at end of file diff --git a/src2/pyast.cobalt b/src/pyast.cobalt similarity index 100% rename from src2/pyast.cobalt rename to src/pyast.cobalt diff --git a/src/pytranslator.py b/src/pytranslator.py deleted file mode 100644 index af28e24..0000000 --- a/src/pytranslator.py +++ /dev/null @@ -1,78 +0,0 @@ -"""Python to lua translator class""" -import ast -import os -import sys -import os -if not (os.path.dirname(os.path.abspath(__file__)).startswith(sys.path[-1])): - from config import Config - from nodevisitor import NodeVisitor - from header import header, pyfooter - from luainit import initcode, allfunctions, generatewithlibraries, robloxfunctions -else: - from .config import Config - from .nodevisitor import NodeVisitor - - from .header import header, pyfooter - from .luainit import initcode, allfunctions, generatewithlibraries, robloxfunctions - -class Translator: - """Python to lua main class translator""" - def __init__(self, config=None, show_ast=False): - self.config = config if config is not None else Config() - self.show_ast = show_ast - - self.output = [] - - def translate(self, pycode): - """Translate python code to lua code""" - py_ast_tree = ast.parse(pycode) - - visitor = NodeVisitor(config=self.config) - - if self.show_ast: # - print(ast.dump(py_ast_tree)) - - visitor.visit(py_ast_tree) - - self.output = visitor.output - # check every single line for function calls - functions = [] - for i in range(len(self.to_code().split("\n"))): - # check if a function is being called, like print() - - for function in (allfunctions+robloxfunctions): - if function in self.to_code().split("\n")[i] and function not in functions: - functions.append(function) - - - # create header for function calls - newheader = header(functions) - - return newheader+self.to_code()+pyfooter - - def to_code(self, code=None, indent=0): - """Create a lua code from the compiler output""" - code = code if code is not None else self.output - - def add_indentation(line): - """Add indentation to the given line""" - indentation_width = 4 - indentation_space = " " - - indent_copy = max(indent, 0) - - return indentation_space * indentation_width * indent_copy + line - - lines = [] - for line in code: - if isinstance(line, str): - lines.append(add_indentation(line)) - elif isinstance(line, list): - sub_code = self.to_code(line, indent + 1) - lines.append(sub_code) - - return "\n".join(lines) - - @staticmethod - def get_luainit(items): - return generatewithlibraries(items) diff --git a/src/reporter.py b/src/reporter.py deleted file mode 100644 index 521198e..0000000 --- a/src/reporter.py +++ /dev/null @@ -1,67 +0,0 @@ -import re - -class Reporter: - """ - Formats the results of pyflakes checks to users. - """ - def __init__(self): - self.diagnostics = [] - - def unexpectedError(self, filename, msg): - """ - An unexpected error occurred attemptinh to process C{filename}. - - @param filename: The path to a file that we could not process. - @ptype filename: C{unicode} - @param msg: A message explaining the problem. - @ptype msg: C{unicode} - """ - self.diagnostics.append(f"1{filename}: {msg}\n") - - def syntaxError(self, filename, msg, lineno, offset, text): - """ - There was a syntax error in C{filename}. - - @param filename: The path to the file with the syntax error. - @ptype filename: C{unicode} - @param msg: An explanation of the syntax error. - @ptype msg: C{unicode} - @param lineno: The line number where the syntax error occurred. - @ptype lineno: C{int} - @param offset: The column on which the syntax error occurred, or None. - @ptype offset: C{int} - @param text: The source code containing the syntax error. - @ptype text: C{unicode} - """ - if text is None: - line = None - else: - line = text.splitlines()[-1] - - # lineno might be None if the error was during tokenization - # lineno might be 0 if the error came from stdin - lineno = max(lineno or 0, 1) - - if offset is not None: - # some versions of python emit an offset of -1 for certain encoding errors - offset = max(offset, 1) - self.diagnostics.append('1%s:%d:%d: %s\n' % - (filename, lineno, offset, msg)) - else: - self.diagnostics.append('1%s:%d: %s\n' % (filename, lineno, msg)) - - if line is not None: - self.diagnostics.append(line) - self.diagnostics.append('\n') - if offset is not None: - self.diagnostics.append(re.sub(r'\S', ' ', line[:offset - 1]) + - "^\n") - - def flake(self, message): - """ - pyflakes found something wrong with the code. - - @param: A L{pyflakes.messages.Message}. - """ - self.diagnostics.append("2"+str(message)) - self.diagnostics.append('\n') diff --git a/src/robloxpy.py b/src/robloxpy.py deleted file mode 100644 index 510786d..0000000 --- a/src/robloxpy.py +++ /dev/null @@ -1,1127 +0,0 @@ -"""Handles the main interface""" - -# PYPI -from flask import Flask, request -from pyflakes import api -from packaging import version -from tqdm import tqdm -from time import sleep - -# FILES -if __name__ == "__main__": - from src import colortext #ctranslator is old and not used - - # MODULAR - from errormanager import * - from installationmanager import * - from configmanager import * - from textcompiler import * - from basecompilers import * - from util import * - from plugin import * - from loader import loader - from climaker import newIncli, newIncli2, newLanguage - from wally import wallyget -else: - from . import colortext #ctranslator is old and not used - - # MODULAR - from .errormanager import * - from .installationmanager import * - from .configmanager import * - from .textcompiler import * - from .basecompilers import * - from .util import * - from .plugin import * - from .loader import loader - from .climaker import newIncli, newIncli2, newLanguage - from .wally import wallyget -# BUILTIN -import subprocess,shutil,sys,threading,json,requests,traceback,pkg_resources,re,sys,webbrowser,pickle, os, zipfile - - -registryrawurl = "https://raw.githubusercontent.com/roblox-pyc/registry/main/registry.json" -global count -count = 0 - -try: - registry = json.loads(requests.get(registryrawurl).text) -except json.JSONDecodeError: - print(colortext.white("Import will not work, registry is corrupted. Please report this issue to the github repo, discord server, or the devforum post\nthanks!")) - registry = {} - - - -# TODO: Create a template for the -""" -def w(): - try: - def incli(): - # Get all the files inside of the path, look for all of them which are .py and even check inside of folders. If this is happening in the same directory as the script, do it in the sub directory test - path = os.getcwd() - - #global count - #count = 0 - localcount = 0 - for r, d, f in os.walk(path): - for file in f: - if file.endswith(".py"): - localcount += 1 - newloader = loader(localcount) - - for r, d, f in os.walk(path): - for file in f: - if file.endswith(".py"): - threading.Thread(target=pycompile, args=(r, file, newloader)).start() - #pycompile(r, file) - else: - othercompile(r, file) - - newloader.yielduntil() - - print(colortext.green("Compiled Files!")) - if getconfig("general", "autocompile", False): - action = input("") - if action == "exit": - exit(0) - else: - incli() - - def incli2(): - # Just like incli, but duplicates the direcotry with -compiled and compiles the files in there, also runs filtercompiledfolder() on the directory - path = os.getcwd()+"-compiled" - if os.path.exists(path): - # delete the folder and child files - shutil.rmtree(path) - shutil.copytree(os.getcwd(), path) - - #global count - #count = 0 - localcount = 0 - for r, d, f in os.walk(path): - for file in f: - if file.endswith(".py"): - localcount += 1 - newloader = loader(localcount) - - for r, d, f in os.walk(path): - for file in f: - if file.endswith(".py"): - threading.Thread(target=pycompile, args=(r, file, newloader)).start() - - else: - othercompile(r, file) - newloader.yielduntil() - filtercompiledfolder() - print(colortext.green("Compiled Files!")) - if getconfig("general", "autocompile", False): - action = input("") - if action == "exit": - exit(0) - else: - incli2() - if sys.argv.__len__() >= 1: - if sys.argv[1] == "p": - p() - elif sys.argv[1] == "lib": - # sys.argv[2] is the path to the file, create a new file there with the name robloxpyc.lua, and write the library to it - lib() - elif sys.argv[1] == "c": - decreapted("c") - # Go through every lua descendant file in the current directory and delete it and create a new file with the same name but .py - confirm = input(warn("Are you sure? This will delete all .lua files and add a .py file with the same name.\n\nType 'yes' to continue.")) - if confirm == "yes": - path = os.getcwd() - - for r, d, f in os.walk(path): - for file in f: - if '.lua' in file: - luafilecontents = "" - with open(os.path.join(r, file), "r") as f: - luafilecontents = f.read() - - os.remove(os.path.join(r, file)) - - # create new file with same name but .py and write the lua file contents to it - open(os.path.join(r, file.replace(".lua", ".py")), "x").close() - # write the old file contents as a py comment - open(os.path.join(r, file.replace(".lua", ".py")), "w").write('\"""\n'+luafilecontents+'\n"\""') - print(colortext.green("Converted to py "+os.path.join(r, file)+" as "+file.replace(".lua", ".py"))) - elif sys.argv[1] == "cd": - # Duplicate the cwd directory, the original will be renamed to -compiled and the new one will be renamed to the original. For the new one, go through every lua descendant file in the current directory and delete it and create a new file with the same name but .py - confirm = input(warn("Are you sure? This will duplicate the current directory and compile the files in the new directory.\n\nType 'yes' to continue.")) - if confirm == "yes": - path = os.getcwd()+"/src" - # check if cwd/default.project.json exists, if it does, then use that as the default project file - #if os.path.exists(os.getcwd()+"/default.project.json"): - # formatdefaultproj(os.getcwd()+"/default.project.json") - # rename directory to -compiled - os.rename(path, path+"-compiled") - # duplicate the directory, remove the -compiled from the end - shutil.copytree(path+"-compiled", path) - # now we have 2 identical directories, one with -compiled and one without. for the one without, go through every lua descendant file in the current directory and delete it and create a new file with the same name but .py - path = os.getcwd() - - for r, d, f in os.walk(backwordreplace(path, "-compiled", "", 1)): - for file in f: - if '.lua' in file: - luafilecontents = "" - with open(os.path.join(r, file), "r") as f: - luafilecontents = f.read() - - os.remove(os.path.join(r, file)) - - # create new file with same name but .py and write the lua file contents to it - open(os.path.join(r, file.replace(".lua", ".py")), "x").close() - # write the old file contents as a C++ comment - open(os.path.join(r, file.replace(".lua", ".py")), "w").write("\"\"\"\n"+luafilecontents+"\n\"\"\"") - - print(colortext.green("Converted "+os.path.join(r, file)+" to "+file.replace(".lua", ".py"))) - # create a .rpyc file in the non -compiled directory - open(os.path.join(backwordreplace(path, "-compiled", "", 1), ".rpyc"), "x").close() - # set cwd to not -compiled - os.chdir(backwordreplace(path, "-compiled", "", 1)) - print(info("Completed! You may need to modify the default.package.json or any other equivalent file to make it use the -compiled directory rather than the original.")) - elif sys.argv[1] == "w": - print(colortext.magenta("Ready to compile ", os.path.join(os.path.dirname(os.path.realpath(__file__)))+" ...\n Type 'exit' to exit, Press enter to compile.")) - incli() - elif sys.argv[1] == "d": - print(colortext.magenta("Ready to compile ", os.path.join(os.path.dirname(os.path.realpath(__file__)))+" ...\n Type 'exit' to exit, Press enter to compile.")) - incli2() - - except IndexError: - print(error("Invalid amount of arguments!", "roblox-py")) - except KeyboardInterrupt: - print(colortext.red("Aborted!")) - sys.exit(0) -def cw(): - try: - print(warn("Note, this is not yet completed and will not work and is just a demo to show the AST and very light nodevisitor. A production version will be released soon.")) - def incli(): - #global count - #count = 0 - localcount = 0 - for r, d, f in os.walk(path): - for file in f: - if file.endswith(".c"): - localcount += 1 - newloader = loader(localcount) - path = os.getcwd() - - for r, d, f in os.walk(path): - for file in f: - # check if it ENDS with .c, not if it CONTAINS .c - # Run use threading - if file.endswith(".c"): - localcount += 1 - threading.Thread(target=ccompile, args=(r, file, newloader)).start() - #ccompile(r, file) - else: - othercompile(r, file) - newloader.yielduntil() - print(colortext.green("Compiled Files!")) - if getconfig("general", "autocompile", False): - action = input("") - if action == "exit": - exit(0) - else: - incli() - def incli2(): - # Just like incli, but duplicates the direcotry with -compiled and compiles the files in there, also runs filtercompiledfolder() on the directory - path = os.getcwd()+"-compiled" - if os.path.exists(path): - # delete the folder and child files - shutil.rmtree(path) - shutil.copytree(os.getcwd(), path) - - #global count - #count = 0 - localcount = 0 - for r, d, f in os.walk(path): - for file in f: - if file.endswith(".c"): - localcount += 1 - newloader = loader(localcount) - - for r, d, f in os.walk(path): - for file in f: - if file.endswith(".c"): - threading.Thread(target=ccompile, args=(r, file, newloader)).start() - else: - othercompile(r, file) - newloader.yielduntil() - filtercompiledfolder() - print(colortext.green("Compiled Files!")) - if getconfig("general", "autocompile", False): - action = input("") - if action == "exit": - exit(0) - else: - incli2() - - - if sys.argv.__len__() >= 1: - if sys.argv[1] == "p": - print(error("Plugins are only supported for python!")) - elif sys.argv[1] == "lib": - # sys.argv[2] is the path to the file, create a new file there with the name robloxpyc.lua, and write the library to it - lib() - elif sys.argv[1] == "c": - decreapted("c") - # Go through every lua descendant file in the current directory and delete it and create a new file with the same name but .py - confirm = input(warn("Are you sure? This will delete all .lua files and add a .c file with the same name.\n\nType 'yes' to continue.")) - if confirm == "yes": - path = os.getcwd() - - for r, d, f in os.walk(path): - for file in f: - if '.lua' in file: - luafilecontents = "" - with open(os.path.join(r, file), "r") as f: - luafilecontents = f.read() - - os.remove(os.path.join(r, file)) - - # create new file with same name but .py and write the lua file contents to it - open(os.path.join(r, file.replace(".lua", ".c")), "x").close() - # write the old file contents as a C comment - open(os.path.join(r, file.replace(".lua", ".c")), "w").write("/*\n"+luafilecontents+"\n*/") - print(colortext.green("Converted to c "+os.path.join(r, file)+" as "+file.replace(".lua", ".c"))) - elif sys.argv[1] == "cd": - # Duplicate the cwd directory, the original will be renamed to -compiled and the new one will be renamed to the original. For the new one, go through every lua descendant file in the current directory and delete it and create a new file with the same name but .py - confirm = input(warn("Are you sure? This will duplicate the current directory and compile the files in the new directory.\n\nType 'yes' to continue.")) - if confirm == "yes": - path = os.getcwd()+"/src" - # rename directory to -compiled - os.rename(path, path+"-compiled") - # duplicate the directory, remove the -compiled from the end - shutil.copytree(path+"-compiled", path) - # now we have 2 identical directories, one with -compiled and one without. for the one without, go through every lua descendant file in the current directory and delete it and create a new file with the same name but .py - path = os.getcwd() - - for r, d, f in os.walk(backwordreplace(path, "-compiled", "", 1)): - for file in f: - if '.lua' in file: - luafilecontents = "" - with open(os.path.join(r, file), "r") as f: - luafilecontents = f.read() - - os.remove(os.path.join(r, file)) - - # create new file with same name but .py and write the lua file contents to it - open(os.path.join(r, file.replace(".lua", ".c")), "x").close() - # write the old file contents as a C++ comment - open(os.path.join(r, file.replace(".lua", ".c")), "w").write("/*\n"+luafilecontents+"\n*/") - - print(colortext.green("Converted "+os.path.join(r, file)+" to "+file.replace(".lua", ".c"))) - open(os.path.join(backwordreplace(path, "-compiled", "", 1), ".rpyc"), "x").close() - # set cwd to not -compiled - os.chdir(backwordreplace(path, "-compiled", "", 1)) - print(info("Completed! You may need to modify the default.package.json or any other equivalent file to make it use the -compiled directory rather than the original.")) - elif sys.argv[1] == "w": - print(colortext.magenta("Ready to compile ", os.path.join(os.path.dirname(os.path.realpath(__file__)))+" ...\n Type 'exit' to exit, Press enter to compile.")) - incli() - elif sys.argv[1] == "d": - print(colortext.magenta("Ready to compile ", os.path.join(os.path.dirname(os.path.realpath(__file__)))+" ...\n Type 'exit' to exit, Press enter to compile.")) - incli2() - - except IndexError: - print(error("Invalid amount of arguments!", "roblox-c")) - except KeyboardInterrupt: - print(colortext.red("Aborted!")) - sys.exit(0) -def cpw(): - - try: - print(warn("Note, this is not yet completed and will not work and is just a demo to show the AST and very light nodevisitor. A production version will be released soon.")) - def incli(): - # Get all the files inside of the path, look for all of them which are .py and even check inside of folders. If this is happening in the same directory as the script, do it in the sub directory test - path = os.getcwd() - #global count - #count = 0 - localcount = 0 - for r, d, f in os.walk(path): - for file in f: - if file.endswith(".cpp"): - localcount += 1 - newloader = loader(localcount) - for r, d, f in os.walk(path): - for file in f: - if file.endswith(".cpp"): - # compile the file to a file with the same name and path but .lua - threading.Thread(target=cppcompile, args=(r, file, newloader)).start() - #cppcompile(r, file) - else: - othercompile(r, file) - newloader.yielduntil() - print(colortext.green("Compiled Files!")) - - if getconfig("general", "autocompile", False): - action = input("") - if action == "exit": - exit(0) - else: - incli() - def incli2(): - # Just like incli, but duplicates the direcotry with -compiled and compiles the files in there, also runs filtercompiledfolder() on the directory - path = os.getcwd()+"-compiled" - if os.path.exists(path): - # delete the folder and child files - shutil.rmtree(path) - shutil.copytree(os.getcwd(), path) - - #global count - #count = 0 - localcount = 0 - for r, d, f in os.walk(path): - for file in f: - if file.endswith(".cpp"): - localcount += 1 - newloader = loader(localcount) - - for r, d, f in os.walk(path): - for file in f: - if file.endswith(".cpp"): - threading.Thread(target=cppcompile, args=(r, file, newloader)).start() - else: - othercompile(r, file) - newloader.yielduntil() - filtercompiledfolder() - print(colortext.green("Compiled Files!")) - if getconfig("general", "autocompile", False): - action = input("") - if action == "exit": - exit(0) - else: - incli2() - - - if sys.argv.__len__() >= 1: - if sys.argv[1] == "p": - print(error("Plugins are only supported for python!")) - elif sys.argv[1] == "lib": - # sys.argv[2] is the path to the file, create a new file there with the name robloxpyc.lua, and write the library to it - lib() - elif sys.argv[1] == "c": - decreapted("c") - # Go through every lua descendant file in the current directory and delete it and create a new file with the same name but .py - confirm = input(warn("Are you sure? This will delete all .lua files and add a .cpp file with the same name.\n\nType 'yes' to continue.")) - if confirm == "yes": - path = os.getcwd() - - for r, d, f in os.walk(path): - for file in f: - if '.lua' in file: - luafilecontents = "" - with open(os.path.join(r, file), "r") as f: - luafilecontents = f.read() - - os.remove(os.path.join(r, file)) - - # create new file with same name but .py and write the lua file contents to it - open(os.path.join(r, file.replace(".lua", ".cpp")), "x").close() - # write the old file contents as a C++ comment - open(os.path.join(r, file.replace(".lua", ".cpp")), "w").write("/*\n"+luafilecontents+"\n*/") - - print(colortext.green("Converted to c++ "+os.path.join(r, file)+" as "+file.replace(".lua", ".cpp"))) - elif sys.argv[1] == "cd": - # Duplicate the cwd directory, the original will be renamed to -compiled and the new one will be renamed to the original. For the new one, go through every lua descendant file in the current directory and delete it and create a new file with the same name but .py - confirm = input(warn("Are you sure? This will duplicate the current directory and compile the files in the new directory.\n\nType 'yes' to continue.")) - if confirm == "yes": - path = os.getcwd()+"/src" - # rename directory to -compiled - os.rename(path, path+"-compiled") - # duplicate the directory, remove the -compiled from the end - shutil.copytree(path+"-compiled", path) - # now we have 2 identical directories, one with -compiled and one without. for the one without, go through every lua descendant file in the current directory and delete it and create a new file with the same name but .py - path = os.getcwd() - - for r, d, f in os.walk(backwordreplace(path, "-compiled", "", 1)): - for file in f: - if '.lua' in file: - luafilecontents = "" - with open(os.path.join(r, file), "r") as f: - luafilecontents = f.read() - - os.remove(os.path.join(r, file)) - - # create new file with same name but .py and write the lua file contents to it - open(os.path.join(r, file.replace(".lua", ".cpp")), "x").close() - # write the old file contents as a C++ comment - open(os.path.join(r, file.replace(".lua", ".cpp")), "w").write("/*\n"+luafilecontents+"\n*/") - - print(colortext.green("Converted "+os.path.join(r, file)+" to "+file.replace(".lua", ".cpp"))) - open(os.path.join(backwordreplace(path, "-compiled", "", 1), ".rpyc"), "x").close() - # set cwd to not -compiled - os.chdir(backwordreplace(path, "-compiled", "", 1)) - print(info("Completed! You may need to modify the default.package.json or any other equivalent file to make it use the -compiled directory rather than the original.")) - elif sys.argv[1] == "w": - print(colortext.magenta("Ready to compile ", os.path.join(os.path.dirname(os.path.realpath(__file__)))+" ...\n Type 'exit' to exit, Press enter to compile.")) - incli() - elif sys.argv[1] == "d": - print(colortext.magenta("Ready to compile ", os.path.join(os.path.dirname(os.path.realpath(__file__)))+" ...\n Type 'exit' to exit, Press enter to compile.")) - incli2() - - except IndexError: - print(error("Invalid amount of arguments!", "roblox-cpp")) - except KeyboardInterrupt: - print(colortext.red("Aborted!")) - sys.exit(0) -def lunar(): - try: - checkboth() # install luarocks and moonscript if not installed - def incli(): - # Get all the files inside of the path, look for all of them which are .py and even check inside of folders. If this is happening in the same directory as the script, do it in the sub directory test - path = os.getcwd() - #global count - #count = 0 - localcount = 0 - for r, d, f in os.walk(path): - for file in f: - if file.endswith(".moon"): - localcount += 1 - newloader = loader(localcount) - for r, d, f in os.walk(path): - for file in f: - if file.endswith(".moon"): - localcount += 1 - threading.Thread(target=lunarcompile, args=(r, file, newloader)).start() - #lunarcompile(r, file) - else: - othercompile(r, file) - newloader.yielduntil() - print(colortext.green("Compiled Files!")) - if getconfig("general", "autocompile", False): - action = input("") - if action == "exit": - exit(0) - else: - incli() - def incli2(): - # Just like incli, but duplicates the direcotry with -compiled and compiles the files in there, also runs filtercompiledfolder() on the directory - path = os.getcwd()+"-compiled" - if os.path.exists(path): - # delete the folder and child files - shutil.rmtree(path) - shutil.copytree(os.getcwd(), path) - - #global count - #count = 0 - localcount = 0 - for r, d, f in os.walk(path): - for file in f: - if file.endswith(".moon"): - localcount += 1 - newloader = loader(localcount) - - for r, d, f in os.walk(path): - for file in f: - if file.endswith(".moon"): - threading.Thread(target=lunarcompile, args=(r, file, newloader)).start() - else: - othercompile(r, file) - newloader.yielduntil() - filtercompiledfolder() - print(colortext.green("Compiled Files!")) - if getconfig("general", "autocompile", False): - action = input("") - if action == "exit": - exit(0) - else: - incli2() - if sys.argv.__len__() >= 1: - if sys.argv[1] == "p": - print(error("Plugins are only supported for python!")) - elif sys.argv[1] == "lib": - # sys.argv[2] is the path to the file, create a new file there with the name robloxpyc.lua, and write the library to it - lib() - elif sys.argv[1] == "c": - decreapted("c") - # Go through every lua descendant file in the current directory and delete it and create a new file with the same name but .py - confirm = input(warn("Are you sure? This will delete all .lua files and add a .moon file with the same name.\n\nType 'yes' to continue.")) - if confirm == "yes": - path = os.getcwd() - - for r, d, f in os.walk(path): - for file in f: - if '.lua' in file: - luafilecontents = "" - with open(os.path.join(r, file), "r") as f: - luafilecontents = f.read() - - os.remove(os.path.join(r, file)) - - # create new file with same name but .py and write the lua file contents to it - open(os.path.join(r, file.replace(".lua", ".moon")), "x").close() - # write the old file contents as a C++ comment - open(os.path.join(r, file.replace(".lua", ".moon")), "w").write("--[[\n"+luafilecontents+"\n]]") - - print(colortext.green("Converted to lunar "+os.path.join(r, file)+" as "+file.replace(".lua", ".moon"))) - elif sys.argv[1] == "w": - print(colortext.magenta("Ready to compile ", os.path.join(os.path.dirname(os.path.realpath(__file__)))+" ...\n Type 'exit' to exit, Press enter to compile.")) - incli() - elif sys.argv[1] == "cd": - # Duplicate the cwd directory, the original will be renamed to -compiled and the new one will be renamed to the original. For the new one, go through every lua descendant file in the current directory and delete it and create a new file with the same name but .py - confirm = input(warn("Are you sure? This will duplicate the current directory and compile the files in the new directory.\n\nType 'yes' to continue.")) - if confirm == "yes": - path = os.getcwd()+"/src" - # rename directory to -compiled - os.rename(path, path+"-compiled") - # duplicate the directory, remove the -compiled from the end - shutil.copytree(path+"-compiled", path) - # now we have 2 identical directories, one with -compiled and one without. for the one without, go through every lua descendant file in the current directory and delete it and create a new file with the same name but .py - path = os.getcwd() - - for r, d, f in os.walk(backwordreplace(path, "-compiled", "", 1)): - for file in f: - if '.lua' in file: - luafilecontents = "" - with open(os.path.join(r, file), "r") as f: - luafilecontents = f.read() - - os.remove(os.path.join(r, file)) - - # create new file with same name but .py and write the lua file contents to it - open(os.path.join(r, file.replace(".lua", ".moon")), "x").close() - # write the old file contents as a C++ comment - open(os.path.join(r, file.replace(".lua", ".moon")), "w").write("--[[\n"+luafilecontents+"\n]]") - - print(colortext.green("Converted "+os.path.join(r, file)+" to "+file.replace(".lua", ".moon"))) - open(os.path.join(backwordreplace(path, "-compiled", "", 1), ".rpyc"), "x").close() - # set cwd to not -compiled - os.chdir(backwordreplace(path, "-compiled", "", 1)) - print(info("Completed! You may need to modify the default.package.json or any other equivalent file to make it use the -compiled directory rather than the original.")) - elif sys.argv[1] == "d": - print(colortext.magenta("Ready to compile ", os.path.join(os.path.dirname(os.path.realpath(__file__)))+" ...\n Type 'exit' to exit, Press enter to compile.")) - incli2() - - except IndexError: - print(error("Invalid amount of arguments!", "roblox-lunar")) - except KeyboardInterrupt: - print(colortext.red("Aborted!")) - sys.exit(0) -def globalincli(): - # Get all the files inside of the path, look for all of them which are .py and even check inside of folders. If this is happening in the same directory as the script, do it in the sub directory test - path = os.getcwd() - #global count - #count = 0 - localcount = 0 - for r, d, f in os.walk(path): - for file in f: - if file.endswith(".moon") or file.endswith(".py") or file.endswith(".ts") or file.endswith(".tsx"): #or file.endswith(".c") or file.endswith(".cpp"): - localcount += 1 - newloader = loader(localcount) - for r, d, f in os.walk(path): - for file in f: - if file.endswith(".moon"): - localcount += 1 - threading.Thread(target=lunarcompile, args=(r, file, newloader)).start() - #lunarcompile(r, file) - elif file.endswith(".py"): - localcount += 1 - threading.Thread(target=pycompile, args=(r, file, newloader)).start() - elif file.endswith(".ts") or file.endswith(".tsx"): - threading.Thread(target=robloxtscompile, args=(r, file, newloader)).start() - else: - othercompile(r, file) - newloader.yielduntil() - print(colortext.green("Compiled Files!")) - action = input("") - if action == "exit": - exit(0) - else: - globalincli() -def globalincli2(): - # Just like incli, but duplicates the direcotry with -compiled and compiles the files in there, also runs filtercompiledfolder() on the directory - path = os.getcwd()+"-compiled" - if os.path.exists(path): - # delete the folder and child files - shutil.rmtree(path) - shutil.copytree(os.getcwd(), path) - - #global count - #count = 0 - localcount = 0 - for r, d, f in os.walk(path): - for file in f: - if file.endswith(".moon") or file.endswith(".py") or file.endswith(".ts") or file.endswith(".tsx"): #or file.endswith(".c") or file.endswith(".cpp"): - localcount += 1 - newloader = loader(localcount) - - for r, d, f in os.walk(path): - for file in f: - if file.endswith(".moon"): - threading.Thread(target=lunarcompile, args=(r, file, newloader)).start() - elif file.endswith(".py"): - localcount += 1 - threading.Thread(target=pycompile, args=(r, file, newloader)).start() - elif file.endswith(".ts") or file.endswith(".tsx"): - threading.Thread(target=robloxtscompile, args=(r, file, newloader)).start() - else: - othercompile(r, file) - newloader.yielduntil() - filtercompiledfolder() - print(colortext.green("Compiled Files!")) - action = input("") - if action == "exit": - exit(0) - else: - globalincli2() -""" - - -w = newLanguage(".py", pycompile, '"""%s"""') -cw = newLanguage(".c", ccompile, '/*%s*/') -cpw = newLanguage(".cpp", cppcompile, '/*%s*/') -lunar = newLanguage(".moon", lunarcompile, '--[[%s]]') - -globalincli = newIncli(["py", "c", "cpp", "moon"], allcompile) -globalincli2 = newIncli2(["py", "c", "cpp", "moon"], allcompile) - -def pyc(): - title = colortext.magenta("roblox-pyc", ["bold"]) - py = colortext.blue("roblox-py", ["bold"]) - c = colortext.blue("roblox-c", ["bold"]) - cpp = colortext.blue("roblox-cpp", ["bold"]) - lunar = colortext.blue("roblox-lunar", ["bold"]) - border = colortext.white("--------------------") - blank = colortext.blue("roblox-", ["bold"])+colortext.magenta("") - blank2 = colortext.blue("rblx-", ["bold"])+colortext.magenta("") - blankpath = colortext.magenta("") - selftool = colortext.blue("roblox-pyc", ["bold"]) - shortselftool = colortext.blue("rblx-pyc", ["bold"]) - shorterselftool = colortext.blue("rpyc", ["bold"]) - print(str(sys.argv)) - try: - if sys.argv[1] == "config": - # Open config menu - print(f""" -Config menu -{border} -1 - {py} -2 - {c} -3 - {cpp} -4 - {lunar} -5 - General - """) - returnval = input("Select which config to open: ") - - if returnval == "1": - print(f"{py} doesnt need to be configured!") - elif returnval == "2": - print(f""" -Configuring {c} -{border} -1 - Change std -2 - Change stdlib - """) - - inputval = input("Select which config to open: ") - if inputval == "1": - returned = input("Enter the std, it currently is %s: " % getconfig("c", "std", "c11")) - setconfig("c", "std", returned, "c11") - elif inputval == "2": - returned = input("Enter the stdlib, it currently is %s: " % getconfig("c", "stdlib", "libc")) - setconfig("c", "stdlib", returned, "libc") - elif returnval == "3": # - print(f""" -Configuring {cpp} -{border} -1 - Change std -2 - Change stdlib - """) - - inputval = input("Select which config to open: ") - if inputval == "1": - returned = input("Enter the std, it currently is %s: " % getconfig("cpp", "std", "c++11")) - setconfig("cpp", "std", returned, "c++11") - elif inputval == "2": - returned = input("Enter the stdlib, it currently is %s: " % getconfig("cpp", "stdlib", "libc++")) - setconfig("cpp", "stdlib", returned, "libc++") - elif returnval == "4": - print(f"{lunar} doesnt need to be configured!") - elif returnval == "5": - print(f""" -Configuring General Settings -{border} -1 - Change C and C++ dylib -2 - Enable or diable autocompile - """) - inputval = input("Select which config to open: ") - if inputval == "1": - returned = input("Enter the dynamic library file, it currently is %s: " % getconfig("c", "dynamiclibpath", "None")) - setconfig("c", "dynamiclibpath", returned, "None") - elif inputval == "2": - returned = input("Would you like to turn autocompile (on/off)") - if returned == "on": - setconfig("general", "autocompile", True, False) - elif returned == "off": - setconfig("general", "autocompile", False, False) - else: - print(error("not a valid option!")) - elif returnval == "6": - colortext.nil(colortext.rainbow_text("Welcome to the secret menu!")) - print("This contains many developer options and some goofy ones too!") - print(f""" - 1 - Traceback on error (Reccomended off, for roblox-pyc developers) - 2 - TTS on error (macOS only) - """) - print(f"Select which", end=" ") - colortext.rainbow_text("secret", end=" ") - inputval = input("config to open: ") - if inputval == "1": - returned = input("Click enter to confirm, CTRL+C to cancel: ") - setconfig("general", "traceback", returned, None) - elif inputval == "2": - if not sys.platform == "darwin": - print(error("I ALREADY TOLD YOU, THIS IS MACOS ONLY!")) - return - setconfig("general", "goofy", returned, None) - elif inputval == "3": - colortext.nil(colortext.rainbow_text("Welcome to the secret secret menu, sadly this is empty for now! :(")) - else: - print(error("Aw man, you didnt select a valid option!")) - else: - print(error("Invalid option!")) - elif sys.argv[1] == "devforum": - webbrowser.open("https://devforum.com") - elif sys.argv[1] == "discord": - webbrowser.open("https://discord.gg/RAXYEjj3") - elif sys.argv[1] == "github": - webbrowser.open("https://github.com/AsynchronousAI/roblox-pyc") - elif sys.argv[1] == "help": - raise IndexError - elif sys.argv[1] == "w": - print(colortext.magenta("Ready to compile ", os.path.join(os.path.dirname(os.path.realpath(__file__)))+" ...\n Type 'exit' to exit, Press enter to compile.")) - globalincli() - elif sys.argv[1] == "d" or sys.argv[1] == ".": - print(colortext.magenta("Ready to compile ", os.path.join(os.path.dirname(os.path.realpath(__file__)))+" ...\n Type 'exit' to exit, Press enter to compile.")) - globalincli2() - elif sys.argv[1] == "format": - print(warn("Are you sure, this will reformat the compiled directory and delete uncompatible files, this is for when a bug occured.", "roblox-pyc")) - if input("Type 'yes' to continue: ") == "yes": - # Delete all files that arent .py, .moon, .lua, .c, .cpp - filtercompiledfolder() - print(colortext.green("Deleted all uncompatible files!")) - elif sys.argv[1] == "p": - print(error("Plugins are only supported for python!", "roblox-pyc")) - elif sys.argv[1] == "lib": - # sys.argv[2] is the path to the file, create a new file there with the name robloxpyc.lua, and write the library to it - lib() - elif sys.argv[1] == "c": - decreapted("c") - print(error("Cannot replace all files for a general language, please specify a language by using rbxpy, rbxlun, rbxc, rbxcpp", "roblox-pyc")) - elif sys.argv[1] == "cd": - print(error("Cannot replace all files for a general language, please specify a language by using rbxpy, rbxlun, rbxc, rbxcpp", "roblox-pyc")) - elif sys.argv[1] == "tsc": - # Pretty much c but for typescript and works - if not check_roblox_ts(): - if not check_npms(): - install_npms() - install_roblox_ts() - confirm = input(warn("Are you sure? This will delete all .lua files and add a .ts file with the same name.\n\nType 'yes' to continue.")) - if confirm == "yes": - path = os.getcwd()+"/src" - - for r, d, f in os.walk(path): - for file in f: - if '.lua' in file: - luafilecontents = "" - with open(os.path.join(r, file), "r") as f: - luafilecontents = f.read() - - os.remove(os.path.join(r, file)) - - # create new file with same name but .py and write the lua file contents to it - open(os.path.join(r, file.replace(".lua", ".ts")), "x").close() - # write the old file contents as a C++ comment - open(os.path.join(r, file.replace(".lua", ".ts")), "w").write("/*\n"+luafilecontents+"\n*/") - - print(colortext.green("Converted to typescript "+os.path.join(r, file)+" as "+file.replace(".lua", ".moon"))) - elif sys.argv[1] == "info": - subprocess.call(["pip", "show", "roblox-pyc"]) - check_for_updates() - elif sys.argv[1] == "install": - # Check registry for package - if sys.argv[2] in registry: - # Find out how to install, cli or package - item = registry[sys.argv[2]] - type = item["type"] - - if type == "cli": - if True: - print(error("CLI packages are not supported on this build!", "roblox-pyc")) - return - # git clone the files to this scripts path - print(colortext.green("Installing "+sys.argv[2]+" ...")) - selfpath = os.path.dirname(os.path.realpath(__file__)) - # create new dir in selfpath called sys.argv[2] - os.mkdir(os.path.join(selfpath, sys.argv[2])) - - subprocess.call(["git", "clone", item["url"], os.path.join(selfpath, sys.argv[2])]) - - # add to config - newlist = getconfig("general", "cli", []) - newlist.append({"name": sys.argv[2], "path": os.path.join(selfpath, sys.argv[2]), "mainscript": item["mainscript"], "target": item["target"], "command": item["command"]}) - elif type == "luaext": - # save to config the name and url data after request - print(colortext.green("Fetching "+sys.argv[2]+" ...")) - newlist = getconfig("general", "luaext", []) - packagedata = json.loads(requests.get(item["url"]).text) - fileurl = packagedata["file"] - - newlist.append({"name": sys.argv[2], "data": requests.get(fileurl).text, "var": packagedata["outputvar"]}) - setconfig("general", "luaext", newlist, []) - print(colortext.green("Fetched "+sys.argv[2]+"!")) - elif type == "package": - # Create new folder in cwd called dependencies if it doesnt exist - print(colortext.green("Installing "+sys.argv[2]+" ...")) - exists = os.path.exists(os.path.join(os.getcwd(), "dependencies")) - if not exists: - lib() - - # Gitclone item["url"] to dependencies folder - subprocess.call(["git", "clone", item["url"], os.path.join(os.getcwd(), "dependencies", sys.argv[2])]) - print(colortext.green("Installed "+sys.argv[2]+"!")) - elif sys.argv[2].startswith("@") and not sys.argv[2].startswith("@rbxts"): - author = sys.argv[2].split("/")[0].replace("@", "") - name = sys.argv[2].split("/")[1] - wallyget(author, name) - elif sys.argv[2].startswith("@rbxts"): - # Use NPM - print(warn("Fetching from NPM is still not stable, you might get missing files or files in the wrong place.")) - if not check_npms(): - install_npms() - # Install to /dependencies not /node_modules - subprocess.call(["npm", "install", sys.argv[2], "--prefix=dependencies"]) - # Delete dependencies/package-lock.json and dependencies/package.json. Use try except - try: - os.remove(os.path.join(os.getcwd(), "dependencies", "package-lock.json")) - except: - pass - try: - os.remove(os.path.join(os.getcwd(), "dependencies", "package.json")) - except: - pass - try: - os.remove(os.path.join(os.getcwd(), "dependencies", ".package.json")) - except: - pass - try: - os.remove(os.path.join(os.getcwd(), "dependencies", ".package-lock.json")) - except: - pass - - # Unpack dependencies/node_modules to dependencies - for r, d, f in os.walk(os.path.join(os.getcwd(), "dependencies", "node_modules")): - for file in f: - shutil.move(os.path.join(r, file), os.path.join(os.getcwd(), "dependencies")) - # Delete dependencies/node_modules - shutil.rmtree(os.path.join(os.getcwd(), "dependencies", "node_modules")) - # If a dependency/.package.json exists or a dependency/.package-lock.json exists, delete it - try: - os.remove(os.path.join(os.getcwd(), "dependencies", ".package.json")) - except: - pass - try: - os.remove(os.path.join(os.getcwd(), "dependencies", ".package-lock.json")) - except: - pass - try: - os.remove(os.path.join(os.getcwd(), "dependencies", "package-lock.json")) - except: - pass - try: - os.remove(os.path.join(os.getcwd(), "dependencies", "package.json")) - except: - pass - - - else: - print(warn("roblox-pyc: Package not in registry!")+"\n\n"+info("If you are trying to install from Wally enter the package name as @/")+"\n\n"+info("If you are trying to install from roblox-ts enter the package name as @rbxts/")+"\nInstall from one of these other package managers:") - print(""" - 1 - luarocks - 2 - pip (compiles to lua) - 3 - pip3 (compiles to lua) - 5 - None - """) - returnval = input("Select which package manager to use: ") - if returnval == "1": - # install to dependencies folder - if not check_luarocks(): - install_luarocks() - subprocess.call(["luarocks", "install", sys.argv[2], "--tree=dependencies"]) - elif returnval == "2": - # install to dependencies folder - subprocess.call(["pip", "install", sys.argv[2], "--target=dependencies", "--upgrade"]) - # compile the newly added directory to lua - elif returnval == "3": - # install to dependencies folder - subprocess.call(["pip3", "install", sys.argv[2], "--target=dependencies", "--upgrade"]) - # compile the newly added directory to lua - else: - print("Invalid option or exited.") - return - print("Compiling to luau...") - #global count - #count = 0 - endcount = 0 - for r, d, f in os.walk(os.path.join(os.getcwd(), "dependencies")): - for file in f: - if file.endswith(".py") or file.endswith(".moon"): - endcount+=1 - newloader = loader(endcount) - for r, d, f in os.walk(os.path.join(os.getcwd(), "dependencies")): - # if dir name is __pycache__ delete it - if os.path.basename(r) == "__pycache__": - # clear children - for child in os.listdir(r): - try: - os.remove(os.path.join(r, child)) - except: - pass - try: - os.rmdir(r) - except: - pass - - for file in f: - if file.endswith(".py"): - endcount+=1 - threading.Thread(target=pycompile, args=(r, file, newloader)).start() - - # delete old file - os.remove(os.path.join(r, file)) - elif file.endswith(".moon"): - endcount+=1 - threading.Thread(target=lunarcompile, args=(r, file, newloader)).start() - - # delete old file - os.remove(os.path.join(r, file)) - elif file.endswith(".c") or file.endswith(".cpp"): - candcpperror() - else: - othercompile(r, file) - - - - newloader.yielduntil() - print("Successfully installed "+sys.argv[2]+"!") - print(warn("Since these modules are from 3rd party sources, they may not work in the roblox environment and you may encounter errors, this is feauture is experimental and any issues in your code caused by this is not our fault.")) - - elif sys.argv[1] == "uninstall": - # Find out how to install, cli or package - item = registry[sys.argv[2]] - type = item["type"] - - # for cli do nothing, for luaext remove from config, for package remove from dependencies folder - if type == "cli": - pass - elif type == "luaext": - currentlist = getconfig("general", "luaext", []) - # remove, if not found error - found = False - - for i in currentlist: - if i["name"] == sys.argv[2]: - currentlist.remove(i) - found = True - if not found: - print(error("Module not found!", "roblox-pyc")) - else: - setconfig("general", "luaext", currentlist, []) - print(colortext.green("Uninstalled "+sys.argv[2]+"!")) - elif type == "package": - dependenciesPath = os.path.join(os.getcwd(), "dependencies") - # if it doesnt exist error - if not os.path.exists(dependenciesPath): - print(error("Dependencies folder not found! Creating one now...", "roblox-py")) - lib() - return - # remove, if not found error - if not os.path.exists(os.path.join(dependenciesPath, sys.argv[2])): - print(error("Package not found!", "roblox-py")) - - if os.path.exists(os.path.join(dependenciesPath, sys.argv[2])): - shutil.rmtree(os.path.join(dependenciesPath, sys.argv[2])) - print(colortext.green("roblox-pyc: Uninstalled "+sys.argv[2]+"!")) - elif sys.argv[1] == "list": - # First list all items in config - print(colortext.green("Extensions:")) - for i in getconfig("general", "luaext", []): - print((" - "+i["name"])) - - print(colortext.green("Packages:")) - # Then list all items in dependencies folder - dependenciesPath = os.path.join(os.getcwd(), "dependencies") - if not os.path.exists(dependenciesPath): - print(warn("roblox-pyc: Dependencies folder not found! Creating one now...")) - lib() - return - for i in os.listdir(dependenciesPath): - print((" - "+i)) - else: - raise IndexError - except IndexError: - print(f""" -{title} -{border} - CLIs: - - {py} - Python to Lua | Best for a functional and simple language - - {c} - C to Lua | Best for learning a more complicated language for fun and educational purposes - - {cpp} - C++ to Lua | Best for learning a more complicated OOP language for fun and educational purposes - - {lunar} - Lunar to Lua | Best for learning a language with some great syntax sugar - {border} - NOTES: - - {py} is the only one that supports plugins, and it supports full python 3.13, which is a dev build of python - - {c} and {cpp} are only capable of light conversions, and are not capable of converting complex code at the time of writing. - - {lunar} is based off MoonScript, and is completed and reccomended if you want a really nice language with good syntax sugar. - - I would highly reccomend {py} and {lunar} for production use over ideal lua, as they are much more powerful and easier to use. - - At the moment lunar is the exact same as moonscript, but adding roblox specific features is planned. - - rpyc and rblx-pyc can be used rather than roblox-pyc, they are just shorter versions of the name. - {border} - CLI DOCS: - - {blank} w - Click enter in the terminal to compile all scripts - - {blank} p - Start the plugin server (only for {py}) - - {blank} lib - Generate dependencies folder with stdlib - - {blank} c - Convert all .lua files to targeted language files, it will comment the existing lua code - - {blank2} w - Click enter in the terminal to compile all scripts - - {blank2} p - Start the plugin server (only for {py}) - - {blank2} lib - Generate dependencies folder with stdlib - - {blank2} c - Convert all .lua files to targeted language files, it will comment the existing lua code - - {selftool} config - Open the config menu - - {selftool} devforum - Open the devforum page in a browser - - {selftool} discord - Open the discord server in a browser - - {selftool} github - Open the github page in a browser - - {selftool} install - Install a item from the registry. Read docs for more info - - {selftool} uninstall - Uninstall a item from the registry. Read docs for more info - - {selftool} list - List all installed packages - {border} - MORE HELP: - - Devforum - - Discord - - Github Issues - - """) - except KeyboardInterrupt: - print(colortext.red("Aborted!")) - sys.exit(0) - - -if __name__ == "__main__": - print(colortext.blue("Test mode")) - mode = input("Select which module to run (1, 2, 3, 4, 5): ") - if os.environ.get("ENABLE_BETA_RPYC"): - print(colortext.magenta("important")+" the following code will be run in beta mode, robloxts support, C(++) support, etc. will all be enabled.") - - if mode == "1": - w() - elif mode == "2": - cw() - elif mode == "3": - cpw() - elif mode == "4": - lunar() - else: - pyc() - diff --git a/src/symbolsstack.py b/src/symbolsstack.py deleted file mode 100644 index 0747757..0000000 --- a/src/symbolsstack.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Class for the symbols stack""" - - -class SymbolsStack: - """Class for the symbols stack""" - def __init__(self): - self.symbols = [[]] - - def add_symbol(self, name): - """Add a new symbol to the curent stack""" - self.symbols[-1].append(name) - - def exists(self, name): - """Check symbol is exists in the current stack""" - for stack in self.symbols: - if name in stack: - return True - return False - - def push(self): - """Push the symbols stack""" - self.symbols.append([]) - - def pop(self): - """Pop the symbols stack""" - self.symbols.pop() diff --git a/src2/template.cobalt b/src/template.cobalt similarity index 100% rename from src2/template.cobalt rename to src/template.cobalt diff --git a/src2/template/.gitignore b/src/template/.gitignore similarity index 100% rename from src2/template/.gitignore rename to src/template/.gitignore diff --git a/src2/template/README.md b/src/template/README.md similarity index 100% rename from src2/template/README.md rename to src/template/README.md diff --git a/src2/template/compiled/client/init.client.example b/src/template/compiled/client/init.client.example similarity index 100% rename from src2/template/compiled/client/init.client.example rename to src/template/compiled/client/init.client.example diff --git a/src2/template/compiled/server/init.server.example b/src/template/compiled/server/init.server.example similarity index 100% rename from src2/template/compiled/server/init.server.example rename to src/template/compiled/server/init.server.example diff --git a/src2/template/compiled/shared/Hello.example b/src/template/compiled/shared/Hello.example similarity index 100% rename from src2/template/compiled/shared/Hello.example rename to src/template/compiled/shared/Hello.example diff --git a/src2/template/default.project.json b/src/template/default.project.json similarity index 100% rename from src2/template/default.project.json rename to src/template/default.project.json diff --git a/src2/template/src/client/init.client.lua b/src/template/src/client/init.client.lua similarity index 100% rename from src2/template/src/client/init.client.lua rename to src/template/src/client/init.client.lua diff --git a/src2/template/src/server/init.server.lua b/src/template/src/server/init.server.lua similarity index 100% rename from src2/template/src/server/init.server.lua rename to src/template/src/server/init.server.lua diff --git a/src2/template/src/shared/Hello.lua b/src/template/src/shared/Hello.lua similarity index 100% rename from src2/template/src/shared/Hello.lua rename to src/template/src/shared/Hello.lua diff --git a/src2/test.cobalt b/src/test.cobalt similarity index 100% rename from src2/test.cobalt rename to src/test.cobalt diff --git a/src/textcompiler.py b/src/textcompiler.py deleted file mode 100644 index 154b440..0000000 --- a/src/textcompiler.py +++ /dev/null @@ -1,80 +0,0 @@ -import json -import os -import sys -import os -if not (os.path.dirname(os.path.abspath(__file__)).startswith(sys.path[-1])): - from errormanager import * - from util import * -else: - from .errormanager import * - from .util import * - - -def json_to_lua(json_str): - data = json.loads(json_str) - return _json_to_lua(data) - -def _json_to_lua(data): - if isinstance(data, dict): - items = [] - for key, value in data.items(): - items.append('[{}] = {}'.format(_json_to_lua(key), _json_to_lua(value))) - return '{{{}}}'.format(', '.join(items)) - elif isinstance(data, list): - items = [] - for value in data: - items.append(_json_to_lua(value)) - return '{{{}}}'.format(', '.join(items)) - elif isinstance(data, str): - return '"{}"'.format(data) - else: - return str(data) - - -def unknowncompile(r, file): - if file.endswith(".txt") or ("." not in file): - # This is for text files and files without a file extension - try: - contents = "" - with open(os.path.join(r, file), "r") as f: - contents = f.read() - contents = contents.replace("]]", "]\]") - contents = "--/ Compiled using roblox-pyc | Textfile compiler \--\nlocal file\nfile = {Contents = [["+contents+"]], Type = 'rawtext', Extension = '"+file.split(".")[file.split(".").__len__()-1]+"', SetSource = function(self, input) self.Contents = input end}" - filename = os.path.basename(file) - sepratedbydot = filename.split(".") - ending = sepratedbydot[sepratedbydot.__len__()-1] - newfilename = filename.replace("."+ending, ".lua") - # if newfilename == oldfilename, add .lua to the end. For files without endings - if filename == newfilename: - newfilename = newfilename+".lua" - - open(os.path.join(r, newfilename), "x").close() - with open(os.path.join(r, newfilename), "w") as f: - f.write(contents) - except UnicodeDecodeError: - print(warn("Failed to read "+os.path.join(r, file)+"!")) -def jsoncompile(r, file): - if file.endswith(".json"): - # compile the file to a file with the same name and path but .lua - try: - contents = "" - with open(os.path.join(r, file), "r") as f: - contents = f.read() - contents = "--/ Compiled using roblox-pyc | JSON compiler \--\nreturn {Contents = "+json_to_lua(contents)+", Type = 'json', Extension = 'json', SetSource = function(self, input) self.Contents = input end}" - filename = os.path.basename(file) - sepratedbydot = filename.split(".") - ending = sepratedbydot[sepratedbydot.__len__()-1] - newfilename = filename.replace("."+ending, ".lua") - # if newfilename == oldfilename, add .lua to the end. For files without endings - if filename == newfilename: - newfilename = newfilename+".lua" - if not os.path.exists(os.path.dirname(os.path.join(r, newfilename))): - open(os.path.join(r, newfilename), "x").close() - with open(os.path.join(r, newfilename), "w") as f: - f.write(contents) - except UnicodeDecodeError: - # Just delete the file - print(warn("Failed to read "+os.path.join(r, file)+"!")) -def othercompile(r, file): # Handles Text files and JSON files, and files without a file extension - jsoncompile(r, file) - unknowncompile(r, file) \ No newline at end of file diff --git a/src/tokenendmode.py b/src/tokenendmode.py deleted file mode 100644 index 9af9de9..0000000 --- a/src/tokenendmode.py +++ /dev/null @@ -1,8 +0,0 @@ -"""Token end mode""" -from enum import Enum - - -class TokenEndMode(Enum): - """This enum represents token end mode""" - LINE_FEED = 0 - LINE_CONTINUE = 1 diff --git a/src/unaryopdesc.py b/src/unaryopdesc.py deleted file mode 100644 index c558d66..0000000 --- a/src/unaryopdesc.py +++ /dev/null @@ -1,28 +0,0 @@ -"""Unary operation description""" -import ast - - -_DEFAULT_FORMAT = "{operation}{value}" - - -class UnaryOperationDesc: - """Unary operation description""" - - OPERATION = { - ast.USub: { - "value": "-", - "format": _DEFAULT_FORMAT, - }, - ast.UAdd: { - "value": "", - "format": _DEFAULT_FORMAT, - }, - ast.Not: { - "value": "not", - "format": "not {value}", - }, - ast.Invert: { - "value": "~", - "format": "bit32.bnot({value})", - }, - } diff --git a/src2/util.cobalt b/src/util.cobalt similarity index 100% rename from src2/util.cobalt rename to src/util.cobalt diff --git a/src/util.py b/src/util.py deleted file mode 100644 index 703ae0f..0000000 --- a/src/util.py +++ /dev/null @@ -1,47 +0,0 @@ -"""Holds many useful functions for roblox-pyc""" -import sys -import os -if not (os.path.dirname(os.path.abspath(__file__)).startswith(sys.path[-1])): - import colortext, configmanager, pytranslator -else: - from . import colortext, configmanager, pytranslator -import os - -def backwordreplace(s, old, new, occurrence): - li = s.rsplit(old, occurrence) - return new.join(li) -def filtercompiledfolder(): - cwd = os.getcwd() - compiled = cwd+"-compiled" - for r, d, f in os.walk(compiled): - for file in f: - if not file.endswith(".lua"): - os.remove(os.path.join(r, file)) -def lib(): - # if /server and /client are found, then error - if os.path.exists(os.path.join(os.getcwd(), "server")) and os.path.exists(os.path.join(os.getcwd(), "client")): - print(colortext.warn("Do not install dependencies inside of the parent folder, rather both the /server and /client folder. Would you like to do this?")) - inputval = input("[Y/n]: ").lower() - if inputval == "n": - sys.exit() - elif inputval == "y": - # Set cwd to server and run lib - os.chdir(os.path.join(os.getcwd(), "server")) - lib() - # set cwd to client and run lib - os.chdir(os.path.join(os.getcwd(), "..", "client")) - lib() - # create dependencies folder if it doesnt exist - if not os.path.exists(os.path.join(os.getcwd(), "dependencies")): - os.makedirs(os.path.join(os.getcwd(), "dependencies")) - - cwd = os.getcwd() - # cwd+sys.argv[2] - dir = os.path.join(cwd,"dependencies", "stdlib.lua") - if not os.path.exists(os.path.dirname(dir)): - open(dir, "x").close() - with open(dir, "w") as f: - translator = pytranslator.Translator() - f.write(translator.get_luainit(configmanager.getconfig("general", "luaext", []))) - # Make a file called content.json in the dependencies folder - open(os.path.join(cwd, "dependencies", "content.json"), "x").close() \ No newline at end of file diff --git a/src/wally.py b/src/wally.py deleted file mode 100644 index df589e6..0000000 --- a/src/wally.py +++ /dev/null @@ -1,59 +0,0 @@ -"""Handles downloading packages from wally.""" - -import sys, os, json, requests, zipfile -if not (os.path.dirname(os.path.abspath(__file__)).startswith(sys.path[-1])): - from errormanager import * -else: - from .errormanager import * - -def wallyget(author, name, isDependant=False): - # Use wally and download the zip and unpack it - print(info(f"Getting @{author}/{name} metadata", "roblox-pyc wally")) - wallyurl = "https://api.wally.run/v1/" - - # first get package metadata should look like: - metadataurl = wallyurl+"/package-metadata/"+author+"/"+name - data = requests.get(metadataurl).text - jsondata = json.loads(data) - - if 'message' in jsondata: - print(error(jsondata["message"])) - print(error("Exiting process", "roblox-pyc wally")) - sys.exit() - jsondata = json.loads(data)["versions"] - #get latest version and dependencies - latestver = jsondata[0] - vernum = latestver["package"]["version"] - dependencies = latestver["dependencies"] - - for i in dependencies: - dependency = dependencies[i] - print("Downloading dependency "+dependency+"...") - wallyget(dependency.split("/")[0], dependency.split("/")[1].split("@")[0], True) - - # Download the package - if not isDependant: - print("\n"*2) - print("Dependencies downloaded, now downloading package...") - print(info(f"Downloading @{author}/{name} v{vernum}", "roblox-pyc wally")) - url = wallyurl+"/package-contents/"+author+"/"+name+"/"+vernum - headers = {"Wally-Version": "1.0.0"} - response = requests.get(url, headers=headers).content - - # create new file in cwd/dependencies called author_name_version.zip and unzip it - print(info("Saving package...", "roblox-pyc wally")) - #### if dependencies folder doesnt exist, create it - if not os.path.exists(os.path.join(os.getcwd(), "dependencies")): - os.makedirs(os.path.join(os.getcwd(), "dependencies")) - #### - open(os.path.join(os.getcwd(), "dependencies", "@"+author+"/"+name+".zip"), "x").close() - with open(os.path.join(os.getcwd(), "dependencies", "@"+author+"/"+name+".zip"), "wb") as file: - file.write(response) - # unzip - print(info("Unzipping package...", "roblox-pyc wally")) - with zipfile.ZipFile(os.path.join(os.getcwd(), "dependencies", "@"+author+"/"+name+".zip"), 'r') as zip_ref: - zip_ref.extractall(os.path.join(os.getcwd(), "dependencies", "@"+author+"/"+name)) - # delete the zip - print(info("Deleting uneeded resources...", "roblox-pyc wally")) - os.remove(os.path.join(os.getcwd(), "dependencies", "@"+author+"/"+name+".zip")) - diff --git a/src/writer.py b/src/writer.py deleted file mode 100644 index 9302bee..0000000 --- a/src/writer.py +++ /dev/null @@ -1,60 +0,0 @@ -########################################################################### -# Code Writer -# -# This is a helper that can be used to write code; it knows how to -# maintain the right number of spaces between code blocks to remain PEP8 -# compliant. -########################################################################### -from __future__ import unicode_literals, print_function - - -class CodeWriter(object): - def __init__(self, out, preamble=None): - self.out = out - self.line_cleared = True - self.blank_lines = 2 - self.depth = 0 - self.empty = True - - if preamble: - self.out.write(preamble) - - def write(self, content): - if not self.empty: - for i in range(0, self.blank_lines): - self.out.write('\n') - self.blank_lines = 0 - if content: - if self.line_cleared: - self.out.write(' ' * self.depth) - self.out.write(content) - self.empty = False - self.line_cleared = False - - def clear_line(self): - if not self.line_cleared: - self.out.write('\n') - self.line_cleared = True - self.blank_lines = 0 - - def clear_minor_block(self): - if not self.line_cleared: - self.out.write('\n') - self.line_cleared = True - while self.blank_lines < 1: - self.blank_lines += 1 - - def clear_major_block(self): - if not self.line_cleared: - self.out.write('\n') - self.line_cleared = True - while self.blank_lines < max(1, 2 - self.depth): - self.blank_lines += 1 - - def start_block(self): - self.empty = True - self.depth += 1 - self.blank_lines = 2 - - def end_block(self): - self.depth -= 1 \ No newline at end of file diff --git a/src2/bridge/CMakeLists.txt b/src2/bridge/CMakeLists.txt deleted file mode 100644 index 38c58a9..0000000 --- a/src2/bridge/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -add_library(src OBJECT luainpython.c pythoninlua.c) -set_target_properties(src PROPERTIES - POSITION_INDEPENDENT_CODE TRUE) - -target_include_directories(src PRIVATE ${LUA_INCLUDE_DIR} ${PYTHON_INCLUDE_DIR}) - -target_compile_definitions(src PRIVATE LUA_LIB) -if (WIN32) - target_compile_definitions(src PRIVATE LUA_BUILD_AS_DLL) -endif (WIN32) - -if (UNIX) - get_filename_component(PYTHON_LIBRT ${PYTHON_LIBRARIES} NAME) - target_compile_definitions(src PRIVATE PYTHON_LIBRT=${PYTHON_LIBRT}) -endif (UNIX) - -if (CMAKE_COMPILER_IS_GNUCC) - target_compile_options(src PUBLIC -Wall -pedantic -std=c99) -endif (CMAKE_COMPILER_IS_GNUCC) diff --git a/src2/bridge/luainpython.c b/src2/bridge/luainpython.c deleted file mode 100644 index c55c6ab..0000000 --- a/src2/bridge/luainpython.c +++ /dev/null @@ -1,596 +0,0 @@ -/* - - Lunatic Python - -------------- - - Copyright (c) 2002-2005 Gustavo Niemeyer - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ -#include - -/* need this to build with Lua 5.2: enables lua_strlen() macro */ -#define LUA_COMPAT_ALL - -#include -#include -#include -#include - -#include "pythoninlua.h" -#include "luainpython.h" - -lua_State *LuaState = NULL; - -static PyObject *LuaObject_New(lua_State *L, int n) -{ - LuaObject *obj = PyObject_New(LuaObject, &LuaObject_Type); - if (obj) - { - lua_pushvalue(L, n); - obj->ref = luaL_ref(L, LUA_REGISTRYINDEX); - obj->refiter = 0; - } - return (PyObject*) obj; -} - -PyObject *LuaConvert(lua_State *L, int n) -{ - - PyObject *ret = NULL; - - switch (lua_type(L, n)) { - - case LUA_TNIL: - Py_INCREF(Py_None); - ret = Py_None; - break; - - case LUA_TSTRING: { - size_t len; - const char *s = lua_tolstring(L, n, &len); - ret = PyUnicode_FromStringAndSize(s, len); - if (!ret) - { - PyErr_Clear(); - ret = PyBytes_FromStringAndSize(s, len); - } - break; - } - - case LUA_TNUMBER: { - lua_Number num = lua_tonumber(L, n); - if (num != (long)num) { - ret = PyFloat_FromDouble(num); - } else { - ret = PyLong_FromLong((long)num); - } - break; - } - - case LUA_TBOOLEAN: - ret = lua_toboolean(L, n) ? Py_True : Py_False; - Py_INCREF(ret); - break; - - case LUA_TUSERDATA: { - py_object *obj = luaPy_to_pobject(L, n); - - if (obj) { - Py_INCREF(obj->o); - ret = obj->o; - break; - } - - /* Otherwise go on and handle as custom. */ - } - - default: - ret = LuaObject_New(L, n); - break; - } - - return ret; -} - -static PyObject *LuaCall(lua_State *L, PyObject *args) -{ - PyObject *ret = NULL; - PyObject *arg; - int nargs, rc, i; - - if (!PyTuple_Check(args)) { - PyErr_SetString(PyExc_TypeError, "tuple expected"); - lua_settop(L, 0); - return NULL; - } - - nargs = PyTuple_Size(args); - for (i = 0; i != nargs; i++) { - arg = PyTuple_GetItem(args, i); - if (arg == NULL) { - PyErr_Format(PyExc_TypeError, - "failed to get tuple item #%d", i); - lua_settop(L, 0); - return NULL; - } - rc = py_convert(L, arg); - if (!rc) { - PyErr_Format(PyExc_TypeError, - "failed to convert argument #%d", i); - lua_settop(L, 0); - return NULL; - } - } - - if (lua_pcall(L, nargs, LUA_MULTRET, 0) != 0) { - PyErr_Format(PyExc_Exception, - "error: %s", lua_tostring(L, -1)); - return NULL; - } - - nargs = lua_gettop(L); - if (nargs == 1) { - ret = LuaConvert(L, 1); - if (!ret) { - PyErr_SetString(PyExc_TypeError, - "failed to convert return"); - lua_settop(L, 0); - Py_DECREF(ret); - return NULL; - } - } else if (nargs > 1) { - ret = PyTuple_New(nargs); - if (!ret) { - PyErr_SetString(PyExc_RuntimeError, - "failed to create return tuple"); - lua_settop(L, 0); - return NULL; - } - for (i = 0; i != nargs; i++) { - arg = LuaConvert(L, i+1); - if (!arg) { - PyErr_Format(PyExc_TypeError, - "failed to convert return #%d", i); - lua_settop(L, 0); - Py_DECREF(ret); - return NULL; - } - PyTuple_SetItem(ret, i, arg); - } - } else { - Py_INCREF(Py_None); - ret = Py_None; - } - - lua_settop(L, 0); - - return ret; -} - -static void LuaObject_dealloc(LuaObject *self) -{ - luaL_unref(LuaState, LUA_REGISTRYINDEX, self->ref); - if (self->refiter) - luaL_unref(LuaState, LUA_REGISTRYINDEX, self->refiter); - Py_TYPE(self)->tp_free((PyObject *)self); -} - -static PyObject *LuaObject_getattr(PyObject *obj, PyObject *attr) -{ - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject*)obj)->ref); - if (lua_isnil(LuaState, -1)) { - lua_pop(LuaState, 1); - PyErr_SetString(PyExc_RuntimeError, "lost reference"); - return NULL; - } - - if (!lua_isstring(LuaState, -1) - && !lua_istable(LuaState, -1) - && !lua_isuserdata(LuaState, -1)) - { - lua_pop(LuaState, 1); - PyErr_SetString(PyExc_RuntimeError, "not an indexable value"); - return NULL; - } - - PyObject *ret = NULL; - int rc = py_convert(LuaState, attr); - if (rc) { - lua_gettable(LuaState, -2); - ret = LuaConvert(LuaState, -1); - } else { - PyErr_SetString(PyExc_ValueError, "can't convert attr/key"); - } - lua_settop(LuaState, 0); - return ret; -} - -static int LuaObject_setattr(PyObject *obj, PyObject *attr, PyObject *value) -{ - int ret = -1; - int rc; - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject*)obj)->ref); - if (lua_isnil(LuaState, -1)) { - lua_pop(LuaState, 1); - PyErr_SetString(PyExc_RuntimeError, "lost reference"); - return -1; - } - if (!lua_istable(LuaState, -1)) { - lua_pop(LuaState, -1); - PyErr_SetString(PyExc_TypeError, "Lua object is not a table"); - return -1; - } - rc = py_convert(LuaState, attr); - if (rc) { - if (NULL == value) { - lua_pushnil(LuaState); - rc = 1; - } else { - rc = py_convert(LuaState, value); - } - - if (rc) { - lua_settable(LuaState, -3); - ret = 0; - } else { - PyErr_SetString(PyExc_ValueError, - "can't convert value"); - } - } else { - PyErr_SetString(PyExc_ValueError, "can't convert key/attr"); - } - lua_settop(LuaState, 0); - return ret; -} - -static PyObject *LuaObject_str(PyObject *obj) -{ - PyObject *ret = NULL; - const char *s; - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject*)obj)->ref); - if (luaL_callmeta(LuaState, -1, "__tostring")) { - s = lua_tostring(LuaState, -1); - lua_pop(LuaState, 1); - if (s) ret = PyUnicode_FromString(s); - } - if (!ret) { - int type = lua_type(LuaState, -1); - switch (type) { - case LUA_TTABLE: - case LUA_TFUNCTION: - ret = PyUnicode_FromFormat("", - lua_typename(LuaState, type), - lua_topointer(LuaState, -1)); - break; - - case LUA_TUSERDATA: - case LUA_TLIGHTUSERDATA: - ret = PyUnicode_FromFormat("", - lua_typename(LuaState, type), - lua_touserdata(LuaState, -1)); - break; - - case LUA_TTHREAD: - ret = PyUnicode_FromFormat("", - lua_typename(LuaState, type), - (void*)lua_tothread(LuaState, -1)); - break; - - default: - ret = PyUnicode_FromFormat("", - lua_typename(LuaState, type)); - break; - - } - } - lua_pop(LuaState, 1); - return ret; -} - -#if LUA_VERSION_NUM == 501 -enum -{ - LUA_OK, LUA_OPEQ, LUA_OPLT, LUA_OPLE, -}; -static int lua_compare(lua_State *L, int lhs, int rhs, int op) -{ - switch(op) - { - case LUA_OPEQ: - return lua_equal(L, lhs, rhs); - case LUA_OPLT: - return lua_lessthan(L, lhs, rhs); - case LUA_OPLE: - return lua_lessthan(L, lhs, rhs) || lua_equal(L, lhs, rhs); - } - return 0; -} -#endif -static int LuaObject_pcmp(lua_State *L) -{ - int op = lua_tointeger(L, -3); - switch(op) - { - case Py_EQ: - lua_pushboolean(L, lua_compare(L, -2, -1, LUA_OPEQ)); - break; - case Py_NE: - lua_pushboolean(L, !lua_compare(L, -2, -1, LUA_OPEQ)); - break; - case Py_GT: - lua_insert(LuaState, -2); - case Py_LT: - lua_pushboolean(L, lua_compare(L, -2, -1, LUA_OPLT)); - break; - case Py_GE: - lua_insert(LuaState, -2); - case Py_LE: - lua_pushboolean(L, lua_compare(L, -2, -1, LUA_OPLE)); - } - - return 1; -} - -static PyObject* LuaObject_richcmp(PyObject *lhs, PyObject *rhs, int op) -{ - if (!LuaObject_Check(rhs)) return Py_False; - - lua_pushcfunction(LuaState, LuaObject_pcmp); - lua_pushinteger(LuaState, op); - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject *)lhs)->ref); - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject *)rhs)->ref); - if (lua_pcall(LuaState, 3, 1, 0) != LUA_OK) - { - PyErr_SetString(PyExc_RuntimeError, lua_tostring(LuaState, -1)); - return NULL; - } - return lua_toboolean(LuaState, -1) ? Py_True : Py_False; -} - -static PyObject *LuaObject_call(PyObject *obj, PyObject *args) -{ - lua_settop(LuaState, 0); - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject*)obj)->ref); - return LuaCall(LuaState, args); -} - -static PyObject *LuaObject_iternext(LuaObject *obj) -{ - PyObject *ret = NULL; - - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject*)obj)->ref); - - if (obj->refiter == 0) - lua_pushnil(LuaState); - else - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, obj->refiter); - - if (lua_next(LuaState, -2) != 0) { - /* Remove value. */ - lua_pop(LuaState, 1); - ret = LuaConvert(LuaState, -1); - /* Save key for next iteration. */ - if (!obj->refiter) - obj->refiter = luaL_ref(LuaState, LUA_REGISTRYINDEX); - else - lua_rawseti(LuaState, LUA_REGISTRYINDEX, obj->refiter); - } else if (obj->refiter) { - luaL_unref(LuaState, LUA_REGISTRYINDEX, obj->refiter); - obj->refiter = 0; - } - - return ret; -} - -static int LuaObject_length(LuaObject *obj) -{ - int len; - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject*)obj)->ref); - len = luaL_len(LuaState, -1); - lua_settop(LuaState, 0); - return len; -} - -static PyObject *LuaObject_subscript(PyObject *obj, PyObject *key) -{ - return LuaObject_getattr(obj, key); -} - -static int LuaObject_ass_subscript(PyObject *obj, - PyObject *key, PyObject *value) -{ - return LuaObject_setattr(obj, key, value); -} - -static PyMappingMethods LuaObject_as_mapping = { -#if PY_VERSION_HEX >= 0x02050000 - (lenfunc)LuaObject_length, /*mp_length*/ -#else - (inquiry)LuaObject_length, /*mp_length*/ -#endif - (binaryfunc)LuaObject_subscript,/*mp_subscript*/ - (objobjargproc)LuaObject_ass_subscript,/*mp_ass_subscript*/ -}; - -PyTypeObject LuaObject_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "lua.custom", /*tp_name*/ - sizeof(LuaObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)LuaObject_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - LuaObject_str, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - &LuaObject_as_mapping, /*tp_as_mapping*/ - 0, /*tp_hash*/ - (ternaryfunc)LuaObject_call, /*tp_call*/ - LuaObject_str, /*tp_str*/ - LuaObject_getattr, /*tp_getattro*/ - LuaObject_setattr, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - "custom lua object", /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - LuaObject_richcmp, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - PyObject_SelfIter, /*tp_iter*/ - (iternextfunc)LuaObject_iternext, /*tp_iternext*/ - 0, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ - 0, /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - 0, /*tp_dictoffset*/ - 0, /*tp_init*/ - PyType_GenericAlloc, /*tp_alloc*/ - PyType_GenericNew, /*tp_new*/ - PyObject_Del, /*tp_free*/ - 0, /*tp_is_gc*/ -}; - - -PyObject *Lua_run(PyObject *args, int eval) -{ - PyObject *ret; - char *buf = NULL; - char *s; - int len; - - if (!PyArg_ParseTuple(args, "s#", &s, &len)) - return NULL; - - if (eval) { - buf = (char *) malloc(strlen("return ")+len+1); - strcpy(buf, "return "); - strncat(buf, s, len); - s = buf; - len = strlen("return ")+len; - } - - if (luaL_loadbuffer(LuaState, s, len, "") != 0) { - PyErr_Format(PyExc_RuntimeError, - "error loading code: %s", - lua_tostring(LuaState, -1)); - free(buf); - return NULL; - } - - free(buf); - - if (lua_pcall(LuaState, 0, 1, 0) != 0) { - PyErr_Format(PyExc_RuntimeError, - "error executing code: %s", - lua_tostring(LuaState, -1)); - return NULL; - } - - ret = LuaConvert(LuaState, -1); - lua_settop(LuaState, 0); - return ret; -} - -PyObject *Lua_execute(PyObject *self, PyObject *args) -{ - return Lua_run(args, 0); -} - -PyObject *Lua_eval(PyObject *self, PyObject *args) -{ - return Lua_run(args, 1); -} - -PyObject *Lua_globals(PyObject *self, PyObject *args) -{ - PyObject *ret = NULL; - lua_getglobal(LuaState, "_G"); - if (lua_isnil(LuaState, -1)) { - PyErr_SetString(PyExc_RuntimeError, - "lost globals reference"); - lua_pop(LuaState, 1); - return NULL; - } - ret = LuaConvert(LuaState, -1); - if (!ret) - PyErr_Format(PyExc_TypeError, - "failed to convert globals table"); - lua_settop(LuaState, 0); - return ret; -} - -static PyObject *Lua_require(PyObject *self, PyObject *args) -{ - lua_getglobal(LuaState, "require"); - if (lua_isnil(LuaState, -1)) { - lua_pop(LuaState, 1); - PyErr_SetString(PyExc_RuntimeError, "require is not defined"); - return NULL; - } - return LuaCall(LuaState, args); -} - -static PyMethodDef lua_methods[] = -{ - {"execute", Lua_execute, METH_VARARGS, NULL}, - {"eval", Lua_eval, METH_VARARGS, NULL}, - {"globals", Lua_globals, METH_NOARGS, NULL}, - {"require", Lua_require, METH_VARARGS, NULL}, - {NULL, NULL} -}; - -#if PY_MAJOR_VERSION >= 3 -static struct PyModuleDef lua_module = -{ - PyModuleDef_HEAD_INIT, - "lua", - "Lunatic-Python Python-Lua bridge", - -1, - lua_methods -}; -#endif - -PyMODINIT_FUNC PyInit_lua(void) -{ - PyObject *m; - if (PyType_Ready(&LuaObject_Type) < 0 || -#if PY_MAJOR_VERSION >= 3 - (m = PyModule_Create(&lua_module)) == NULL) - return NULL; -#else - (m = Py_InitModule3("lua", lua_methods, - "Lunatic-Python Python-Lua bridge")) == NULL) - return; -#endif - - if (!LuaState) - { - LuaState = luaL_newstate(); - luaL_openlibs(LuaState); - luaopen_python(LuaState); - lua_settop(LuaState, 0); - } - -#if PY_MAJOR_VERSION >= 3 - return m; -#endif -} diff --git a/src2/bridge/luainpython.h b/src2/bridge/luainpython.h deleted file mode 100644 index 73ea65c..0000000 --- a/src2/bridge/luainpython.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - - Lunatic Python - -------------- - - Copyright (c) 2002-2005 Gustavo Niemeyer - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ -#ifndef LUAINPYTHON_H -#define LUAINPYTHON_H - -#if LUA_VERSION_NUM == 501 - #define luaL_len lua_objlen - #define luaL_setfuncs(L, l, nup) luaL_register(L, NULL, (l)) - #define luaL_newlib(L, l) (lua_newtable(L), luaL_register(L, NULL, (l))) -#endif - -typedef struct -{ - PyObject_HEAD - int ref; - int refiter; -} LuaObject; - -extern PyTypeObject LuaObject_Type; - -#define LuaObject_Check(op) PyObject_TypeCheck(op, &LuaObject_Type) - -PyObject* LuaConvert(lua_State *L, int n); - -extern lua_State *LuaState; - -#if PY_MAJOR_VERSION < 3 -# define PyInit_lua initlua -#endif -PyMODINIT_FUNC PyInit_lua(void); - -#endif diff --git a/src2/bridge/pythoninlua.c b/src2/bridge/pythoninlua.c deleted file mode 100644 index cbfc433..0000000 --- a/src2/bridge/pythoninlua.c +++ /dev/null @@ -1,675 +0,0 @@ -/* - - Lunatic Python - -------------- - - Copyright (c) 2002-2005 Gustavo Niemeyer - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ -#include -#if defined(__linux__) -# include -#endif - -/* need this to build with Lua 5.2: defines luaL_register() macro */ -#define LUA_COMPAT_MODULE - -#include -#include - -#include "pythoninlua.h" -#include "luainpython.h" - -static int py_asfunc_call(lua_State *); -static int py_eval(lua_State *); - -static int py_convert_custom(lua_State *L, PyObject *o, int asindx) -{ - py_object *obj = (py_object*) lua_newuserdata(L, sizeof(py_object)); - if (!obj) - luaL_error(L, "failed to allocate userdata object"); - - Py_INCREF(o); - obj->o = o; - obj->asindx = asindx; - luaL_getmetatable(L, POBJECT); - lua_setmetatable(L, -2); - - return 1; -} - -int py_convert(lua_State *L, PyObject *o) -{ - int ret = 0; - if (o == Py_None) - { - /* Not really needed, but this way we may check - * for errors with ret == 0. */ - lua_pushnil(L); - ret = 1; - } else if (o == Py_True) { - lua_pushboolean(L, 1); - ret = 1; - } else if (o == Py_False) { - lua_pushboolean(L, 0); - ret = 1; - } else if (PyUnicode_Check(o) || PyBytes_Check(o)) { - PyObject *bstr = PyUnicode_AsEncodedString(o, "utf-8", NULL); - Py_ssize_t len; - char *s; - - PyErr_Clear(); - PyBytes_AsStringAndSize(bstr ? bstr : o, &s, &len); - lua_pushlstring(L, s, len); - if (bstr) Py_DECREF(bstr); - ret = 1; -#if PY_MAJOR_VERSION < 3 - } else if (PyInt_Check(o)) { - lua_pushnumber(L, (lua_Number)PyInt_AsLong(o)); - ret = 1; -#endif - } else if (PyLong_Check(o)) { - lua_pushnumber(L, (lua_Number)PyLong_AsLong(o)); - ret = 1; - } else if (PyFloat_Check(o)) { - lua_pushnumber(L, (lua_Number)PyFloat_AsDouble(o)); - ret = 1; - } else if (LuaObject_Check(o)) { - lua_rawgeti(L, LUA_REGISTRYINDEX, ((LuaObject*)o)->ref); - ret = 1; - } else { - int asindx = PyDict_Check(o) || PyList_Check(o) || PyTuple_Check(o); - ret = py_convert_custom(L, o, asindx); - } - return ret; -} - -static int py_object_call(lua_State *L) -{ - PyObject *args; - PyObject *value; - PyObject *pKywdArgs = NULL; - int nargs = lua_gettop(L)-1; - int ret = 0; - int i; - py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); - assert(obj); - - if (!PyCallable_Check(obj->o)) - return luaL_error(L, "object is not callable"); - - // passing a single table forces named keyword call style, e.g. plt.plot{x, y, c='red'} - if (nargs==1 && lua_istable(L, 2)) { - lua_pushnil(L); /* first key */ - nargs=0; - while (lua_next(L, 2) != 0) { - if (lua_isnumber(L, -2)) { - int i = lua_tointeger(L, -2); - nargs = i < nargs ? nargs : i; - } - lua_pop(L, 1); - } - args = PyTuple_New(nargs); - if (!args) { - PyErr_Print(); - return luaL_error(L, "failed to create arguments tuple"); - } - pKywdArgs = PyDict_New(); - if (!pKywdArgs) { - Py_DECREF(args); - PyErr_Print(); - return luaL_error(L, "failed to create arguments dictionary"); - } - lua_pushnil(L); /* first key */ - while (lua_next(L, 2) != 0) { - if (lua_isnumber(L, -2)) { - PyObject *arg = LuaConvert(L, -1); - if (!arg) { - Py_DECREF(args); - Py_DECREF(pKywdArgs); - return luaL_error(L, "failed to convert argument #%d", lua_tointeger(L, -2)); - } - PyTuple_SetItem(args, lua_tointeger(L, -2)-1, arg); - } - else if (lua_isstring(L, -2)) { - PyObject *arg = LuaConvert(L, -1); - if (!arg) { - Py_DECREF(args); - Py_DECREF(pKywdArgs); - return luaL_error(L, "failed to convert argument '%s'", lua_tostring(L, -2)); - } - PyDict_SetItemString(pKywdArgs, lua_tostring(L, -2), arg); - Py_DECREF(arg); - } - lua_pop(L, 1); - } - } - // regular call style e.g. plt.plot(x, y) - else { - args = PyTuple_New(nargs); - if (!args) { - PyErr_Print(); - return luaL_error(L, "failed to create arguments tuple"); - } - for (i = 0; i != nargs; i++) { - PyObject *arg = LuaConvert(L, i+2); - if (!arg) { - Py_DECREF(args); - return luaL_error(L, "failed to convert argument #%d", i+1); - } - PyTuple_SetItem(args, i, arg); - } - } - - value = PyObject_Call(obj->o, args, pKywdArgs); - Py_DECREF(args); - if (pKywdArgs) Py_DECREF(pKywdArgs); - - if (value) { - ret = py_convert(L, value); - Py_DECREF(value); - } else { - PyErr_Print(); - luaL_error(L, "error calling python function"); - } - - return ret; -} - -static int _p_object_newindex_set(lua_State *L, py_object *obj, - int keyn, int valuen) -{ - PyObject *value; - PyObject *key = LuaConvert(L, keyn); - - if (!key) { - return luaL_argerror(L, 1, "failed to convert key"); - } - - if (!lua_isnil(L, valuen)) { - value = LuaConvert(L, valuen); - if (!value) { - Py_DECREF(key); - return luaL_argerror(L, 1, "failed to convert value"); - } - - if (PyObject_SetItem(obj->o, key, value) == -1) { - PyErr_Print(); - luaL_error(L, "failed to set item"); - } - - Py_DECREF(value); - } else { - if (PyObject_DelItem(obj->o, key) == -1) { - PyErr_Print(); - luaL_error(L, "failed to delete item"); - } - } - - Py_DECREF(key); - return 0; -} - -static int py_object_newindex_set(lua_State *L) -{ - py_object *obj = (py_object*) luaL_checkudata(L, lua_upvalueindex(1), POBJECT); - if (lua_gettop(L) != 2) - return luaL_error(L, "invalid arguments"); - - return _p_object_newindex_set(L, obj, 1, 2); -} - -static int py_object_newindex(lua_State *L) -{ - PyObject *value; - const char *attr; - py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); - assert(obj); - - if (obj->asindx || lua_type(L, 2) != LUA_TSTRING) - return _p_object_newindex_set(L, obj, 2, 3); - - attr = luaL_checkstring(L, 2); - assert(attr); - - value = LuaConvert(L, 3); - if (!value) { - return luaL_argerror(L, 1, "failed to convert value"); - } - - if (PyObject_SetAttrString(obj->o, attr, value) == -1) { - Py_DECREF(value); - PyErr_Print(); - return luaL_error(L, "failed to set value"); - } - - Py_DECREF(value); - return 0; -} - -static int _p_object_index_get(lua_State *L, py_object *obj, int keyn) -{ - PyObject *key = LuaConvert(L, keyn); - PyObject *item; - int ret = 0; - - if (!key) { - return luaL_argerror(L, 1, "failed to convert key"); - } - - item = PyObject_GetItem(obj->o, key); - if (!item) { - item = PyObject_GetAttr(obj->o, key); - } - - Py_DECREF(key); - - if (item) { - ret = py_convert(L, item); - Py_DECREF(item); - } else { - PyErr_Clear(); - if (lua_gettop(L) > keyn) { - lua_pushvalue(L, keyn+1); - ret = 1; - } - } - - return ret; -} - -static int py_object_index_get(lua_State *L) -{ - py_object *obj = (py_object*) luaL_checkudata(L, lua_upvalueindex(1), POBJECT); - int top = lua_gettop(L); - if (top < 1 || top > 2) - return luaL_error(L, "invalid arguments"); - - return _p_object_index_get(L, obj, 1); -} - -static int py_object_index(lua_State *L) -{ - int ret = 0; - PyObject *value; - const char *attr; - py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); - assert(obj); - - if (obj->asindx || lua_type(L, 2) != LUA_TSTRING) - return _p_object_index_get(L, obj, 2); - - attr = luaL_checkstring(L, 2); - assert(attr); - - if (strcmp(attr, "__get") == 0) { - lua_pushvalue(L, 1); - lua_pushcclosure(L, py_object_index_get, 1); - return 1; - } else if (strcmp(attr, "__set") == 0) { - lua_pushvalue(L, 1); - lua_pushcclosure(L, py_object_newindex_set, 1); - return 1; - } - - - value = PyObject_GetAttrString(obj->o, attr); - if (value) { - ret = py_convert(L, value); - Py_DECREF(value); - } else { - PyErr_Clear(); - lua_pushnil(L); - ret = 1; - } - - return ret; -} - -static int py_object_gc(lua_State *L) -{ - py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); - assert(obj); - - Py_DECREF(obj->o); - return 0; -} - -static int py_object_tostring(lua_State *L) -{ - py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); - assert(obj); - - PyObject *repr = PyObject_Str(obj->o); - if (!repr) - { - char buf[256]; - snprintf(buf, 256, "python object: %p", (void *)obj->o); - lua_pushstring(L, buf); - PyErr_Clear(); - } - else - { - py_convert(L, repr); - assert(lua_type(L, -1) == LUA_TSTRING); - Py_DECREF(repr); - } - return 1; -} - -static int py_operator_lambda(lua_State *L, const char *op) -{ - static char script_buff[] = "lambda a, b: a b"; - static size_t len = sizeof(script_buff) / sizeof(script_buff[0]); - snprintf(script_buff, len, "lambda a, b: a %s b", op); - - lua_pushcfunction(L, py_eval); - lua_pushlstring(L, script_buff, len); - lua_call(L, 1, 1); - return luaL_ref(L, LUA_REGISTRYINDEX); -} - -#define make_pyoperator(opname, opliteral) \ - static int py_object_ ## opname(lua_State *L) \ - { \ - static int op_ref = LUA_REFNIL; \ - if (op_ref == LUA_REFNIL) op_ref = py_operator_lambda(L, opliteral); \ - \ - lua_rawgeti(L, LUA_REGISTRYINDEX, op_ref); \ - lua_insert(L, 1); \ - return py_object_call(L); \ - } \ - struct opname ## __LINE__ // force semi - -make_pyoperator(_pow, "**"); -make_pyoperator(_mul, "*"); -make_pyoperator(_div, "/"); -make_pyoperator(_add, "+"); -make_pyoperator(_sub, "-"); - -static const luaL_Reg py_object_mt[] = -{ - {"__call", py_object_call}, - {"__index", py_object_index}, - {"__newindex", py_object_newindex}, - {"__gc", py_object_gc}, - {"__tostring", py_object_tostring}, - {"__pow", py_object__pow}, - {"__mul", py_object__mul}, - {"__div", py_object__div}, - {"__add", py_object__add}, - {"__sub", py_object__sub}, - {NULL, NULL} -}; - -static int py_run(lua_State *L, int eval) -{ - const char *s = luaL_checkstring(L, 1); - PyObject *m, *d, *o; - int ret = 0; - - lua_settop(L, 1); - - if (!eval) - { - lua_pushliteral(L, "\n"); - lua_concat(L, 2); - - s = luaL_checkstring(L, 1); - } - - m = PyImport_AddModule("__main__"); - if (!m) - return luaL_error(L, "Can't get __main__ module"); - - d = PyModule_GetDict(m); - - o = PyRun_String(s, eval ? Py_eval_input : Py_file_input, - d, d); - if (!o) - { - PyErr_Print(); - return 0; - } - - if (py_convert(L, o)) - ret = 1; - - Py_DECREF(o); - -#if PY_MAJOR_VERSION < 3 - if (Py_FlushLine()) -#endif - PyErr_Clear(); - - return ret; -} - -static int py_execute(lua_State *L) -{ - return py_run(L, 0); -} - -static int py_eval(lua_State *L) -{ - return py_run(L, 1); -} - -static int py_asindx(lua_State *L) -{ - py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); - assert(obj); - - return py_convert_custom(L, obj->o, 1); -} - -static int py_asattr(lua_State *L) -{ - py_object *obj = (py_object*) luaL_checkudata(L, 1, POBJECT); - assert(obj); - - return py_convert_custom(L, obj->o, 0); -} - -static int py_asfunc_call(lua_State *L) -{ - lua_pushvalue(L, lua_upvalueindex(1)); - lua_insert(L, 1); - return py_object_call(L); -} - -static int py_asfunc(lua_State *L) -{ - py_object *obj = luaL_checkudata(L, 1, POBJECT); - if (!PyCallable_Check(obj->o)) - return luaL_error(L, "object is not callable"); - - lua_settop(L, 1); - lua_pushcclosure(L, py_asfunc_call, 1); - - return 1; -} - -static int py_globals(lua_State *L) -{ - PyObject *globals; - - if (lua_gettop(L) != 0) { - return luaL_error(L, "invalid arguments"); - } - - globals = PyEval_GetGlobals(); - if (!globals) { - PyObject *module = PyImport_AddModule("__main__"); - if (!module) { - return luaL_error(L, "Can't get __main__ module"); - } - globals = PyModule_GetDict(module); - } - - if (!globals) { - PyErr_Print(); - return luaL_error(L, "can't get globals"); - } - - return py_convert_custom(L, globals, 1); -} - -static int py_locals(lua_State *L) -{ - PyObject *locals; - - if (lua_gettop(L) != 0) { - return luaL_error(L, "invalid arguments"); - } - - locals = PyEval_GetLocals(); - if (!locals) - return py_globals(L); - - return py_convert_custom(L, locals, 1); -} - -static int py_builtins(lua_State *L) -{ - PyObject *builtins; - - if (lua_gettop(L) != 0) { - return luaL_error(L, "invalid arguments"); - } - - builtins = PyEval_GetBuiltins(); - if (!builtins) { - PyErr_Print(); - return luaL_error(L, "failed to get builtins"); - } - - return py_convert_custom(L, builtins, 1); -} - -static int py_import(lua_State *L) -{ - const char *name = luaL_checkstring(L, 1); - PyObject *module = PyImport_ImportModule((char*)name); - int ret; - - if (!module) - { - PyErr_Print(); - return luaL_error(L, "failed importing '%s'", name); - } - - ret = py_convert_custom(L, module, 0); - Py_DECREF(module); - return ret; -} - -py_object* luaPy_to_pobject(lua_State *L, int n) -{ - if(!lua_getmetatable(L, n)) return NULL; - luaL_getmetatable(L, POBJECT); - int is_pobject = lua_rawequal(L, -1, -2); - lua_pop(L, 2); - - return is_pobject ? (py_object *) lua_touserdata(L, n) : NULL; -} - -static const luaL_Reg py_lib[] = -{ - {"execute", py_execute}, - {"eval", py_eval}, - {"asindx", py_asindx}, - {"asattr", py_asattr}, - {"asfunc", py_asfunc}, - {"locals", py_locals}, - {"globals", py_globals}, - {"builtins", py_builtins}, - {"import", py_import}, - {NULL, NULL} -}; - -LUA_API int luaopen_python(lua_State *L) -{ - int rc; - - /* Register module */ - luaL_newlib(L, py_lib); - - /* Register python object metatable */ - luaL_newmetatable(L, POBJECT); - luaL_setfuncs(L, py_object_mt, 0); - lua_pop(L, 1); - - /* Initialize Lua state in Python territory */ - if (!LuaState) LuaState = L; - - /* Initialize Python interpreter */ - if (!Py_IsInitialized()) - { - PyObject *luam, *mainm, *maind; -#if PY_MAJOR_VERSION >= 3 - wchar_t *argv[] = {L"", 0}; -#else - char *argv[] = {"", 0}; -#endif - Py_SetProgramName(argv[0]); - PyImport_AppendInittab("lua", PyInit_lua); - - /* Loading python library symbols so that dynamic extensions don't throw symbol not found error. - Ref Link: http://stackoverflow.com/questions/29880931/importerror-and-pyexc-systemerror-while-embedding-python-script-within-c-for-pam - */ -#if defined(__linux__) -# define STR(s) #s -# define PYLIB_STR(s) STR(s) -#if !defined(PYTHON_LIBRT) -# error PYTHON_LIBRT must be defined when building under Linux! -#endif - void *ok = dlopen(PYLIB_STR(PYTHON_LIBRT), RTLD_NOW | RTLD_GLOBAL); - assert(ok); (void) ok; -#endif - - Py_Initialize(); - PySys_SetArgv(1, argv); - /* Import 'lua' automatically. */ - luam = PyImport_ImportModule("lua"); - if (!luam) - return luaL_error(L, "Can't import lua module"); - - mainm = PyImport_AddModule("__main__"); - if (!mainm) - { - Py_DECREF(luam); - return luaL_error(L, "Can't get __main__ module"); - } - - maind = PyModule_GetDict(mainm); - PyDict_SetItemString(maind, "lua", luam); - Py_DECREF(luam); - } - - /* Register 'none' */ - rc = py_convert_custom(L, Py_None, 0); - if (!rc) - return luaL_error(L, "failed to convert none object"); - - lua_pushvalue(L, -1); - lua_setfield(L, LUA_REGISTRYINDEX, "Py_None"); /* registry.Py_None */ - - lua_setfield(L, -2, "none"); /* python.none */ - - return 1; -} diff --git a/src2/bridge/pythoninlua.h b/src2/bridge/pythoninlua.h deleted file mode 100644 index dada102..0000000 --- a/src2/bridge/pythoninlua.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - - Lunatic Python - -------------- - - Copyright (c) 2002-2005 Gustavo Niemeyer - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ -#ifndef PYTHONINLUA_H -#define PYTHONINLUA_H - -#define POBJECT "POBJECT" - -#if PY_MAJOR_VERSION < 3 - #define PyBytes_Check PyString_Check - #define PyBytes_AsStringAndSize PyString_AsStringAndSize -#endif - -int py_convert(lua_State *L, PyObject *o); - -typedef struct -{ - PyObject *o; - int asindx; -} py_object; - -py_object* luaPy_to_pobject(lua_State *L, int n); -LUA_API int luaopen_python(lua_State *L); - -#endif From 8e2982cfc15bd950d9800b5d0caefc2c25c527f1 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sun, 17 Sep 2023 13:15:44 -0500 Subject: [PATCH 12/77] Basic python parser --- src/pyast.cobalt | 50 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/pyast.cobalt b/src/pyast.cobalt index d9a21fe..443f853 100644 --- a/src/pyast.cobalt +++ b/src/pyast.cobalt @@ -1 +1,49 @@ -// Python code to astclass \ No newline at end of file +// Python code to astclass +/* +The nodevisitor itself is written in Cobalt +but is wrapped around a python auto-generated +parser & visior runner. +*/ +py = import("python") + +code = [[ +def add(a, b): + result = a + b + return result +]] + +visitor = { + visit_FunctionDef: function(node) { + print("FunctionDef: " .. node.name) + }, + visit_ClassDef: function(node) { + print("ClassDef: " .. node.name) + }, + visit_Assign: function(node) { + print("Assign: " .. node.targets[0].id) + }, +} + +/* Parse code to table tree inside of python */ +tree = {} +source = ([[ +import ast +import cobalt +visitors = cobalt.globals().visitor +code = cobalt.globals().code +tree = ast.parse(code) + +class Visitor(ast.NodeVisitor):]]) + +for (i, v in pairs(visitor)) { + source = source .. ("\n\t"..[[def ]]..i..[[(self, node): +]].."\t\t"..[[visitors.]]..i..[[(node) +]].."\t\t"..[[self.generic_visit(node)]]) +} + +source = source.."\n"..[[ +visitor = Visitor() +visitor.visit(tree) +]] +print(source) +py.execute(source) \ No newline at end of file From 8f03a7bbe3a6220e6d7c88a22d991ce3d4a57cbe Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sun, 17 Sep 2023 13:17:25 -0500 Subject: [PATCH 13/77] Format --- src/pyast.cobalt | 52 ++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/pyast.cobalt b/src/pyast.cobalt index 443f853..4761dd4 100644 --- a/src/pyast.cobalt +++ b/src/pyast.cobalt @@ -6,12 +6,7 @@ parser & visior runner. */ py = import("python") -code = [[ -def add(a, b): - result = a + b - return result -]] - +gen = "" /* string to be automatically written by visitor */ visitor = { visit_FunctionDef: function(node) { print("FunctionDef: " .. node.name) @@ -24,26 +19,31 @@ visitor = { }, } -/* Parse code to table tree inside of python */ -tree = {} -source = ([[ -import ast -import cobalt -visitors = cobalt.globals().visitor -code = cobalt.globals().code -tree = ast.parse(code) -class Visitor(ast.NodeVisitor):]]) -for (i, v in pairs(visitor)) { - source = source .. ("\n\t"..[[def ]]..i..[[(self, node): -]].."\t\t"..[[visitors.]]..i..[[(node) -]].."\t\t"..[[self.generic_visit(node)]]) -} +return function(code){ + /* Runs the visitor */ + tree = {} + source = ([[ + import ast + import cobalt + visitors = cobalt.globals().visitor + code = cobalt.globals().code + tree = ast.parse(code) + + class Visitor(ast.NodeVisitor):]]) + + for (i, v in pairs(visitor)) { + source = source .. ("\n\t"..[[def ]]..i..[[(self, node): + ]].."\t\t"..[[visitors.]]..i..[[(node) + ]].."\t\t"..[[self.generic_visit(node)]]) + } -source = source.."\n"..[[ -visitor = Visitor() -visitor.visit(tree) -]] -print(source) -py.execute(source) \ No newline at end of file + source = source.."\n"..[[ + visitor = Visitor() + visitor.visit(tree) + ]] + print(source) + py.execute(source) + return gen; +} \ No newline at end of file From 610678b09ae54cfa9d69e5f28bbc3637b68d6fa4 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sun, 17 Sep 2023 13:24:59 -0500 Subject: [PATCH 14/77] Main interface --- src/cli/main.c | 27 -------------------------- src/init.cobalt | 50 +++++++++++++++++++++++++++++++++++++++++++++++-- src/test.cobalt | 0 3 files changed, 48 insertions(+), 29 deletions(-) delete mode 100644 src/cli/main.c delete mode 100644 src/test.cobalt diff --git a/src/cli/main.c b/src/cli/main.c deleted file mode 100644 index 2e0a96c..0000000 --- a/src/cli/main.c +++ /dev/null @@ -1,27 +0,0 @@ -#include -#include -#include - -int main(int argc, char *argv[]){ - // Mirror the arguments like so - // cobalt -e "import('rpyc').cli(ARG1, ARG2, ARG3, ...)" - - // use popen to run the command - - char command[256] = "cobalt -e \"import('rpyc').cli("; // 256 is arbitrary - for(int i = 1; i < argc; i++){ - strcat(command, argv[i]); - if(i != argc - 1){ - strcat(command, ", "); - } - } - strcat(command, ")\""); - - FILE *fp = popen(command, "r"); - char buff[256]; - while(fgets(buff, 256, fp) != NULL){ - printf("%s", buff); - } - pclose(fp); - return 0; -} \ No newline at end of file diff --git a/src/init.cobalt b/src/init.cobalt index 05d7b35..c387e5d 100644 --- a/src/init.cobalt +++ b/src/init.cobalt @@ -1,11 +1,57 @@ var self = {} -function self.cli(...){ - var args = {...} +/******* IMPORTS ********/ +/* IMPORT AST GENERATORS */ +var pyast = import("pyast") +var cppast = import("cppast") +var cast = import("cast") +/* IMPORT CODE GENERATORS */ +var generator = import("generator") + +/* IMPORT TEMPLATES */ +var template = import("template") +/************************/ + + + +/** API **/ +/* +includes functions that wrap around the generators, templates, +and parsers to provide a simple interface for the user/developer +using rpyc +*/ + +/* functions for parsing */ +function self::ParsePython(code){ + return pyast(code) +} +function self::ParseCpp(code){ + return cppast(code) +} +function self::ParseC(code){ + return cast(code) +} + +/* functions for generating code */ +function self::Generate(ast){ + return generator(ast) +} + +/* functions for loading template */ +function self.loadTemplate(path, format){ + return template(path, format) +} + +/****** INTERFACE ********/ +/* +operated by the CLI +*/ +if (arg){ // being run through interpreter or AOT compiler if (#args != 1){ error("Invalid number of arguments") os.exit(1) } } + return self \ No newline at end of file diff --git a/src/test.cobalt b/src/test.cobalt deleted file mode 100644 index e69de29..0000000 From a0425630f5ed870d012ca513704dff8659ef11ac Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sun, 17 Sep 2023 13:26:06 -0500 Subject: [PATCH 15/77] Use #include rather than import --- src/init.cobalt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/init.cobalt b/src/init.cobalt index c387e5d..cfc985a 100644 --- a/src/init.cobalt +++ b/src/init.cobalt @@ -2,15 +2,15 @@ var self = {} /******* IMPORTS ********/ /* IMPORT AST GENERATORS */ -var pyast = import("pyast") -var cppast = import("cppast") -var cast = import("cast") +#include "pyast.cobalt" +#include "cppast.cobalt" +#include "cast.cobalt" /* IMPORT CODE GENERATORS */ -var generator = import("generator") +#include "generator.cobalt" /* IMPORT TEMPLATES */ -var template = import("template") +#include "template.cobalt" /************************/ From d9b90a8e677a321de1f7d4af9707972dbf225cee Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sun, 17 Sep 2023 13:55:17 -0500 Subject: [PATCH 16/77] CMake build --- .gitignore | 18 +++++++++++++++++- CMakeLists.txt | 4 ++++ src/CMakeLists.txt | 16 ++++++++++++++++ src/cast.cobalt | 6 +++++- src/cppast.cobalt | 6 +++++- src/generator.cobalt | 2 +- src/pyast.cobalt | 2 +- 7 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 src/CMakeLists.txt diff --git a/.gitignore b/.gitignore index 7e99e36..ec5623f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,17 @@ -*.pyc \ No newline at end of file +# idrk +*.pyc + +# CMake +/cmake_install.cmake +/CMakeCache.txt +/Makefile +/CMakeFiles +/src/CMakeFiles +/src/Makefile +/src/cmake_install.cmake +/src/Makefile.cmake +/src/Makefile +/src/init.c + +# Cobalt preprocessed +*.cii \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4692948 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.12) +project(MyProject) + +add_subdirectory(src) \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..8448aec --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.12) +project(MyProject) + +set(COBALT_COMPILER "cobaltaot") +set(SOURCE_FILE "init.cobalt") + +# Generate the C file from the Cobalt source +add_custom_command( + OUTPUT init.c + COMMAND ${COBALT_COMPILER} ${SOURCE_FILE} -o init.c -e + DEPENDS ${SOURCE_FILE} + COMMENT "Compiling.." +) + +# Compile the C file to an executable +add_executable(app init.c) \ No newline at end of file diff --git a/src/cast.cobalt b/src/cast.cobalt index 75a135e..b6edbe5 100644 --- a/src/cast.cobalt +++ b/src/cast.cobalt @@ -1 +1,5 @@ -// C code to astclass \ No newline at end of file +// C code to astclass + +cast = function(){ + +} \ No newline at end of file diff --git a/src/cppast.cobalt b/src/cppast.cobalt index 6650d72..7bc3479 100644 --- a/src/cppast.cobalt +++ b/src/cppast.cobalt @@ -1 +1,5 @@ -// C++ AST to astclass \ No newline at end of file +// C++ AST to astclass + +cppast = function(){ + +} \ No newline at end of file diff --git a/src/generator.cobalt b/src/generator.cobalt index 7e8eab2..fbcb4d6 100644 --- a/src/generator.cobalt +++ b/src/generator.cobalt @@ -176,7 +176,7 @@ function visit(node){ } function generate(ast){ if (ast->getType() == "SOURCE"){ - source = [[--// Generated with roblox-pyc (v3) by @AsynchronousAI \\--]] + source = "--// Generated with roblox-pyc (v3) by @AsynchronousAI \\--" visit(ast) }else{ error("Tried to generate Luau code, AST is incorrectly formatted. (err: MISSING SOURCE))") diff --git a/src/pyast.cobalt b/src/pyast.cobalt index 4761dd4..88c2aa9 100644 --- a/src/pyast.cobalt +++ b/src/pyast.cobalt @@ -21,7 +21,7 @@ visitor = { -return function(code){ +pyast = function(code){ /* Runs the visitor */ tree = {} source = ([[ From 80f0ed06a423e750c8213060d8d8fa27f55d14dd Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sun, 17 Sep 2023 13:59:00 -0500 Subject: [PATCH 17/77] Flags --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4692948..8d9a6be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,9 @@ cmake_minimum_required(VERSION 3.12) project(MyProject) +if (NOT COBALT_INCLUDES) + message(FATAL_ERROR "Please provide Cobalt include path like `-DCOBALT_INCLUDES=/path/to/cobalt/includes`") +endif() +set(CMAKE_C_FLAGS "-w -O3 -I${COBALT_INCLUDES}") +message("CMAKE_C_FLAGS: ${CMAKE_C_FLAGS}") add_subdirectory(src) \ No newline at end of file From d37e78e2429388f738cbc776895d1f416c39d7de Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sun, 17 Sep 2023 16:18:33 -0500 Subject: [PATCH 18/77] Improvements --- .argon.project.json | 53 ++++++++++++++++++++++++++++++++ .gitignore | 5 ++-- README.md | 73 ++++++++++++++------------------------------- src/astclass.cobalt | 8 ----- src/init.cobalt | 11 +++++-- src/pyast.cobalt | 1 - src/util.cobalt | 7 ++--- 7 files changed, 90 insertions(+), 68 deletions(-) create mode 100644 .argon.project.json diff --git a/.argon.project.json b/.argon.project.json new file mode 100644 index 0000000..7688d03 --- /dev/null +++ b/.argon.project.json @@ -0,0 +1,53 @@ +{ + "name": "Argon", + "tree": { + "$className": "DataModel", + "Chat": { + "$path": "src/Chat" + }, + "Lighting": { + "$path": "src/Lighting" + }, + "LocalizationService": { + "$path": "src/LocalizationService" + }, + "ReplicatedFirst": { + "$path": "src/ReplicatedFirst" + }, + "ReplicatedStorage": { + "$path": "src/ReplicatedStorage" + }, + "ServerScriptService": { + "$path": "src/ServerScriptService" + }, + "ServerStorage": { + "$path": "src/ServerStorage" + }, + "SoundService": { + "$path": "src/SoundService" + }, + "StarterGui": { + "$path": "src/StarterGui" + }, + "StarterPack": { + "$path": "src/StarterPack" + }, + "StarterPlayer": { + "StarterCharacterScripts": { + "$path": "src/StarterPlayer/StarterCharacterScripts" + }, + "StarterPlayerScripts": { + "$path": "src/StarterPlayer/StarterPlayerScripts" + } + }, + "Teams": { + "$path": "src/Teams" + }, + "TestService": { + "$path": "src/TestService" + }, + "Workspace": { + "$path": "src/Workspace" + } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index ec5623f..ef9ae35 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,6 @@ /src/Makefile /src/init.c -# Cobalt preprocessed -*.cii \ No newline at end of file +# Cobalt preprocessed and compiled +*.cii +*.byte \ No newline at end of file diff --git a/README.md b/README.md index 57850d9..baa42c0 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ ## Sponsors 0 sadly + ## Contributors @@ -49,15 +50,7 @@ *** -``` -pip install roblox-pyc -``` - - - -Python, Lunar, C, C++ Compiler for Roblox. - -Python 3.13 (dev), C (all versions), C++ (all versions), Lunar -> Lua(u) +Python, C, C++ Compiler for Roblox. > This has NO RELATION with .pyc files, roblox-py, or roblox-ts @@ -67,45 +60,25 @@ Python 3.13 (dev), C (all versions), C++ (all versions), Lunar -> Lua(u) *** -### Features - -* 🔄 **Interchangeable**\ - roblox-pyc supports using Lua, Lunar, roblox-ts, C, C++, and Python all at once so you can have the best of all sides. -* ☄️ **Ultrafast compiler**\ - The roblox-pyc compiler is designed so entire projects can be compiled in a matter of seconds -* 📉 **Optimized code**\ - roblox-pyc is a source-source compiler, it doesn't use any WASM or anything like that to cheat its way and has major performance drops. It uses an AST and rewrites your code and optimizes it. -* ⚠️ **Easy error checking**\ - Your code can easily be checked for errors because of the precompiler error system. -* 🧩 **Cross-language module support**\ - roblox-pyc allows you to require/import modules from other languages. -* 🛠️ **Supports everything**\ - Regardless if you use Rojo, Argon, in Mac, Windows with any code editors or anything else roblox-pyc is highly customizable and allows you to use any of them -* ↗️ **Customizable**\ - You can customize roblox-pyc to change your C/C++ version or dynamic library or any QoL features, not only that roblox-pyc and all of its dependencies are open-source so you can mod it and change anything to your liking -* 💻 **Languages**\ - roblox-pyc supports a great variety of languages that are fully programmed. -* 🌎 **Upload your code to the world**\ - Using a VScode sync plugin you can upload your code to the world with GitHub, GitLab, whatever. -* 📲 **In-roblox plugin**\ - If you dont what to use VScode, python supports a roblox plugin which can be hosted in the terminal with all the features listed above! -* 🌙 **Lunar**\ - roblox-pyc comes with a custom language called lunar with amazing syntax features and an extended standard library, which is a modified version of MoonScript for roblox - -*** -## Unsupported features -- Import * (python) -- Syntax based slicing (python) (workaround: use slice builtin function) -- C/C++ (not implemented yet) -- _\_slots_\_ (python) (adding soon) -- _\_dict_\_ (python) (adding soon) +### Building +```bash +cmake -DCOBALT_INCLUDES=/path/to/cobalt/headers . +make +``` *** - -### Credits - -* [Highlighter](https://github.com/boatbomber/Highlighter). modified to work with python (plugin usage) -* [TextBoxPlus](https://github.com/boatbomber/TextBoxPlus). uses a modified version with autocomplete (plugin usage) -* [pythonlua](https://github.com/dmitrii-eremin/python-lua). this is heavily modified version with flask implementation and compiler changes. (read licenses in [copyright.txt](COPYRIGHTS.txt)) -* [seasnake](https://github.com/pybee/seasnake) and sealang. Modified to convert C/C++ to Luau rather than C/C++ to Python 2.7 -* [MoonScript](https://github.com/leafo/moonscript). Modified to work with the Roblox API (Lunar). -* [LuauAST](). roblox-pyc uses roblox-ts's LuauAST to generate Luau code. (not used in current versions) +### API +```js +var rpyc = import("roblox-pyc"); // #include-ing roblox-pyc/init.cobalt might turn on interface mode and cause issues. +var ast = rpyc->ParseCPP("#include \n int main() { printf('Hi') return 0; }"); +print(rpyc->Generate(ast)) +/* +Output: +_G.rpyc.include("stdio.h") + +function main() + printf("Hi") + return 0 +end +*/ + +``` \ No newline at end of file diff --git a/src/astclass.cobalt b/src/astclass.cobalt index 04be17e..21bfeb4 100644 --- a/src/astclass.cobalt +++ b/src/astclass.cobalt @@ -87,11 +87,3 @@ function AST.head() { return AST.new(head); } -// Example usage -var head = AST.head(); -var child1 = head->addChild("child1", {"prop1", "prop2"}, "type1"); -var child2 = head->addChild("child2", {"prop3", "prop4"}, "type2"); -print(head->getType()); // prints "head" -print(child1->getType()); // prints "type1" -print(child2->getType()); // prints "type2" -print(child1->getParent()->getType()); // prints "head" diff --git a/src/init.cobalt b/src/init.cobalt index cfc985a..b8b25e3 100644 --- a/src/init.cobalt +++ b/src/init.cobalt @@ -1,6 +1,14 @@ var self = {} +function error(text){ + print("\27[1;31merror:\27[0m "..text) + os.exit(1) +} /******* IMPORTS ********/ +/* IMPORT DEPENDENCIES */ +#include "astclass.cobalt" +#include "util.cobalt" + /* IMPORT AST GENERATORS */ #include "pyast.cobalt" #include "cppast.cobalt" @@ -48,9 +56,8 @@ function self.loadTemplate(path, format){ operated by the CLI */ if (arg){ // being run through interpreter or AOT compiler - if (#args != 1){ + if (#arg != 1){ error("Invalid number of arguments") - os.exit(1) } } diff --git a/src/pyast.cobalt b/src/pyast.cobalt index 88c2aa9..425328c 100644 --- a/src/pyast.cobalt +++ b/src/pyast.cobalt @@ -43,7 +43,6 @@ pyast = function(code){ visitor = Visitor() visitor.visit(tree) ]] - print(source) py.execute(source) return gen; } \ No newline at end of file diff --git a/src/util.cobalt b/src/util.cobalt index 0ca08df..0d5da24 100644 --- a/src/util.cobalt +++ b/src/util.cobalt @@ -7,8 +7,7 @@ - endswith(txt, end) */ -file = file || import("file"); -filex = import("ext.file") +file = import("file"); function backwardreplace(s, old, new, occurences){ // Example: @@ -51,6 +50,4 @@ function filterfolder(path, ext){ pfile->close(); return true; -} - -print(filterfolder("", ".lua")) \ No newline at end of file +} \ No newline at end of file From e07e5e0fbcef114e3a2f097894d631d8c9c7d936 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sun, 17 Sep 2023 16:21:01 -0500 Subject: [PATCH 19/77] Fix --- README.md | 1 - src/init.cobalt | 9 +++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index baa42c0..d8a4d68 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ > **NOTICE:** > This is the indev build -> credits to lunatic-python [![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#contributors-) diff --git a/src/init.cobalt b/src/init.cobalt index b8b25e3..e345517 100644 --- a/src/init.cobalt +++ b/src/init.cobalt @@ -5,6 +5,15 @@ function error(text){ os.exit(1) } /******* IMPORTS ********/ +/* +#includes instead of imports +are used because this one file get +compiled into the final executable +and because imports can get +complicated when dealing with +CLI's. +*/ + /* IMPORT DEPENDENCIES */ #include "astclass.cobalt" #include "util.cobalt" From b5f61778635bff3c3936f15f29527a3b0df836bd Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sun, 17 Sep 2023 16:49:32 -0500 Subject: [PATCH 20/77] Interface --- src/init.cobalt | 64 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/src/init.cobalt b/src/init.cobalt index e345517..c01d234 100644 --- a/src/init.cobalt +++ b/src/init.cobalt @@ -64,10 +64,70 @@ function self.loadTemplate(path, format){ /* operated by the CLI */ +#define VERSION "3.0.0" +#define TAB "\t\b\b\b\b" +function usage(){ + print("\n"..[[usage: rpyc [options] +Available options are: +]]..TAB..[[-o file output to 'file' (as a file or directory if -d is used) +]]..TAB..[[-i file input from 'file' +]]..TAB..[[-d compile all files in current directory +]]..TAB..[[-w watch mode (directory only) +]]..TAB..[[-v show version information +]]..TAB..[[-vd show version number only]]) + os.exit(0) +} +function version(){ + print("\27[1;34mcopyright:\27[0m roblox-pyc \27[1m"..VERSION.."\27[0m licensed under the GNU Affero General Public License by \27[1m@AsynchronousAI\27[0m") + os.exit(0) +} if (arg){ // being run through interpreter or AOT compiler - if (#arg != 1){ - error("Invalid number of arguments") + if (#arg < 1){ + print("rpyc: no arguments provided") + usage() } + var flags = {} + for (i, v in pairs(arg)){ + if (v == "cobalt" || v == "cobaltc.byte") continue + if (string.get(v, 1) == "-"){ + flags[#flags+1] = v + }else{ + warn("Unhandled argument: "..v) + } + } + + /* Run flags */ + var mode = "none" + for (i, v in pairs(flags)){ + if (v == "-w") { + /* Watch mode*/ + }else if (v == "-h" || v == "--help") { + usage() + }else if (v == "-v" || v == "--version") { + version() + }else if (v == "-vd") { + print(VERSION) + }else if (v == "-w") { + mode = "watch" + }else if (v->split(":")[1] == "-i") { + mode = "file" + if (#v->split(":") != 2) error("-i flag requires a file and only one file\n\tExample: -i:main.py") + _G.infile = v->split(":")[2] + }else if (v->split(":")[1] == "-o") { + mode = "file" + if (#v->split(":") != 2) error("-o flag requires a file and only one file\n\tExample: -o:main.lua") + _G.outfile = v->split(":")[2] + }else if (v->split(":")[1] == "-d") { + mode = "directory" + _G.directory = require("file").currentdir() + } + } + + /* Operate mode */ + if (mode == "none"){ + error("No mode specified") + } + } return self \ No newline at end of file From 40f28b286ef6acd59523e9d0a1c6ecba66fd0895 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Mon, 2 Oct 2023 18:09:32 -0500 Subject: [PATCH 21/77] Python based --- .gitignore | 12 -- CMakeLists.txt | 9 - README.md | 25 +-- src/API.md | 12 -- src/CMakeLists.txt | 16 -- src/Floorplan.md | 97 --------- src/README.md | 2 - src/astclass.cobalt | 89 --------- src/cast.cobalt | 5 - src/cppast.cobalt | 5 - src/generator.cobalt | 185 ------------------ src/init.cobalt | 133 ------------- src/init.py | 74 +++++++ src/pyast.cobalt | 48 ----- src/template.cobalt | 6 - src/template/.gitignore | 6 - src/template/README.md | 25 --- .../compiled/client/init.client.example | 1 - .../compiled/server/init.server.example | 1 - src/template/compiled/shared/Hello.example | 3 - src/template/default.project.json | 72 ------- src/template/src/client/init.client.lua | 1 - src/template/src/server/init.server.lua | 1 - src/template/src/shared/Hello.lua | 3 - src/util.cobalt | 53 ----- 25 files changed, 75 insertions(+), 809 deletions(-) delete mode 100644 CMakeLists.txt delete mode 100644 src/API.md delete mode 100644 src/CMakeLists.txt delete mode 100644 src/Floorplan.md delete mode 100644 src/README.md delete mode 100644 src/astclass.cobalt delete mode 100644 src/cast.cobalt delete mode 100644 src/cppast.cobalt delete mode 100644 src/generator.cobalt delete mode 100644 src/init.cobalt create mode 100644 src/init.py delete mode 100644 src/pyast.cobalt delete mode 100644 src/template.cobalt delete mode 100644 src/template/.gitignore delete mode 100644 src/template/README.md delete mode 100644 src/template/compiled/client/init.client.example delete mode 100644 src/template/compiled/server/init.server.example delete mode 100644 src/template/compiled/shared/Hello.example delete mode 100644 src/template/default.project.json delete mode 100644 src/template/src/client/init.client.lua delete mode 100644 src/template/src/server/init.server.lua delete mode 100644 src/template/src/shared/Hello.lua delete mode 100644 src/util.cobalt diff --git a/.gitignore b/.gitignore index ef9ae35..30369f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,6 @@ # idrk *.pyc -# CMake -/cmake_install.cmake -/CMakeCache.txt -/Makefile -/CMakeFiles -/src/CMakeFiles -/src/Makefile -/src/cmake_install.cmake -/src/Makefile.cmake -/src/Makefile -/src/init.c - # Cobalt preprocessed and compiled *.cii *.byte \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 8d9a6be..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -cmake_minimum_required(VERSION 3.12) -project(MyProject) - -if (NOT COBALT_INCLUDES) - message(FATAL_ERROR "Please provide Cobalt include path like `-DCOBALT_INCLUDES=/path/to/cobalt/includes`") -endif() -set(CMAKE_C_FLAGS "-w -O3 -I${COBALT_INCLUDES}") -message("CMAKE_C_FLAGS: ${CMAKE_C_FLAGS}") -add_subdirectory(src) \ No newline at end of file diff --git a/README.md b/README.md index d8a4d68..d36347c 100644 --- a/README.md +++ b/README.md @@ -57,27 +57,4 @@ Python, C, C++ Compiler for Roblox. > Python is fully implemented, all code should work because it supports the dev build of Python 3.13. -*** - -### Building -```bash -cmake -DCOBALT_INCLUDES=/path/to/cobalt/headers . -make -``` -*** -### API -```js -var rpyc = import("roblox-pyc"); // #include-ing roblox-pyc/init.cobalt might turn on interface mode and cause issues. -var ast = rpyc->ParseCPP("#include \n int main() { printf('Hi') return 0; }"); -print(rpyc->Generate(ast)) -/* -Output: -_G.rpyc.include("stdio.h") - -function main() - printf("Hi") - return 0 -end -*/ - -``` \ No newline at end of file +*** \ No newline at end of file diff --git a/src/API.md b/src/API.md deleted file mode 100644 index 9e89fab..0000000 --- a/src/API.md +++ /dev/null @@ -1,12 +0,0 @@ -# API -Goal API -## init -```bash -$ rpyc init -``` -Clones template -## . -```bash -$ rpyc . # or any other path -``` -Compiles all files in the source folder to the compiled folder. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 8448aec..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -cmake_minimum_required(VERSION 3.12) -project(MyProject) - -set(COBALT_COMPILER "cobaltaot") -set(SOURCE_FILE "init.cobalt") - -# Generate the C file from the Cobalt source -add_custom_command( - OUTPUT init.c - COMMAND ${COBALT_COMPILER} ${SOURCE_FILE} -o init.c -e - DEPENDS ${SOURCE_FILE} - COMMENT "Compiling.." -) - -# Compile the C file to an executable -add_executable(app init.c) \ No newline at end of file diff --git a/src/Floorplan.md b/src/Floorplan.md deleted file mode 100644 index dad0f62..0000000 --- a/src/Floorplan.md +++ /dev/null @@ -1,97 +0,0 @@ -# Floorplan -A plan on how the code will be organize: -- util: Required by all code, provides basic functions -- interface: Core compiler, wrapped by init.cobalt with the API and CLI -- generator: Used by interface to generate code from the ast -- template: Provides a interface to duplicate the template for a inputted extension -- /bridge: provides bridging functions for Lua and Python -- astclass: provides a base class for ast to pyast to generate -- pyast: Uses bridge and uses the python library `ast` to generate a ast and convert it to astclass -- cast: Uses bridge and uses the python library `libclang` to generate a C99 ast and convert it to astclass -- cppast: Uses bridge and uses the python library `libclang` to generate a C++99 (base) ast and convert it to astclass - -## AST Reference -```lua --- variable declarations -local a = 1 -local b = "hello" -local c = true - --- if statement -if c then - print(b) -else - print("world") -end - --- for loop with index -for i = 1, 5 do - print(i) -end - --- for loop with table -local t = {1, 2, 3, 4, 5} -for i, v in ipairs(t) do - print(i, v) -end - --- function definition -function add(x, y) - return x + y -end - --- function call -local sum = add(a, 2) -print(sum) - --- table indexing -local person = {name = "Alice", age = 30} -print(person.name) - --- table new indexing -local mt = { - __newindex = function(t, k, v) - print("setting", k, "to", v) - rawset(t, k, v) - end -} -local t = {} -setmetatable(t, mt) -t.foo = "bar" -``` - -Equal AST: -```bash -- SOURCE --- DECL A 1 NUMBER --- DECL B "hello" STRING --- DECL C true BOOLEAN --- ASSIGN B "hi" STRING global=true --- IF C ---- CALL PRINT B --- ELSE ---- CALL PRINT "world" --- ENDSTMT --- FOR I 1 5 ---- CALL PRINT I --- ENDSTMT --- DECL T {1, 2, 3, 4, 5} TABLE --- FOR I V T ---- CALL PRINT I V --- ENDSTMT --- DECL FUNCTION add X Y ---- RETURN X + Y --- ENDSTMT --- DECL SUM add A 2 --- CALL PRINT SUM --- DECL PERSON {name = "Alice", age = 30} TABLE --- CALL PRINT PERSON.name --- DECL MT {} --- DECL MT __newindex FUNCTION T K V ---- CALL PRINT "setting" K "to" V ---- CALL RAWSET T K V --- ENDSTMT --- DECL T {} --- CALL setmetatable T MT --- SET T foo "bar" -``` diff --git a/src/README.md b/src/README.md deleted file mode 100644 index 4a88605..0000000 --- a/src/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# src2 -Rewritting `/src` in `cobalt` rather than `python`. \ No newline at end of file diff --git a/src/astclass.cobalt b/src/astclass.cobalt deleted file mode 100644 index 21bfeb4..0000000 --- a/src/astclass.cobalt +++ /dev/null @@ -1,89 +0,0 @@ -// AST Class - -/* Methods -- node->getChildren() - Get all children of node - Returns array of nodes -- node->getType() - Get type of node - Returns string -- node.parent - Get parent of node - Returns node -- node.name - Get name of node - Returns string -- node.properties - Get all properties of node - Returns array of strings - -- node->add(name, properties, type) - Create empty node inside of node - Returns node -- ast.head() - Create empty node head - Returns node -*/ - -// Define the AST class -var AST = {}; -AST.__index = AST; - -// Constructor function -function AST.new(node) { - var this = setmetatable({}, AST); - this.node = node; - return this; -} - -// Method to get the node type -function AST::getType() { - return this.node.type; -} - -// Method to get the node children -function AST::getChildren() { - var children = {}; - for( i, child in ipairs(this.node.children) ) { - table.insert(children, AST.new(child)); - } - return children; -} - -// Method to get the parent node -function AST::getParent() { - return AST.new(this.node.parent); -} - -// Method to get the node name -function AST::getName() { - return this.node.name; -} - -// Method to get the node properties -function AST::getProperties() { - return this.node.properties; -} - -// Method to add a child node -function AST::addChild(name, properties, type) { - var child = { - name = name, - properties = properties, - type = type, - children = {}, - parent = this.node - }; - table.insert(this.node.children, child); - return AST.new(child); -} - -// Method to get the node depth (How many parents are above it) -function AST::getDepth() { - var depth = 0; - var parent = this.node.parent; - while(parent != null) { - depth++; - parent = parent.parent; - } - return depth; -} - -// Static method to create a new head node -function AST.head() { - var head = { - name = "head", - properties = {}, - type = "head", - children = {}, - parent = null - }; - return AST.new(head); -} - diff --git a/src/cast.cobalt b/src/cast.cobalt deleted file mode 100644 index b6edbe5..0000000 --- a/src/cast.cobalt +++ /dev/null @@ -1,5 +0,0 @@ -// C code to astclass - -cast = function(){ - -} \ No newline at end of file diff --git a/src/cppast.cobalt b/src/cppast.cobalt deleted file mode 100644 index 7bc3479..0000000 --- a/src/cppast.cobalt +++ /dev/null @@ -1,5 +0,0 @@ -// C++ AST to astclass - -cppast = function(){ - -} \ No newline at end of file diff --git a/src/generator.cobalt b/src/generator.cobalt deleted file mode 100644 index fbcb4d6..0000000 --- a/src/generator.cobalt +++ /dev/null @@ -1,185 +0,0 @@ -// Luau code generator. Generates Luau code from AST in astclass.cobalt. -astlib = import("astclass") -source = "" -util = import("util") - -// VISITOR UTILS -function write(txt, depth){ - depthstr = "" - for (i = 1, (depth || 1)) { - depthstr = depthstr.." " - } - source = source.."\n"..(depthstr)..txt -} -var alreadyDefined = [] - -// VISITS -// Visitor supports: -// - Source init -// - Declaration and assignment -// - Function calls -// - Return statements -// - If statements -// - Else statements -// - Elseif statements -// - While statements -// - For numeric statements -// - For table statements -// - Function statements - -var visits = { - "SOURCE": function(node){ - write() // Newline init - }, - "DECL": function(node){ - var properties = node->getProperties() - - var name = properties["name"] - var value = properties["value"] - var type = properties["type"] // Ignore for now - - write("local "..name.." = "..value, node->getDepth()) - table.insert(alreadyDefined, name) - }, - "ASSIGN": function(node){ - var properties = node->getProperties() - - var name = properties["name"] - var value = properties["value"] - - if (!alreadyDefined[name] && !properties["global"]){ - write("local "..name.." = "..value, node->getDepth()) - }else{ - write(name.." = "..value, node->getDepth()) - } - }, - "CALL": function(node){ - var properties = node->getProperties() - - var name = properties["name"] - var args = properties["args"] - - var argString = "" - - for (i, v in pairs(args)){ - argString = argString..v - if (i != #args){ - argString = argString..", " - } - } - - write(name.."("..argString..")", node->getDepth()) - }, - "RETURN": function(node){ - var properties = node->getProperties() - - var value = properties["value"] - - write("return "..value, node->getDepth()) - }, - "IF": function(node){ - var properties = node->getProperties() - - var condition = properties["condition"] - - write("if "..condition.." then", node->getDepth()) - - return null, true - }, - "ELSE": function(node){ - // source should currently end with "end" - if (util.endsWith(source, "end")){ - source = util.backwardsReplace(source, "end", "", 1) - write("else", node->getDepth()) - }else{ - error("Tried to generate Luau code, AST is incorrectly formatted. (err: ELSE MISSING END)") - } - - return null, true - }, - "ELSEIF": function(node){ - var properties = node->getProperties() - - var condition = properties["condition"] - - if (util.endsWith(source, "end")){ - source = util.backwardsReplace(source, "end", "", 1) - write("elseif "..condition.." then", node->getDepth()) - }else{ - error("Tried to generate Luau code, AST is incorrectly formatted. (err: ELSEIF MISSING END)") - } - - return null, true - }, - "WHILE": function(node){ - var properties = node->getProperties() - - var condition = properties["condition"] - - write("while "..condition.." do", node->getDepth()) - - return null, true - }, - "FORN": function(node){ - var properties = node->getProperties() - - var name = properties["name"] - var start = properties["start"] - var end = properties["end"] - - write("for "..name.." = "..start..", "..end.." do", node->getDepth()) - - return null, true - }, - "FORT": function(node){ - var properties = node->getProperties() - - var name = properties["name"] - var table = properties["table"] - - write("for "..name.." in "..table.." do", node->getDepth()) - - return null, true - }, - "FUNCTION": function(node){ - var properties = node->getProperties() - - var name = properties["name"] - var args = properties["args"] - - var argString = "" - - for (i, v in pairs(args)){ - argString = argString..v - if (i != #args){ - argString = argString..", " - } - } - - write("function "..name.."("..argString..")", node->getDepth()) - - return null, true - }, -} - -// VISITOR -function visit(node){ - var ignore, addend = visits[node->getType()](node) - if (!ignore){ - for (i, v in pairs(node->getChildren())){ - visit(v) - } - } - if (addend){ - write("end") - } -} -function generate(ast){ - if (ast->getType() == "SOURCE"){ - source = "--// Generated with roblox-pyc (v3) by @AsynchronousAI \\--" - visit(ast) - }else{ - error("Tried to generate Luau code, AST is incorrectly formatted. (err: MISSING SOURCE))") - } - return source; -} \ No newline at end of file diff --git a/src/init.cobalt b/src/init.cobalt deleted file mode 100644 index c01d234..0000000 --- a/src/init.cobalt +++ /dev/null @@ -1,133 +0,0 @@ -var self = {} - -function error(text){ - print("\27[1;31merror:\27[0m "..text) - os.exit(1) -} -/******* IMPORTS ********/ -/* -#includes instead of imports -are used because this one file get -compiled into the final executable -and because imports can get -complicated when dealing with -CLI's. -*/ - -/* IMPORT DEPENDENCIES */ -#include "astclass.cobalt" -#include "util.cobalt" - -/* IMPORT AST GENERATORS */ -#include "pyast.cobalt" -#include "cppast.cobalt" -#include "cast.cobalt" - -/* IMPORT CODE GENERATORS */ -#include "generator.cobalt" - -/* IMPORT TEMPLATES */ -#include "template.cobalt" -/************************/ - - - -/** API **/ -/* -includes functions that wrap around the generators, templates, -and parsers to provide a simple interface for the user/developer -using rpyc -*/ - -/* functions for parsing */ -function self::ParsePython(code){ - return pyast(code) -} -function self::ParseCpp(code){ - return cppast(code) -} -function self::ParseC(code){ - return cast(code) -} - -/* functions for generating code */ -function self::Generate(ast){ - return generator(ast) -} - -/* functions for loading template */ -function self.loadTemplate(path, format){ - return template(path, format) -} - -/****** INTERFACE ********/ -/* -operated by the CLI -*/ -#define VERSION "3.0.0" -#define TAB "\t\b\b\b\b" -function usage(){ - print("\n"..[[usage: rpyc [options] -Available options are: -]]..TAB..[[-o file output to 'file' (as a file or directory if -d is used) -]]..TAB..[[-i file input from 'file' -]]..TAB..[[-d compile all files in current directory -]]..TAB..[[-w watch mode (directory only) -]]..TAB..[[-v show version information -]]..TAB..[[-vd show version number only]]) - os.exit(0) -} -function version(){ - print("\27[1;34mcopyright:\27[0m roblox-pyc \27[1m"..VERSION.."\27[0m licensed under the GNU Affero General Public License by \27[1m@AsynchronousAI\27[0m") - os.exit(0) -} -if (arg){ // being run through interpreter or AOT compiler - if (#arg < 1){ - print("rpyc: no arguments provided") - usage() - } - var flags = {} - for (i, v in pairs(arg)){ - if (v == "cobalt" || v == "cobaltc.byte") continue - if (string.get(v, 1) == "-"){ - flags[#flags+1] = v - }else{ - warn("Unhandled argument: "..v) - } - } - - /* Run flags */ - var mode = "none" - for (i, v in pairs(flags)){ - if (v == "-w") { - /* Watch mode*/ - }else if (v == "-h" || v == "--help") { - usage() - }else if (v == "-v" || v == "--version") { - version() - }else if (v == "-vd") { - print(VERSION) - }else if (v == "-w") { - mode = "watch" - }else if (v->split(":")[1] == "-i") { - mode = "file" - if (#v->split(":") != 2) error("-i flag requires a file and only one file\n\tExample: -i:main.py") - _G.infile = v->split(":")[2] - }else if (v->split(":")[1] == "-o") { - mode = "file" - if (#v->split(":") != 2) error("-o flag requires a file and only one file\n\tExample: -o:main.lua") - _G.outfile = v->split(":")[2] - }else if (v->split(":")[1] == "-d") { - mode = "directory" - _G.directory = require("file").currentdir() - } - } - - /* Operate mode */ - if (mode == "none"){ - error("No mode specified") - } - -} - -return self \ No newline at end of file diff --git a/src/init.py b/src/init.py new file mode 100644 index 0000000..50c2940 --- /dev/null +++ b/src/init.py @@ -0,0 +1,74 @@ +import sys + +VERSION = "3.0.0" +TAB = "\t\b\b\b\b" + +def warn(msg): + print("\033[1;33m" + "warning: " + "\033[0m" + msg) +def info(msg): + print("\033[1;32m" + "info: " + "\033[0m" + msg) +def error(msg): + print("\033[1;31m" + "error: " + "\033[0m" + msg) + sys.exit() + +def usage(): + print("\n"+f"""usage: rbxpy [options] +Available options are: +{tab}-o file output to 'file' (as a file or directory if -d is used) +{tab}-i file input from 'file' +{tab}-d compile all files in current directory +{tab}-w watch mode (directory only) +{tab}-v show version information +{tab}-vd show version number only""") + sys.exit() + +def version(): + print("\033[1;34m" + "copyright:" + "\033[0m" + " roblox-pyc " + "\033[1m" + VERSION + "\033[0m" + " licensed under the GNU Affero General Public License by " + "\033[1m" + "@AsynchronousAI" + "\033[0m") + os.exit(0) + +arg = sys.argv +if (arg): # being run through interpreter or AOT compiler + if (len(arg) < 1): + print("rbxpy: no arguments provided") + usage() + + flags = {} + for v in (arg): + if (v[1] == "-") : + flags[len(flags)+1] = v + else: + warn("Unhandled argument: " + v) + + # Run flags + mode = "none" + input = "" + output = "" + capture = False + + for v in (flags): + if capture: + capture = False + continue + + if (v == "-h" or v == "--help"): + usage() + elif (v == "-v" or v == "--version"): + version() + elif (v == "-vd"): + print(VERSION) + elif (v == "-w"): + mode = "watch" + elif (v == "-o"): + capture = True + mode = "output" + output = flags[i+1] + elif not capture: + input = v + + + # Operate mode + if (mode == "none"): + error("No mode specified") +else: # being run through the interpreter + print("rbxpy: no arguments provided") + usage() \ No newline at end of file diff --git a/src/pyast.cobalt b/src/pyast.cobalt deleted file mode 100644 index 425328c..0000000 --- a/src/pyast.cobalt +++ /dev/null @@ -1,48 +0,0 @@ -// Python code to astclass -/* -The nodevisitor itself is written in Cobalt -but is wrapped around a python auto-generated -parser & visior runner. -*/ -py = import("python") - -gen = "" /* string to be automatically written by visitor */ -visitor = { - visit_FunctionDef: function(node) { - print("FunctionDef: " .. node.name) - }, - visit_ClassDef: function(node) { - print("ClassDef: " .. node.name) - }, - visit_Assign: function(node) { - print("Assign: " .. node.targets[0].id) - }, -} - - - -pyast = function(code){ - /* Runs the visitor */ - tree = {} - source = ([[ - import ast - import cobalt - visitors = cobalt.globals().visitor - code = cobalt.globals().code - tree = ast.parse(code) - - class Visitor(ast.NodeVisitor):]]) - - for (i, v in pairs(visitor)) { - source = source .. ("\n\t"..[[def ]]..i..[[(self, node): - ]].."\t\t"..[[visitors.]]..i..[[(node) - ]].."\t\t"..[[self.generic_visit(node)]]) - } - - source = source.."\n"..[[ - visitor = Visitor() - visitor.visit(tree) - ]] - py.execute(source) - return gen; -} \ No newline at end of file diff --git a/src/template.cobalt b/src/template.cobalt deleted file mode 100644 index 55a0f34..0000000 --- a/src/template.cobalt +++ /dev/null @@ -1,6 +0,0 @@ -// Template manager, allows you to duplicate template. -function new(format){ - // Duplicate `scriptpath`/template to cwd - var scriptpath = debug.getinfo(1).source->sub(2) - var scriptdir = scriptpath->match("(.*/)") -} \ No newline at end of file diff --git a/src/template/.gitignore b/src/template/.gitignore deleted file mode 100644 index a6f495e..0000000 --- a/src/template/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# Project place file -/template.rbxlx - -# Roblox Studio lock files -/*.rbxlx.lock -/*.rbxl.lock \ No newline at end of file diff --git a/src/template/README.md b/src/template/README.md deleted file mode 100644 index f681d4f..0000000 --- a/src/template/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# template -Generated by [Rojo](https://github.com/rojo-rbx/rojo) 7.3.0. Modified template -for roblox-pyc. - - -## Getting Started -To compile the `/src` folder, use: -```bash -rpyc . -# Quickly compiles all files in the source folder to the compiled folder. -``` -To build the place from scratch, use: - -```bash -rojo build -o "template.rbxlx" -# Generates a place file from the compiled folder. -``` - -Next, open `template.rbxlx` in Roblox Studio and start the Rojo server: - -```bash -rojo serve -``` - -For more help, check out [the Rojo documentation](https://rojo.space/docs). \ No newline at end of file diff --git a/src/template/compiled/client/init.client.example b/src/template/compiled/client/init.client.example deleted file mode 100644 index 692fe35..0000000 --- a/src/template/compiled/client/init.client.example +++ /dev/null @@ -1 +0,0 @@ -print("Hello world, from client!") diff --git a/src/template/compiled/server/init.server.example b/src/template/compiled/server/init.server.example deleted file mode 100644 index 062823b..0000000 --- a/src/template/compiled/server/init.server.example +++ /dev/null @@ -1 +0,0 @@ -print("Hello world, from server!") diff --git a/src/template/compiled/shared/Hello.example b/src/template/compiled/shared/Hello.example deleted file mode 100644 index bd356c1..0000000 --- a/src/template/compiled/shared/Hello.example +++ /dev/null @@ -1,3 +0,0 @@ -return function() - print("Hello, world!") -end diff --git a/src/template/default.project.json b/src/template/default.project.json deleted file mode 100644 index bd1e7fa..0000000 --- a/src/template/default.project.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "name": "template", - "tree": { - "$className": "DataModel", - - "ReplicatedStorage": { - "Shared": { - "$path": "compiled/shared" - } - }, - - "ServerScriptService": { - "Server": { - "$path": "compiled/server" - } - }, - - "StarterPlayer": { - "StarterPlayerScripts": { - "Client": { - "$path": "compiled/client" - } - } - }, - - "Workspace": { - "$properties": { - "FilteringEnabled": true - }, - "Baseplate": { - "$className": "Part", - "$properties": { - "Anchored": true, - "Color": [ - 0.38823, - 0.37254, - 0.38823 - ], - "Locked": true, - "Position": [ - 0, - -10, - 0 - ], - "Size": [ - 512, - 20, - 512 - ] - } - } - }, - "Lighting": { - "$properties": { - "Ambient": [ - 0, - 0, - 0 - ], - "Brightness": 2, - "GlobalShadows": true, - "Outlines": false, - "Technology": "Voxel" - } - }, - "SoundService": { - "$properties": { - "RespectFilteringEnabled": true - } - } - } -} \ No newline at end of file diff --git a/src/template/src/client/init.client.lua b/src/template/src/client/init.client.lua deleted file mode 100644 index 692fe35..0000000 --- a/src/template/src/client/init.client.lua +++ /dev/null @@ -1 +0,0 @@ -print("Hello world, from client!") diff --git a/src/template/src/server/init.server.lua b/src/template/src/server/init.server.lua deleted file mode 100644 index 062823b..0000000 --- a/src/template/src/server/init.server.lua +++ /dev/null @@ -1 +0,0 @@ -print("Hello world, from server!") diff --git a/src/template/src/shared/Hello.lua b/src/template/src/shared/Hello.lua deleted file mode 100644 index bd356c1..0000000 --- a/src/template/src/shared/Hello.lua +++ /dev/null @@ -1,3 +0,0 @@ -return function() - print("Hello, world!") -end diff --git a/src/util.cobalt b/src/util.cobalt deleted file mode 100644 index 0d5da24..0000000 --- a/src/util.cobalt +++ /dev/null @@ -1,53 +0,0 @@ -// util.cobalt - Utility functions for roblox-pyc - -/* Functions: -- backwardreplace(s, old, new, occurences) -- filerfolder(path) -- genLib() -- endswith(txt, end) -*/ - -file = import("file"); - -function backwardreplace(s, old, new, occurences){ - // Example: - /* - backwardreplace("x.py.py", ".py", ".lua", 1) - > x.py.lua - */ - var newstring = s; - var i = 0; - while (i < occurences){ - newstring = string.sub(newstring, 1, #(newstring) - #(old)) .. new; - i++; - } - - return newstring; -} - -function endswith(txt, end){ - // Returns true if `txt` ends with `end` - return string.sub(txt, -#(end)) == end; -} - -function filterfolder(path, ext){ - // use file (LuaFileSystem) to get remove all . files inside of the folder relative to the cwd - var directory = file.currentdir() .. path; - var pfile - if (os.getenv("OS") == "Windows_NT"){ - pfile = io.popen('dir /b "'..directory..'"'); - }else{ - pfile = io.popen('ls -a "'..directory..'"'); - } - var rt = {}; - - for (filename in pfile->lines()) { - if (!(filename == "." || filename == "..") && endswith(filename, ext)){ - os.remove(directory .. "/" .. filename) - } - } - - pfile->close(); - - return true; -} \ No newline at end of file From b7c70ae27f3ec57b321f20af0d3ef9a8e6e1e442 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Mon, 2 Oct 2023 18:36:34 -0500 Subject: [PATCH 22/77] Working copy --- COPYRIGHTS.txt => COPYRIGHTS.md | 4 +- src/init.py | 978 ++++++++++++++++++++++++++++++-- test.py | 2 + 3 files changed, 931 insertions(+), 53 deletions(-) rename COPYRIGHTS.txt => COPYRIGHTS.md (99%) create mode 100644 test.py diff --git a/COPYRIGHTS.txt b/COPYRIGHTS.md similarity index 99% rename from COPYRIGHTS.txt rename to COPYRIGHTS.md index a6bc8c1..f616149 100644 --- a/COPYRIGHTS.txt +++ b/COPYRIGHTS.md @@ -1,4 +1,4 @@ -Pythonlua: +# Pythonlua: Apache License Version 2.0, January 2004 @@ -202,7 +202,7 @@ Pythonlua: See the License for the specific language governing permissions and limitations under the License. -TextBoxPlus and Highlighter (used in roblox plugin): +# TextBoxPlus and Highlighter (used in roblox plugin): MIT License diff --git a/src/init.py b/src/init.py index 50c2940..ab7fc9a 100644 --- a/src/init.py +++ b/src/init.py @@ -1,5 +1,883 @@ -import sys +#!/usr/bin/env python3 +import sys, ast, yaml +import ast +import os +from pathlib import Path +#### COMPILER #### +"""Config""" +class Config: + """Translator config.""" + def __init__(self, filename=None): + self.data = { + "class": { + "return_at_the_end": False, + }, + } + + if filename is not None: + self.load(filename) + + def load(self, filename): + """Load config from the file""" + try: + with open(filename, "r") as stream: + data = yaml.load(stream) + self.data.update(data) + except FileNotFoundError: + pass # Use a default config if the file not found + except yaml.YAMLError as ex: + print(ex) + + def __getitem__(self, key): + """Get data values""" + return self.data[key] + +"""Binary operation description""" +_DEFAULT_BIN_FORMAT = "{left} {operation} {right}" + + +class BinaryOperationDesc: + """Binary operation description""" + + OPERATION = { + ast.Add: { + "value": "+", + "format": _DEFAULT_BIN_FORMAT, + }, + ast.Sub: { + "value": "-", + "format": _DEFAULT_BIN_FORMAT, + }, + ast.Mult: { + "value": "*", + "format": _DEFAULT_BIN_FORMAT, + }, + ast.Div: { + "value": "/", + "format": _DEFAULT_BIN_FORMAT, + }, + ast.Mod: { + "value": "", + "format": "math.fmod({left}, {right})", + }, + ast.Pow: { + "value": "", + "format": "math.pow({left}, {right})", + }, + ast.FloorDiv: { + "value": "/", + "format": "math.floor({left} {operation} {right})", + }, + ast.LShift: { + "value": "", + "format": "bit32.lshift({left}, {right})", + }, + ast.RShift: { + "value": "", + "format": "bit32.rshift({left}, {right})", + }, + ast.BitOr: { + "value": "", + "format": "bit32.bor({left}, {right})", + }, + ast.BitAnd: { + "value": "", + "format": "bit32.band({left}, {right})", + }, + ast.BitXor: { + "value": "", + "format": "bit32.bxor({left}, {right})", + }, + } +"""Boolean operation description""" +_DEFAULT_BOOL_FORMAT = "{left} {operation} {right}" + + +class BooleanOperationDesc: + """Binary operation description""" + + OPERATION = { + ast.And: { + "value": "and", + "format": _DEFAULT_BOOL_FORMAT, + }, + ast.Or: { + "value": "or", + "format": _DEFAULT_BOOL_FORMAT, + }, + } + +"""Compare operation description""" + +class CompareOperationDesc: + """Compare operation description""" + + OPERATION = { + ast.Eq: "==", + ast.NotEq: "~=", + ast.Lt: "<", + ast.LtE: "<=", + ast.Gt: ">", + ast.GtE: ">=", + ast.In: { + "format": "operator_in({left}, {right})", + }, + ast.NotIn: { + "format": "not operator_in({left}, {right})", + }, + } + +"""Name constant description""" + + +class NameConstantDesc: + """Name constant description""" + + NAME = { + None: "nil", + True: "true", + False: "false", + } + +"""Unary operation description""" +import ast + + +_DEFAULT_UN_FORMAT = "{operation}{value}" + + +class UnaryOperationDesc: + """Unary operation description""" + + OPERATION = { + ast.USub: { + "value": "-", + "format": _DEFAULT_UN_FORMAT, + }, + ast.UAdd: { + "value": "", + "format": _DEFAULT_UN_FORMAT, + }, + ast.Not: { + "value": "not", + "format": "not {value}", + }, + ast.Invert: { + "value": "~", + "format": "bit32.bnot({value})", + }, + } +"""Token end mode""" +from enum import Enum + + +class TokenEndMode(Enum): + """This enum represents token end mode""" + LINE_FEED = 0 + LINE_CONTINUE = 1 + +"""Class for the symbols stack""" +class SymbolsStack: + """Class for the symbols stack""" + def __init__(self): + self.symbols = [[]] + + def add_symbol(self, name): + """Add a new symbol to the curent stack""" + self.symbols[-1].append(name) + + def exists(self, name): + """Check symbol is exists in the current stack""" + for stack in self.symbols: + if name in stack: + return True + return False + + def push(self): + """Push the symbols stack""" + self.symbols.append([]) + + def pop(self): + """Pop the symbols stack""" + self.symbols.pop() + +"""Class to store the python code context""" +class Context: + """Class to store the python code context""" + def __init__(self, values=None): + values = values if values is not None else { + "token_end_mode": TokenEndMode.LINE_FEED, + "class_name": "", + "locals": SymbolsStack(), + "globals": SymbolsStack(), # Not working yet + "loop_label_name": "", + "docstring": False, + } + + self.ctx_stack = [values] + + def last(self): + """Return actual context state""" + return self.ctx_stack[-1] + + def push(self, values): + """Push new context state with new values""" + value = self.ctx_stack[-1].copy() + value.update(values) + self.ctx_stack.append(value) + + def pop(self): + """Pop last context state""" + assert len(self.ctx_stack) > 1, "Pop context failed. This is a last context in the stack." + return self.ctx_stack.pop() + +"""Label counter for the loops continue""" +class LoopCounter: + """Loop counter""" + COUNTER = 0 + + @staticmethod + def get_next(): + """Return next loop continue label name""" + LoopCounter.COUNTER += 1 + return "loop_label_{}".format(LoopCounter.COUNTER) + +"""Node visitor""" +class NodeVisitor(ast.NodeVisitor): + LUACODE = "[[luacode]]" + + """Node visitor""" + def __init__(self, context=None, config=None): + self.context = context if context is not None else Context() + self.config = config + self.last_end_mode = TokenEndMode.LINE_FEED + self.output = [] + + def visit_Assign(self, node): + """Visit assign""" + target = self.visit_all(node.targets[0], inline=True) + value = self.visit_all(node.value, inline=True) + + local_keyword = "" + + last_ctx = self.context.last() + + if last_ctx["class_name"]: + target = ".".join([last_ctx["class_name"], target]) + + if "." not in target and not last_ctx["locals"].exists(target): + local_keyword = "local " + last_ctx["locals"].add_symbol(target) + + + self.emit("{local}{target} = {value}".format(local=local_keyword, + target=target, + value=value)) + + def visit_AugAssign(self, node): + """Visit augassign""" + operation = BinaryOperationDesc.OPERATION[node.op.__class__] + + target = self.visit_all(node.target, inline=True) + + values = { + "left": target, + "right": self.visit_all(node.value, inline=True), + "operation": operation["value"], + } + + line = "({})".format(operation["format"]) + line = line.format(**values) + + self.emit("{target} = {line}".format(target=target, line=line)) + + def visit_Attribute(self, node): + """Visit attribute""" + line = "{object}.{attr}" + values = { + "object": self.visit_all(node.value, True), + "attr": node.attr, + } + self.emit(line.format(**values)) + + def visit_BinOp(self, node): + """Visit binary operation""" + operation = BinaryOperationDesc.OPERATION[node.op.__class__] + line = "({})".format(operation["format"]) + values = { + "left": self.visit_all(node.left, True), + "right": self.visit_all(node.right, True), + "operation": operation["value"], + } + + self.emit(line.format(**values)) + + def visit_BoolOp(self, node): + """Visit boolean operation""" + operation = BooleanOperationDesc.OPERATION[node.op.__class__] + line = "({})".format(operation["format"]) + values = { + "left": self.visit_all(node.values[0], True), + "right": self.visit_all(node.values[1], True), + "operation": operation["value"], + } + + self.emit(line.format(**values)) + + def visit_Break(self, node): + """Visit break""" + self.emit("break") + + def visit_Call(self, node): + """Visit function call""" + line = "{name}({arguments})" + + name = self.visit_all(node.func, inline=True) + arguments = [self.visit_all(arg, inline=True) for arg in node.args] + + self.emit(line.format(name=name, arguments=", ".join(arguments))) + + def visit_ClassDef(self, node): + """Visit class definition""" + bases = [self.visit_all(base, inline=True) for base in node.bases] + + local_keyword = "" + last_ctx = self.context.last() + if not last_ctx["class_name"] and not last_ctx["locals"].exists(node.name): + local_keyword = "local " + last_ctx["locals"].add_symbol(node.name) + + name = node.name + if last_ctx["class_name"]: + name = ".".join([last_ctx["class_name"], name]) + + values = { + "local": local_keyword, + "name": name, + "node_name": node.name, + } + + self.emit("{local}{name} = class(function({node_name})".format(**values)) + + self.context.push({"class_name": node.name}) + self.visit_all(node.body) + self.context.pop() + + self.output[-1].append("return {node_name}".format(**values)) + + self.emit("end, {{{}}})".format(", ".join(bases))) + + # Return class object only in the top-level classes. + # Not in the nested classes. + if self.config["class"]["return_at_the_end"] and not last_ctx["class_name"]: + self.emit("return {}".format(name)) + + def visit_Compare(self, node): + """Visit compare""" + + line = "" + + left = self.visit_all(node.left, inline=True) + for i in range(len(node.ops)): + operation = node.ops[i] + operation = CompareOperationDesc.OPERATION[operation.__class__] + + right = self.visit_all(node.comparators[i], inline=True) + + values = { + "left": left, + "right": right, + } + + if isinstance(operation, str): + values["op"] = operation + line += "{left} {op} {right}".format(**values) + elif isinstance(operation, dict): + line += operation["format"].format(**values) + + if i < len(node.ops) - 1: + left = right + line += " and " + + self.emit("({})".format(line)) + + def visit_Continue(self, node): + """Visit continue""" + last_ctx = self.context.last() + line = "goto {}".format(last_ctx["loop_label_name"]) + self.emit(line) + + def visit_Delete(self, node): + """Visit delete""" + targets = [self.visit_all(target, inline=True) for target in node.targets] + nils = ["nil" for _ in targets] + line = "{targets} = {nils}".format(targets=", ".join(targets), + nils=", ".join(nils)) + self.emit(line) + + def visit_Dict(self, node): + """Visit dictionary""" + keys = [] + + for key in node.keys: + value = self.visit_all(key, inline=True) + if isinstance(key, ast.Str): + value = "[{}]".format(value) + keys.append(value) + + values = [self.visit_all(item, inline=True) for item in node.values] + + elements = ["{} = {}".format(keys[i], values[i]) for i in range(len(keys))] + elements = ", ".join(elements) + self.emit("dict {{{}}}".format(elements)) + + def visit_DictComp(self, node): + """Visit dictionary comprehension""" + self.emit("(function()") + self.emit("local result = dict {}") + + ends_count = 0 + + for comp in node.generators: + line = "for {target} in {iterator} do" + values = { + "target": self.visit_all(comp.target, inline=True), + "iterator": self.visit_all(comp.iter, inline=True), + } + line = line.format(**values) + self.emit(line) + ends_count += 1 + + for if_ in comp.ifs: + line = "if {} then".format(self.visit_all(if_, inline=True)) + self.emit(line) + ends_count += 1 + + line = "result[{key}] = {value}" + values = { + "key": self.visit_all(node.key, inline=True), + "value": self.visit_all(node.value, inline=True), + } + self.emit(line.format(**values)) + + self.emit(" ".join(["end"] * ends_count)) + + self.emit("return result") + self.emit("end)()") + + def visit_Ellipsis(self, node): + """Visit ellipsis""" + self.emit("...") + + def visit_Expr(self, node): + """Visit expr""" + expr_is_docstring = False + if isinstance(node.value, ast.Str): + expr_is_docstring = True + + self.context.push({"docstring": expr_is_docstring}) + output = self.visit_all(node.value) + self.context.pop() + + self.output.append(output) + + def visit_FunctionDef(self, node): + """Visit function definition""" + line = "{local}function {name}({arguments})" + + last_ctx = self.context.last() + + name = node.name + if last_ctx["class_name"]: + name = ".".join([last_ctx["class_name"], name]) + + arguments = [arg.arg for arg in node.args.args] + + if node.args.vararg is not None: + arguments.append("...") + + local_keyword = "" + + if "." not in name and not last_ctx["locals"].exists(name): + local_keyword = "local " + last_ctx["locals"].add_symbol(name) + + function_def = line.format(local=local_keyword, + name=name, + arguments=", ".join(arguments)) + + self.emit(function_def) + + self.context.push({"class_name": ""}) + self.visit_all(node.body) + self.context.pop() + + body = self.output[-1] + + if node.args.vararg is not None: + line = "local {name} = list {{...}}".format(name=node.args.vararg.arg) + body.insert(0, line) + + arg_index = -1 + for i in reversed(node.args.defaults): + line = "{name} = {name} or {value}" + + arg = node.args.args[arg_index] + values = { + "name": arg.arg, + "value": self.visit_all(i, inline=True), + } + body.insert(0, line.format(**values)) + + arg_index -= 1 + + self.emit("end") + + for decorator in reversed(node.decorator_list): + decorator_name = self.visit_all(decorator, inline=True) + values = { + "name": name, + "decorator": decorator_name, + } + line = "{name} = {decorator}({name})".format(**values) + self.emit(line) + + def visit_For(self, node): + """Visit for loop""" + line = "for {target} in {iter} do" + + values = { + "target": self.visit_all(node.target, inline=True), + "iter": self.visit_all(node.iter, inline=True), + } + + self.emit(line.format(**values)) + + continue_label = LoopCounter.get_next() + self.context.push({ + "loop_label_name": continue_label, + }) + self.visit_all(node.body) + self.context.pop() + + #self.output[-1].append("::{}::".format(continue_label)) 5.2 + + self.emit("end") + + def visit_Global(self, node): + """Visit globals""" + last_ctx = self.context.last() + for name in node.names: + last_ctx["globals"].add_symbol(name) + + def visit_If(self, node): + """Visit if""" + test = self.visit_all(node.test, inline=True) + + line = "if {} then".format(test) + + self.emit(line) + self.visit_all(node.body) + + if node.orelse: + if isinstance(node.orelse[0], ast.If): + elseif = node.orelse[0] + elseif_test = self.visit_all(elseif.test, inline=True) + + line = "elseif {} then".format(elseif_test) + self.emit(line) + + output_length = len(self.output) + self.visit_If(node.orelse[0]) + + del self.output[output_length] + del self.output[-1] + else: + self.emit("else") + self.visit_all(node.orelse) + + self.emit("end") + + def visit_IfExp(self, node): + """Visit if expression""" + line = "{cond} and {true_cond} or {false_cond}" + values = { + "cond": self.visit_all(node.test, inline=True), + "true_cond": self.visit_all(node.body, inline=True), + "false_cond": self.visit_all(node.orelse, inline=True), + } + + self.emit(line.format(**values)) + + def visit_Import(self, node): + """Visit import""" + line = 'local {asname} = require "{name}"' + values = {"asname": "", "name": ""} + + if node.names[0].asname is None: + values["name"] = node.names[0].name + values["asname"] = values["name"] + values["asname"] = values["asname"].split(".")[-1] + else: + values["asname"] = node.names[0].asname + values["name"] = node.names[0].name + + self.emit(line.format(**values)) + + def visit_Index(self, node): + """Visit index""" + self.emit(self.visit_all(node.value, inline=True)) + + def visit_Lambda(self, node): + """Visit lambda""" + line = "function({arguments}) return" + + arguments = [arg.arg for arg in node.args.args] + + function_def = line.format(arguments=", ".join(arguments)) + + output = [] + output.append(function_def) + output.append(self.visit_all(node.body, inline=True)) + output.append("end") + + self.emit(" ".join(output)) + + def visit_List(self, node): + """Visit list""" + elements = [self.visit_all(item, inline=True) for item in node.elts] + line = "list {{{}}}".format(", ".join(elements)) + self.emit(line) + + def visit_ListComp(self, node): + """Visit list comprehension""" + self.emit("(function()") + self.emit("local result = list {}") + + ends_count = 0 + + for comp in node.generators: + line = "for {target} in {iterator} do" + values = { + "target": self.visit_all(comp.target, inline=True), + "iterator": self.visit_all(comp.iter, inline=True), + } + line = line.format(**values) + self.emit(line) + ends_count += 1 + + for if_ in comp.ifs: + line = "if {} then".format(self.visit_all(if_, inline=True)) + self.emit(line) + ends_count += 1 + + line = "result.append({})" + line = line.format(self.visit_all(node.elt, inline=True)) + self.emit(line) + + self.emit(" ".join(["end"] * ends_count)) + + self.emit("return result") + self.emit("end)()") + + def visit_Module(self, node): + """Visit module""" + self.visit_all(node.body) + self.output = self.output[0] + + def visit_Name(self, node): + """Visit name""" + self.emit(node.id) + + def visit_NameConstant(self, node): + """Visit name constant""" + self.emit(NameConstantDesc.NAME[node.value]) + + def visit_Num(self, node): + """Visit number""" + self.emit(str(node.n)) + + def visit_Pass(self, node): + """Visit pass""" + pass + + def visit_Return(self, node): + """Visit return""" + line = "return " + line += self.visit_all(node.value, inline=True) + self.emit(line) + + def visit_Starred(self, node): + """Visit starred object""" + value = self.visit_all(node.value, inline=True) + line = "unpack({})".format(value) + self.emit(line) + + def visit_Str(self, node): + """Visit str""" + value = node.s + if value.startswith(NodeVisitor.LUACODE): + value = value[len(NodeVisitor.LUACODE):] + self.emit(value) + elif self.context.last()["docstring"]: + self.emit('--[[ {} ]]'.format(node.s)) + else: + self.emit('"{}"'.format(node.s)) + + def visit_Subscript(self, node): + """Visit subscript""" + line = "{name}[{index}]" + values = { + "name": self.visit_all(node.value, inline=True), + "index": self.visit_all(node.slice, inline=True), + } + + self.emit(line.format(**values)) + + def visit_Tuple(self, node): + """Visit tuple""" + elements = [self.visit_all(item, inline=True) for item in node.elts] + self.emit(", ".join(elements)) + + def visit_UnaryOp(self, node): + """Visit unary operator""" + operation = UnaryOperationDesc.OPERATION[node.op.__class__] + value = self.visit_all(node.operand, inline=True) + + line = operation["format"] + values = { + "value": value, + "operation": operation["value"], + } + + self.emit(line.format(**values)) + + def visit_While(self, node): + """Visit while""" + test = self.visit_all(node.test, inline=True) + + self.emit("while {} do".format(test)) + + continue_label = LoopCounter.get_next() + self.context.push({ + "loop_label_name": continue_label, + }) + self.visit_all(node.body) + self.context.pop() + + #self.output[-1].append("::{}::".format(continue_label)) 5.2 + + self.emit("end") + + def visit_With(self, node): + """Visit with""" + self.emit("do") + + self.visit_all(node.body) + + body = self.output[-1] + lines = [] + for i in node.items: + line = "" + if i.optional_vars is not None: + line = "local {} = " + line = line.format(self.visit_all(i.optional_vars, + inline=True)) + line += self.visit_all(i.context_expr, inline=True) + lines.append(line) + + for line in lines: + body.insert(0, line) + + self.emit("end") + + def generic_visit(self, node): + """Unknown nodes handler""" + error("Unknown node: {}".format(node)) + + def visit_all(self, nodes, inline=False): + """Visit all nodes in the given list""" + + if not inline: + last_ctx = self.context.last() + last_ctx["locals"].push() + + visitor = NodeVisitor(context=self.context, config=self.config) + + if isinstance(nodes, list): + for node in nodes: + visitor.visit(node) + if not inline: + self.output.append(visitor.output) + else: + visitor.visit(nodes) + if not inline: + self.output.extend(visitor.output) + + if not inline: + last_ctx = self.context.last() + last_ctx["locals"].pop() + + if inline: + return " ".join(visitor.output) + + def emit(self, value): + """Add translated value to the output""" + self.output.append(value) + +"""Python to lua translator class""" +class Translator: + """Python to lua main class translator""" + def __init__(self, config=None, show_ast=False): + self.config = config if config is not None else Config() + self.show_ast = show_ast + + self.output = [] + + def translate(self, pycode): + """Translate python code to lua code""" + py_ast_tree = ast.parse(pycode) + + visitor = NodeVisitor(config=self.config) + + if self.show_ast: + print(ast.dump(py_ast_tree)) + + visitor.visit(py_ast_tree) + + self.output = visitor.output + + return self.to_code() + + def to_code(self, code=None, indent=0): + """Create a lua code from the compiler output""" + code = code if code is not None else self.output + + def add_indentation(line): + """Add indentation to the given line""" + indentation_width = 4 + indentation_space = " " + + indent_copy = max(indent, 0) + + return indentation_space * indentation_width * indent_copy + line + + lines = [] + for line in code: + if isinstance(line, str): + lines.append(add_indentation(line)) + elif isinstance(line, list): + sub_code = self.to_code(line, indent + 1) + lines.append(sub_code) + + return "\n".join(lines) + + @staticmethod + def get_luainit(): # Return STDlib + return """""" + +#### INTERFACE #### VERSION = "3.0.0" TAB = "\t\b\b\b\b" @@ -12,63 +890,61 @@ def error(msg): sys.exit() def usage(): - print("\n"+f"""usage: rbxpy [options] + print("\n"+f"""usage: rbxpy [file] [options] > [gen] Available options are: -{tab}-o file output to 'file' (as a file or directory if -d is used) -{tab}-i file input from 'file' -{tab}-d compile all files in current directory -{tab}-w watch mode (directory only) {tab}-v show version information -{tab}-vd show version number only""") +{tab}-vd show version number only" +{tab}-ast show python ast tree before code +{tab}-s generate std.lua +{tab}-u open this""") sys.exit() def version(): print("\033[1;34m" + "copyright:" + "\033[0m" + " roblox-pyc " + "\033[1m" + VERSION + "\033[0m" + " licensed under the GNU Affero General Public License by " + "\033[1m" + "@AsynchronousAI" + "\033[0m") - os.exit(0) - -arg = sys.argv -if (arg): # being run through interpreter or AOT compiler - if (len(arg) < 1): - print("rbxpy: no arguments provided") - usage() - - flags = {} - for v in (arg): - if (v[1] == "-") : - flags[len(flags)+1] = v - else: - warn("Unhandled argument: " + v) + sys.exit(0) - # Run flags - mode = "none" - input = "" - output = "" - capture = False +"""The main entry point to the translator""" +def main(): + """Entry point function to the translator""" + args = sys.argv[1:] + ast = False + input_filename = "NONE" - for v in (flags): - if capture: - capture = False - continue - - if (v == "-h" or v == "--help"): - usage() - elif (v == "-v" or v == "--version"): + for arg in args: + if arg == "-v": version() - elif (v == "-vd"): + elif arg == "-vd": print(VERSION) - elif (v == "-w"): - mode = "watch" - elif (v == "-o"): - capture = True - mode = "output" - output = flags[i+1] - elif not capture: - input = v - - - # Operate mode - if (mode == "none"): - error("No mode specified") -else: # being run through the interpreter - print("rbxpy: no arguments provided") - usage() \ No newline at end of file + sys.exit() + elif arg == "-u": + usage() + elif arg == "-s": + error("In development") + elif arg == "-ast": + ast = True + else: + input_filename = arg + if input_filename == "NONE": + usage() + if not Path(input_filename).is_file(): + error( + "The given filename ('{}') is not a file.".format(input_filename)) + + content = None + with open(input_filename, "r") as file: + content = file.read() + + if not content: + error("The input file is empty.") + + translator = Translator(Config(".pyluaconf.yaml"), + show_ast=ast) + lua_code = translator.translate(content) + + if not ast: + print(lua_code) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..f81811c --- /dev/null +++ b/test.py @@ -0,0 +1,2 @@ +while True: + print("Hello World") \ No newline at end of file From cb9bde33e97ab23084090dc7141c74ddaede5868 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Mon, 2 Oct 2023 18:43:57 -0500 Subject: [PATCH 23/77] Fixes --- src/{init.py => __main__.py} | 6 +++--- test.py | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) rename src/{init.py => __main__.py} (99%) delete mode 100644 test.py diff --git a/src/init.py b/src/__main__.py similarity index 99% rename from src/init.py rename to src/__main__.py index ab7fc9a..5f199e8 100644 --- a/src/init.py +++ b/src/__main__.py @@ -882,11 +882,11 @@ def get_luainit(): # Return STDlib TAB = "\t\b\b\b\b" def warn(msg): - print("\033[1;33m" + "warning: " + "\033[0m" + msg) + sys.stderr.write("\033[1;33m" + "warning: " + "\033[0m" + msg) def info(msg): - print("\033[1;32m" + "info: " + "\033[0m" + msg) + sys.stderr.write("\033[1;32m" + "info: " + "\033[0m" + msg) def error(msg): - print("\033[1;31m" + "error: " + "\033[0m" + msg) + sys.stderr.write("\033[1;31m" + "error: " + "\033[0m" + msg + "\n") sys.exit() def usage(): diff --git a/test.py b/test.py deleted file mode 100644 index f81811c..0000000 --- a/test.py +++ /dev/null @@ -1,2 +0,0 @@ -while True: - print("Hello World") \ No newline at end of file From 2feec03a5d99770eef24c23d67acc1ab7ac1d11c Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Mon, 2 Oct 2023 18:59:04 -0500 Subject: [PATCH 24/77] Try, catch & raise --- .all-contributorsrc | 2 +- .gitignore | 5 ++++- README.md | 7 ------- src/__main__.py | 25 +++++++++++++++++++++++++ 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 30c2b3c..3657aea 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -57,6 +57,6 @@ "skipCi": true, "repoType": "github", "repoHost": "https://github.com", - "projectName": "roblox-pyc", + "projectName": "roblox-py", "projectOwner": "AsynchronousAI" } diff --git a/.gitignore b/.gitignore index 30369f1..c713b25 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,7 @@ # Cobalt preprocessed and compiled *.cii -*.byte \ No newline at end of file +*.byte + +test.py +test.lua \ No newline at end of file diff --git a/README.md b/README.md index d36347c..99b04c7 100644 --- a/README.md +++ b/README.md @@ -50,11 +50,4 @@ *** Python, C, C++ Compiler for Roblox. - -> This has NO RELATION with .pyc files, roblox-py, or roblox-ts - -> C/C++ is still in progress. - -> Python is fully implemented, all code should work because it supports the dev build of Python 3.13. - *** \ No newline at end of file diff --git a/src/__main__.py b/src/__main__.py index 5f199e8..0b8ed30 100644 --- a/src/__main__.py +++ b/src/__main__.py @@ -752,7 +752,32 @@ def visit_UnaryOp(self, node): } self.emit(line.format(**values)) + + def visit_Raise(self, node): + """Visit raise""" + line = "error({})".format(self.visit_all(node.exc, inline=True)) + self.emit(line) + + def visit_Try(self, node): + """Visit try""" + self.emit("xpcall(function()") + self.visit_all(node.body) + + self.emit("end, function(err)") + + self.visit_all(node.handlers) + + self.emit("end)") + + def visit_ExceptHandler(self, node): + """Visit exception handler""" + self.emit("if err:find('{}') then".format(node.type.id)) + + self.visit_all(node.body) + + self.emit("end") + def visit_While(self, node): """Visit while""" test = self.visit_all(node.test, inline=True) From 1a2ee0ae00614e7cad61742a578592a5cf077f47 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Mon, 2 Oct 2023 19:02:49 -0500 Subject: [PATCH 25/77] Header --- src/__main__.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/__main__.py b/src/__main__.py index 0b8ed30..db8d27d 100644 --- a/src/__main__.py +++ b/src/__main__.py @@ -4,6 +4,10 @@ import os from pathlib import Path +#### CONSTANTS #### +VERSION = "3.0.0" +TAB = "\t\b\b\b\b" + #### COMPILER #### """Config""" class Config: @@ -850,6 +854,9 @@ def visit_all(self, nodes, inline=False): def emit(self, value): """Add translated value to the output""" self.output.append(value) +"""Header""" +Header = f"""--// Generated by roblox-pyc v{VERSION} \\\\-- +""" """Python to lua translator class""" class Translator: @@ -873,7 +880,7 @@ def translate(self, pycode): self.output = visitor.output - return self.to_code() + return Header+self.to_code() def to_code(self, code=None, indent=0): """Create a lua code from the compiler output""" @@ -903,8 +910,6 @@ def get_luainit(): # Return STDlib return """""" #### INTERFACE #### -VERSION = "3.0.0" -TAB = "\t\b\b\b\b" def warn(msg): sys.stderr.write("\033[1;33m" + "warning: " + "\033[0m" + msg) From 8f2599d290266a8fd1e5e10fd745a06641ca19d2 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Mon, 2 Oct 2023 20:40:47 -0500 Subject: [PATCH 26/77] Handle classmethods --- src/__main__.py | 89 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/src/__main__.py b/src/__main__.py index db8d27d..bc50f72 100644 --- a/src/__main__.py +++ b/src/__main__.py @@ -249,7 +249,7 @@ def get_next(): """Node visitor""" class NodeVisitor(ast.NodeVisitor): - LUACODE = "[[luacode]]" + LUACODE = "luau" """Node visitor""" def __init__(self, context=None, config=None): @@ -493,10 +493,22 @@ def visit_FunctionDef(self, node): last_ctx = self.context.last() name = node.name + type = 1 # 1 = static, 2 = class + for decorator in reversed(node.decorator_list): + decorator_name = self.visit_all(decorator, inline=True) + + if decorator_name == "classmethod": + type = 2 + elif decorator_name == "staticmethod": + type = 1 if last_ctx["class_name"]: name = ".".join([last_ctx["class_name"], name]) - - arguments = [arg.arg for arg in node.args.args] + + if type == 1: + arguments = [arg.arg for arg in node.args.args] + else: + arguments = ["self"] + arguments.extend([arg.arg for arg in node.args.args]) if node.args.vararg is not None: arguments.append("...") @@ -540,6 +552,8 @@ def visit_FunctionDef(self, node): for decorator in reversed(node.decorator_list): decorator_name = self.visit_all(decorator, inline=True) + if decorator_name == "classmethod" or decorator_name == "staticmethod": + continue values = { "name": name, "decorator": decorator_name, @@ -575,6 +589,75 @@ def visit_Global(self, node): for name in node.names: last_ctx["globals"].add_symbol(name) + def visit_AsyncFunctionDef(self, node): + """Visit async function definition""" + line = "{local}function {name}({arguments}) coroutine.wrap(function()" + + last_ctx = self.context.last() + + name = node.name + if last_ctx["class_name"]: + name = ".".join([last_ctx["class_name"], name]) + + arguments = [arg.arg for arg in node.args.args] + + if node.args.vararg is not None: + arguments.append("...") + + local_keyword = "" + + if "." not in name and not last_ctx["locals"].exists(name): + local_keyword = "local " + last_ctx["locals"].add_symbol(name) + + function_def = line.format(local=local_keyword, + name=name, + arguments=", ".join(arguments)) + + self.emit(function_def) + + self.context.push({"class_name": ""}) + self.visit_all(node.body) + self.context.pop() + + body = self.output[-1] + + if node.args.vararg is not None: + line = "local {name} = list {{...}}".format(name=node.args.vararg.arg) + body.insert(0, line) + + arg_index = -1 + for i in reversed(node.args.defaults): + line = "{name} = {name} or {value}" + + arg = node.args.args[arg_index] + values = { + "name": arg.arg, + "value": self.visit_all(i, inline=True), + } + body.insert(0, line.format(**values)) + + arg_index -= 1 + + self.emit("end)() end") + + for decorator in reversed(node.decorator_list): + decorator_name = self.visit_all(decorator, inline=True) + values = { + "name": name, + "decorator": decorator_name, + } + line = "{name} = {decorator}({name})".format(**values) + self.emit(line) + + def visit_Await(self, node): + """Visit await""" + self.emit("coroutine.await({})".format(self.visit_all(node.value, inline=True))) + + def visit_Yield(self, node): + """Visit yield""" + self.emit("coroutine.yield({})".format(self.visit_all(node.value, inline=True))) + def visit_If(self, node): """Visit if""" test = self.visit_all(node.test, inline=True) From aa6d5d8820b8fe9c3e84970be75a5d8e0beda8fb Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Mon, 2 Oct 2023 21:14:14 -0500 Subject: [PATCH 27/77] Better syntax errors --- src/__main__.py | 52 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/src/__main__.py b/src/__main__.py index bc50f72..2883f7b 100644 --- a/src/__main__.py +++ b/src/__main__.py @@ -363,7 +363,8 @@ def visit_ClassDef(self, node): } self.emit("{local}{name} = class(function({node_name})".format(**values)) - + self.depend("class") + self.context.push({"class_name": node.name}) self.visit_all(node.body) self.context.pop() @@ -699,7 +700,7 @@ def visit_IfExp(self, node): def visit_Import(self, node): """Visit import""" - line = 'local {asname} = require "{name}"' + line = 'local {asname} = require("{name}")' values = {"asname": "", "name": ""} if node.names[0].asname is None: @@ -711,6 +712,33 @@ def visit_Import(self, node): values["name"] = node.names[0].name self.emit(line.format(**values)) + + def visit_ImportFrom(self, node): + """Visit import from""" + module = node.module + if module is None: + module = "" + else: + module = module + + for name in node.names: + if name.asname is None: + if name.name == "*": + error("import * is unsupproted") + else: + self.emit("local {name} = require(\"{module}\").{name}".format( + name=name.name, + module=module, + )) + else: + if name.name == "*": + error("import * is unsupproted") + else: + self.emit("local {name} = require(\"{module}\").{realname}".format( + name=name.asname, + module=module, + realname=name.name, + )) def visit_Index(self, node): """Visit index""" @@ -937,10 +965,21 @@ def visit_all(self, nodes, inline=False): def emit(self, value): """Add translated value to the output""" self.output.append(value) + def depend(self, value): + """Add dependency value to the object""" + self.dependencies.append(value) + """Header""" Header = f"""--// Generated by roblox-pyc v{VERSION} \\\\-- """ +def genDependencies(dependencies): + total = "" + for v in dependencies: + pass + + return total + """Python to lua translator class""" class Translator: """Python to lua main class translator""" @@ -952,8 +991,13 @@ def __init__(self, config=None, show_ast=False): def translate(self, pycode): """Translate python code to lua code""" - py_ast_tree = ast.parse(pycode) - + try: + # code that uses ast + py_ast_tree = ast.parse(pycode) + except SyntaxError as err: + sys.stderr.write("\033[1;31m" + "syntax error: " + "\033[0m" + str(err) + "\n") + sys.exit(1) + visitor = NodeVisitor(config=self.config) if self.show_ast: From ac881c3966e19b8e1044f00a03f36ea6ad5fabd1 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Mon, 2 Oct 2023 21:20:15 -0500 Subject: [PATCH 28/77] Avoid syntax errors --- src/__main__.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/__main__.py b/src/__main__.py index 2883f7b..8c2b840 100644 --- a/src/__main__.py +++ b/src/__main__.py @@ -303,6 +303,10 @@ def visit_Attribute(self, node): "object": self.visit_all(node.value, True), "attr": node.attr, } + # Does object start and end with a " " + if (values["object"].startswith('"') and values["object"].endswith('"')) or (values["object"].startswith('\'') and values["object"].endswith('\'')) or (values["object"].startswith('{') and values["object"].endswith('}')) or (values["object"].startswith('[') and values["object"].endswith(']')) or (values["object"].startswith('`') and values["object"].endswith('`')): + values["object"] = "({})".format(values["object"]) + self.emit(line.format(**values)) def visit_BinOp(self, node): @@ -435,11 +439,13 @@ def visit_Dict(self, node): elements = ["{} = {}".format(keys[i], values[i]) for i in range(len(keys))] elements = ", ".join(elements) + self.depend("dict") self.emit("dict {{{}}}".format(elements)) def visit_DictComp(self, node): """Visit dictionary comprehension""" self.emit("(function()") + self.depend("dict") self.emit("local result = dict {}") ends_count = 0 @@ -533,7 +539,8 @@ def visit_FunctionDef(self, node): body = self.output[-1] if node.args.vararg is not None: - line = "local {name} = list {{...}}".format(name=node.args.vararg.arg) + self.depend("list") + line = "local {name} = list({{...})".format(name=node.args.vararg.arg) body.insert(0, line) arg_index = -1 From b6fe89fc0f99813d4b3d2a2c117fcc8db8f4bdcf Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Tue, 3 Oct 2023 16:48:25 -0500 Subject: [PATCH 29/77] LUA to PYTHON --- src/__main__.py | 1178 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1153 insertions(+), 25 deletions(-) diff --git a/src/__main__.py b/src/__main__.py index 8c2b840..18cbd36 100644 --- a/src/__main__.py +++ b/src/__main__.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -import sys, ast, yaml -import ast -import os +import sys, ast, yaml, re, os, astunparse +from pprint import pprint from pathlib import Path #### CONSTANTS #### @@ -145,8 +144,6 @@ class NameConstantDesc: } """Unary operation description""" -import ast - _DEFAULT_UN_FORMAT = "{operation}{value}" @@ -977,8 +974,9 @@ def depend(self, value): self.dependencies.append(value) """Header""" -Header = f"""--// Generated by roblox-pyc v{VERSION} \\\\-- -""" +HEADER = f"--// Generated by roblox-pyc v{VERSION} \\\\--\n" +PY_HEADER = f"## Generated by roblox-pyc v{VERSION} ##\n" + def genDependencies(dependencies): total = "" @@ -1014,7 +1012,7 @@ def translate(self, pycode): self.output = visitor.output - return Header+self.to_code() + return HEADER+self.to_code() def to_code(self, code=None, indent=0): """Create a lua code from the compiler output""" @@ -1042,7 +1040,1100 @@ def add_indentation(line): @staticmethod def get_luainit(): # Return STDlib return """""" - + +""" Lua Lexer """ +# Convert source to tokens +def is_operator(char): + return char in [ + "~=", "=", "==", "(", ")", "<", "+", "-", "*", ">", "<", "not", "%", + "/", "{", "}", ",", "[", "]", "#" + ] + + +KEYWORDS = [ + "while", "do", "end", "if", "elseif", "else", "then", "function", "return", + "for", "in", +] + + +def is_keyword(word): + return word in KEYWORDS + + +def is_letter(char): + return re.search(r'[a-zA-Z]|_', char) + + +def is_num(char): + return re.search(r'[0-9]', char) + + +def extract_operator(chars): + op = "" + for letter in chars: + if not is_operator(op+letter): + break + + op = op+letter + del chars[0:len(op)] + return op + + +def extract_num(chars): + num = "" + + for letter in chars: + if not is_num(letter) and letter != ".": + break + + num = num+letter + del chars[0:len(num)] + return num + + +def extract_str(indicator, chars): + out = "" + for letter in chars[1:]: + if letter == indicator: + break + + out = out+letter + del chars[0:len(out)+2] + return out + + +def extract_word(chars): + word = "" + for letter in chars: + if not is_letter(letter) and not re.search(r'([0-9]|_)', letter): + break + + word = word+letter + del chars[0:len(word)] + return word + + +def extract_multiline_comment(chars): + string_chars = "".join(chars) + end_index = string_chars.index("--]]") + + val = string_chars[0:end_index] + del chars[0:end_index+4] + return val + + +def extract_comment(chars): + string_chars = "".join(chars) + end_index = string_chars.index("\n") + + val = string_chars[2:end_index] + del chars[0:end_index] + return val + + +def extract_multiline_str(chars): + string_chars = "".join(chars) + end_index = string_chars.index("]]") + + val = string_chars[2:end_index] + del chars[0:end_index+2] + return val + +def lexer(source): + chars = list(source) + tokens = [] + + while len(chars): + char = chars[0] + + if char == "\n": + char = chars.pop(0) + tokens.append({"type": "NL"}) + continue + + if chars[0:4] == ["-", "-", "[", "["]: + comment = extract_multiline_comment(chars) + tokens.append({"type": "MULTI-COMMENT", "value": comment}) + continue + + if chars[0:3] == ['n', 'i', 'l']: + tokens.append({"type": "NIL", "value": 'nil'}) + del chars[0:3] + continue + + if chars[0:2] == ["-", "-"]: + comment = extract_comment(chars) + tokens.append({"type": "COMMENT", "value": comment}) + continue + + if chars[0:3] == ["a", "n", "d"]: + tokens.append({"type": "OP", "value": "and"}) + del chars[0:3] + continue + + if chars[0:2] == ["o", "r"]: + tokens.append({"type": "OP", "value": "or"}) + del chars[0:2] + continue + + if chars[0:3] == ["n", "o", "t"]: + tokens.append({"type": "OP", "value": "not"}) + del chars[0:3] + continue + + if chars[0:2] == ["~", "="]: + tokens.append({"type": "OP", "value": "~="}) + del chars[0:2] + continue + + if chars[0:2] == [".", "."]: + tokens.append({"type": "OP", "value": ".."}) + del chars[0:2] + continue + + if chars[0:2] == [">", "="]: + tokens.append({"type": "OP", "value": ">="}) + del chars[0:2] + continue + + if chars[0:2] == ["<", "="]: + tokens.append({"type": "OP", "value": "<="}) + del chars[0:2] + continue + + if len(chars) >= 2 and char == "-" and is_num(chars[1]): + del chars[0:1] + num = "-"+extract_num(chars) + tokens.append({"type": "NUMBER", "value": num}) + continue + + if is_num(char): + num = extract_num(chars) + tokens.append({"type": "NUMBER", "value": num}) + continue + + if is_operator(char): + operator = extract_operator(chars) + tokens.append({"type": "OP", "value": operator}) + continue + + if char == "'": + string = extract_str("'", chars) + tokens.append({"type": "STRING", "value": string}) + continue + + if char == '"': + string = extract_str('"', chars) + tokens.append({"type": "STRING", "value": string}) + continue + + if chars[0:2] == ["[", "["]: + comment = extract_multiline_str(chars) + tokens.append({"type": "STRING", "value": comment}) + continue + + if is_letter(char): + word = extract_word(chars) + if is_keyword(word): + tokens.append({"type": "KEYWORD", "value": word}) + continue + + if word in ["true", "false"]: + tokens.append({"type": "BOOLEAN", "value": word}) + continue + + tokens.append({"type": "NAME", "value": word}) + continue + + chars.pop(0) + + return tokens + +""" Lua Parser """ +# Covert tokens to AST + +OPERATORS = [ + "+", "-", "=", "*", ">", "<", "~=", "==", "..", ">=", "<=", "%", "/", "and", "or", +] + +fn_name_index = 0 + + +def generate_function_name(): + global fn_name_index + + fn_name_index = fn_name_index + 1 + return "__fn{0}".format(fn_name_index) + + +def parse_tokens(tokens, in_body=0, in_table_construct=0, in_fn_arguments=0): + + out = [] + + while len(tokens) > 0: + token = tokens.pop(0) + + # Make sure we do not construct tuple when comma list is passed + # as function arguments/table constructor + if len(tokens) \ + and is_op(tokens[0], ",") \ + and in_fn_arguments == 0 \ + and in_table_construct == 0: + + tuple_tokens = [token] + tokens.pop(0) + + # Continue through comma list until the end + while len(tokens) > 0: + tuple_tokens.append(tokens.pop(0)) + + if len(tokens) == 0: + break + + if not is_op(tokens[0], ","): + break + + tokens.pop(0) + + out.append({"type": "tuple", "value": parse_tokens(tuple_tokens)}) + continue + + if token["type"] == "NUMBER": + out.append({"type": "number", "value": token["value"]}) + continue + + if token["type"] == "STRING": + out.append({"type": "string", "value": token["value"]}) + continue + + if token["type"] == "BOOLEAN": + out.append({"type": "boolean", "value": token["value"]}) + continue + + if token["type"] == "NIL": + out.append({"type": "nil", "value": None}) + continue + + if token["type"] == "OP" and token["value"] == "#": + assignments = extract_assignments(tokens) + out.append({ + "type": "call", + "name": "#", + "args": parse_tokens(assignments), + }) + continue + + if token["type"] == "OP" and token["value"] == "{": + table_tokens = extract_table(tokens) + table_tokens = extract_assignments_by_comma(table_tokens) + + nodes = map( + lambda x: parse_tokens(x, in_table_construct=1), + table_tokens + ) + + nodes = [x[0] for x in nodes] + + # print(table_tokens) + out.append({ + "type": "table", + "value": nodes, + }) + continue + + if token["type"] == "OP" and token["value"] == "not": + assignments = extract_assignments(tokens) + out.append({ + "type": "call", + "name": token["value"], + "args": parse_tokens(assignments), + }) + continue + + # Ignore [ if beeing used as constructor in table + if in_table_construct == 1 and is_op(token, "["): + continue + + # [ is beeing used as a accessor for table + if in_table_construct == 0 and is_op(token, "["): + key_tokens = extract_until_end_op(tokens, "]") + + expression = { + "type": "call", + "name": "[", + "args": [out.pop(), parse_tokens(key_tokens)], + } + + if in_body: # Do not wrap expression if already running in one + out.append({ + "type": "expr", + "value": [expression], + }) + else: + out.append(expression) + continue + + if token["type"] == "OP" and token["value"] in OPERATORS: + assignments = extract_assignments(tokens) + + # Move function outside assignment and declare it in above scope + # with keyword ref + if token["value"] == "=" and is_lex_keyword(assignments[0], "function"): + assignments = inline_anonymous_function(assignments, out) + + out.append({ + "type": "call", + "name": token["value"], + "args": [ + out.pop(), + parse_tokens( + assignments, + in_table_construct=in_table_construct, + ) + ], + }) + continue + + if token["type"] == "NAME" and len(tokens) and is_op(tokens[0], "("): + args = extract_args(tokens) + + expression = { + "type": "call", + "name": token["value"], + "args": parse_tokens(args, in_fn_arguments=1) + } + + if in_body: # Do not wrap expression if already running in one + out.append({ + "type": "expr", + "value": [expression], + }) + else: + out.append(expression) + + continue + + if token["type"] == "KEYWORD" and token["value"] == "else": + body = extract_if_body(tokens) + + out.append({ + "type": "else", + "body": parse_tokens(body, in_body=1), + }) + continue + + if token["type"] == "KEYWORD" and token["value"] in ["if", "elseif"]: + if_nodes = extract_scope_body(tokens) + + test_nodes = extract_to_keyword(if_nodes, "then") + body = extract_if_body(if_nodes) + + out.append({ + "type": "if", + "test": parse_tokens(test_nodes), + "body": parse_tokens(body, in_body=1), + "else": parse_tokens(if_nodes), + }) + continue + + if token["type"] == "KEYWORD" and token["value"] == "for": + + + body_tokens = extract_scope_body(tokens) + iteration_tokens = extract_to_keyword(body_tokens, "do") + if contains_op(iteration_tokens, "="): + target_tokens = extract_to_op(iteration_tokens, "=") + else: + target_tokens = extract_to_keyword(iteration_tokens, "in") + + out.append({ + "type": "for", + "target": parse_tokens(target_tokens), + "iteration": parse_tokens(iteration_tokens), + "body": parse_tokens(body_tokens, in_body=1), + }) + continue + + if token["type"] == "KEYWORD" and token["value"] == "while": + while_tokens = extract_scope_body(tokens) + test_tokens = extract_to_keyword(while_tokens, "do") + + out.append({ + "type": "while", + "test": parse_tokens(test_tokens), + "body": parse_tokens(while_tokens, in_body=1), + }) + continue + + if token["type"] == "KEYWORD" and token["value"] == "return": + assignments = extract_assignments(tokens) + + if is_lex_keyword(assignments[0], "function"): + assignments = inline_anonymous_function(assignments, out) + + out.append({ + "type": "return", + "value": parse_tokens(assignments), + }) + continue + + if token["type"] == "KEYWORD" and token["value"] == "function": + function_tokens = extract_scope_body(tokens) + signature_tokens = extract_fn_signature(function_tokens) + function_name = "" + + if signature_tokens[0]["type"] == "NAME": + name_token = signature_tokens.pop(0) + function_name = name_token["value"] + else: + function_name = None + + parameter_tokens = signature_tokens[1:-1] + # Only accept name as argument + parameter_tokens = filter( + lambda x: x["type"] == "NAME", + parameter_tokens + ) + + parameter_tokens = map( + lambda x: {"type": "argument", "name": x["value"]}, + parameter_tokens + ) + + out.append({ + "type": "function", + "name": function_name, + "args": list(parameter_tokens), + "body": parse_tokens(function_tokens, in_body=1), + }) + continue + + if token["type"] == "NAME": + out.append({ + "type": "name", + "name": token["value"], + }) + continue + + return out + + +def is_op(token, op): + return token.get("type", None) == "OP" and token["value"] == op + + +def is_lex_keyword(token, keyword): + return token["type"] == "KEYWORD" and token["value"] == keyword + + +def extract_table(tokens): + out = [] + depth = 0 + + while len(tokens) > 0: + token = tokens.pop(0) + out.append(token) + + if depth > 0 and is_op(token, "}"): + depth = depth +1 + continue + + if is_op(token, "}"): + break + + return out + + +def extract_fn_signature(tokens): + out = [] + + while len(tokens) > 0: + token = tokens.pop(0) + out.append(token) + if is_op(token, ")"): + break + return out + + +def extract_scope_body(tokens): + out = [] + + depth = 0 + + while len(tokens) > 0: + token = tokens.pop(0) + out.append(token) + + if token["type"] == "KEYWORD" and token["value"] in ["if", "function"]: + depth = depth + 1 + continue + + if depth > 0 and token["type"] == "KEYWORD" and token["value"] in ["if", "end"]: + depth = depth - 1 + continue + + if depth == 0 and token["type"] == "KEYWORD" and token["value"] == "end": + break + + return out + + +def extract_if_body(tokens): + out = [] + depth = 0 + + while len(tokens) > 0: + token = tokens[0] + + if is_lex_keyword(token, "if"): + out.append(token) + tokens.pop(0) + depth = depth + 1 + continue + + if depth > 0 and is_lex_keyword(token, "end"): + out.append(token) + tokens.pop(0) + depth = depth - 1 + continue + + if depth == 0 and is_lex_keyword(token, "elseif"): + break + + if depth == 0 and is_lex_keyword(token, "else"): + break + + if depth == 0 and is_lex_keyword(token, "end"): + break + + out.append(token) + tokens.pop(0) + + return out + +def extract_until_end_op(tokens, exit_op="]"): + out = [] + + while len(tokens) > 0: + token = tokens.pop(0) + + if is_op(token, exit_op): + break + + out.append(token) + + return out + +def extract_until_end_op(tokens, exit_op="]"): + out = [] + + while len(tokens) > 0: + token = tokens.pop(0) + + if is_op(token, exit_op): + break + + out.append(token) + + return out + + +def extract_to_op(tokens, exit_op="="): + out = [] + + while len(tokens) > 0: + token = tokens.pop(0) + + if is_op(token, exit_op): + break + + out.append(token) + + return out + +def extract_to_keyword(tokens, exit_keyword="then"): + out = [] + + while len(tokens) > 0: + token = tokens.pop(0) + + if is_lex_keyword(token, exit_keyword): + break + + out.append(token) + + return out + + +def extract_assignments(tokens): + out = [] + depth = 0 + + while len(tokens) > 0: + token = tokens.pop(0) + + if is_lex_keyword(token, "function"): + out.append(token) + depth = depth + 1 + continue + + if is_op(token, "("): + out.append(token) + depth = depth + 1 + continue + + if is_op(token, "{"): + out.append(token) + depth = depth + 1 + continue + + if is_op(token, ")"): + out.append(token) + depth = depth - 1 + continue + + if is_lex_keyword(token, "end"): + out.append(token) + depth = depth - 1 + continue + + if is_op(token, "}"): + out.append(token) + depth = depth - 1 + continue + + if token["type"] == "NL" and depth == 0: + break + + out.append(token) + + return out + +def inline_anonymous_function(tokens, out): + fn_name = generate_function_name() + + fn_tokens = parse_tokens(tokens) + fn_tokens[0]["name"] = fn_name + out.insert(-1, fn_tokens[0]) + assignments = [{"type": "NAME", "value": fn_name}] + return assignments + + +def extract_args(tokens): + depth = 1 + args = [] + + tokens.pop(0) # Drop ( + + while depth != 0: + token = tokens.pop(0) + + if is_op(token, "("): + depth = depth+1 + + if is_op(token, ")"): + depth = depth-1 + + args.append(token) + + args = args[:-1] # Drop ) + return args + + +def extract_assignments_by_comma(tokens): + pairs = [[]] + depth = 0 + + while len(tokens) > 0: + token = tokens.pop(0) + + if is_op(token, "{"): + depth = depth + 1 + + if is_op(token, "("): + depth = depth + 1 + + if is_op(token, "}"): + depth = depth - 1 + + if is_op(token, ")"): + depth = depth - 1 + + if depth == 0 and is_op(token, ","): + pairs.append([]) + continue + + pairs[-1].append(token) + + return pairs + + +def contains_op(tokens, op): + for token in tokens: + if is_op(token, op): + return True + return False + + +def parse(tokens): + ast_ = parse_tokens(tokens, in_body=1) + return ast_ + +""" Python Generator """ + +def ast_to_py_ast(nodes): + ast_ = parse_nodes(nodes) + + bootstrap = [] + + ast_ = bootstrap + ast_ + + tree = ast.Module(ast_, []) + tree = ast.fix_missing_locations(tree) + + return tree + + +def parse_nodes(nodes, ctx_klass=ast.Load): + out = [] + while len(nodes) > 0: + node = nodes.pop(0) + + if node["type"] == "name" and node["name"] == "_G": + out.append( + ast.Call( + func=ast.Name(id='globals', ctx=ast.Load()), + args=[], + keywords=[], + ) + ) + continue + + if node["type"] == "tuple": + expressions = parse_nodes(node["value"], ctx_klass=ctx_klass) + + out.append( + ast.Tuple( + elts=expressions, + ctx=ctx_klass(), + ) + ) + continue + + if node["type"] == "table": + argument_nodes = [] + keyword_nodes = [] + + for x in node["value"]: + if not (x["type"] == "call" and x["name"] == "="): + argument_nodes.append(x) + continue + + keyword_nodes.append(x) + + key_nodes = [x["args"][0] for x in keyword_nodes] + # Convert name references to strings + key_nodes = [ + {"type": "string", "value": x["name"]} + if x["type"] == "name" else x + for x in key_nodes + ] + + value_nodes = [x["args"][1] for x in keyword_nodes] + value_nodes = [x[0] for x in value_nodes] + value_nodes = parse_nodes(value_nodes) + + keywords = [] + for x in (zip(key_nodes, value_nodes)): + name_node, value_node = x + name = name_node["value"] + + # Apply __ to make sure its casted in Table + if name_node["type"] == "number": + name = "__{0}".format(name) + + keywords.append( + ast.keyword(arg=name, value=value_node) + ) + + out.append( + ast.Call( + func=ast.Name(id='Table', ctx=ast.Load()), + args=parse_nodes(argument_nodes), + keywords=keywords, + ) + ) + continue + + if node["type"] == "string": + out.append(ast.Str(s=node["value"])) + continue + + if node["type"] == "boolean": + value = node["value"] + value = True if value == "true" else value + value = False if value == "false" else value + out.append(ast.NameConstant(value=value)) + continue + + if node["type"] == "number": + value = node["value"] + value = float(value) if "." in value else int(value) + + out.append(ast.Num(n=value)) + continue + + if node["type"] == "nil": + out.append(ast.NameConstant(value=None)) + continue + + if node["type"] == "return": + out.append( + ast.Return(value=parse_nodes(node["value"])[0]) + ) + continue + + if node["type"] == "assign": + out.append( + ast.Assign( + targets=[ + ast.Name(id=node["name"], ctx=ast.Store()) + ], + value=parse_nodes(node["value"])[0], + ) + ) + continue + + if node["type"] == "name": + out.append( + ast.Name(id=node["name"], ctx=ctx_klass()), + ) + continue + + if node["type"] == "expr": + out.append( + ast.Expr( + value=parse_nodes(node["value"])[0] + ) + ) + continue + + if node["type"] == "function": + body_nodes = parse_nodes(node["body"]) + out.append( + ast.FunctionDef( + name=node["name"], + args=ast.arguments( + args=[ + ast.arg( + arg=x["name"], + annotation=None, + ) for x in node["args"] + ], + posonlyargs=[], + vararg=None, + kwonlyargs=[], + kw_defaults=[], + kwarg=None, + defaults=[] + ), + body=body_nodes, + decorator_list=[], + ) + ) + continue + + if node["type"] == "if": + test_nodes = parse_nodes(node["test"]) + body_nodes = parse_nodes(node["body"]) + else_nodes = parse_nodes(node["else"]) + + out.append( + ast.If( + test=test_nodes[0], + body=body_nodes, + orelse=else_nodes, + ) + ) + continue + + if node["type"] == "for": + target_expr = parse_nodes(node["target"], ctx_klass=ast.Store) + body_expr = parse_nodes(node["body"]) + + iteration_nodes = node["iteration"] + + # Apply range constructor + if iteration_nodes[0]["type"] == "tuple": + iteration_expr = [ + ast.Call( + func=ast.Name(id='get_for_range', ctx=ast.Load()), + args=parse_nodes(iteration_nodes[0]["value"]), + keywords=[], + ) + ] + + else: + iteration_expr = parse_nodes(iteration_nodes) + + out.append( + ast.For( + target=target_expr[0], + iter=iteration_expr[0], + body=body_expr, + orelse=[] + ) + ) + continue + + if node["type"] == "while": + test_nodes = parse_nodes(node["test"]) + body_nodes = parse_nodes(node["body"]) + + out.append( + ast.While( + test=test_nodes[0], + body=body_nodes, + orelse=[], + ) + ) + + if node["type"] == "else": + body_nodes = parse_nodes(node["body"]) + out = out + body_nodes + continue + + if node["type"] == "call": + if node["name"] == "#": + out.append( + ast.Call( + func=ast.Name(id='len', ctx=ast.Load()), + args=parse_nodes(node["args"]), + keywords=[], + ) + ) + continue + + if node["name"] == "[": + value_node = node["args"][0] + value_expression = parse_nodes([value_node])[0] + + out.append( + ast.Subscript( + value=value_expression, + slice=ast.Index( + value=parse_nodes(node["args"][1])[0] + ), + ctx=ast.Load(), + ) + ) + + continue + + if node["name"] == "=": + name_arg = node["args"][0] + value_arg = node["args"][1] + + target_expr = parse_nodes([name_arg], ctx_klass=ast.Store) + value_expr = parse_nodes(value_arg) + + out.append( + ast.Assign( + targets=target_expr, + value=value_expr[0], + ) + ) + continue + + if node["name"] in ["-", "%", "+", "..", "*", "/"]: + ops = node["name"] + + arg_left = parse_nodes([node["args"][0]]) + arg_right = parse_nodes(node["args"][1]) + + ops_ref = { + "-": ast.Sub, + "%": ast.Mod, + "+": ast.Add, + "..": ast.Add, + "*": ast.Mult, + "/": ast.Div, + } + + out.append( + ast.BinOp( + left=arg_left[0], + op=ops_ref[ops](), + right=arg_right[0], + ) + ) + continue + + if node["name"] in ["and", "or"]: + ops = node["name"] + + arg_left = parse_nodes([node["args"][0]]) + arg_right = parse_nodes(node["args"][1]) + + ops_ref = { + "and": ast.And, + "or": ast.Or, + } + + out.append( + ast.BoolOp( + op=ops_ref[ops](), + values=[ + arg_left[0], + arg_right[0], + ] + ) + ) + continue + + if node["name"] in [">", "<", "~=", "==", "<=", ">="]: + ops = node["name"] + + arg_left = parse_nodes([node["args"][0]]) + arg_right = parse_nodes(node["args"][1]) + + ops_ref = { + ">": ast.Gt, + ">=": ast.GtE, + "<": ast.Lt, + "<=": ast.LtE, + "~=": ast.NotEq, + "==": ast.Eq, + } + + out.append( + ast.Compare( + left=arg_left[0], + ops=[ops_ref[ops]()], + comparators=arg_right, + ) + ) + continue + + if node["name"] == "not": + out.append( + ast.UnaryOp( + op=ast.Not(), + operand=parse_nodes(node["args"])[0] + ) + ) + continue + + out.append( + ast.Call( + func=ast.Name(id=node["name"], ctx=ast.Load()), + args=parse_nodes(node["args"], ctx_klass=ast.Load), + keywords=[] + ) + ) + continue + + return out #### INTERFACE #### def warn(msg): @@ -1073,7 +2164,7 @@ def main(): args = sys.argv[1:] ast = False input_filename = "NONE" - + type = 1 # 1: py->lua, 2: lua->py for arg in args: if arg == "-v": version() @@ -1086,27 +2177,64 @@ def main(): error("In development") elif arg == "-ast": ast = True + elif arg == "-py": + type = 1 + elif arg == "-lua": + type = 2 else: input_filename = arg - if input_filename == "NONE": - usage() - if not Path(input_filename).is_file(): - error( - "The given filename ('{}') is not a file.".format(input_filename)) + + if type == 1: + if input_filename == "NONE": + usage() + if not Path(input_filename).is_file(): + error( + "The given filename ('{}') is not a file.".format(input_filename)) + + content = None + with open(input_filename, "r") as file: + content = file.read() + + if not content: + error("The input file is empty.") + + translator = Translator(Config(".pyluaconf.yaml"), + show_ast=ast) + lua_code = translator.translate(content) + + if not ast: + print(lua_code) + else: + file_handler = open(input_filename, 'r') + source = file_handler.read() + + tokens = lexer(source) + + #if kwargs["strip_comments"]: + # tokens = list(filter(lambda x: x["type"] != "COMMENT", tokens)) + # tokens = list(filter(lambda x: x["type"] != "MULTILINE-COMMENT", tokens)) + + #if kwargs["tokens"]: + # pprint(tokens) + # return + + ast_ = parse(tokens) + + #if kwargs["ast"]: + # pprint(ast_) + # return - content = None - with open(input_filename, "r") as file: - content = file.read() + py_ast = ast_to_py_ast(ast_) - if not content: - error("The input file is empty.") + #if kwargs["py_ast"]: + # print(ast.dump(py_ast)) + # return - translator = Translator(Config(".pyluaconf.yaml"), - show_ast=ast) - lua_code = translator.translate(content) + #if kwargs["py_code"]: + # print(astunparse.unparse(py_ast)) + # return - if not ast: - print(lua_code) + print(PY_HEADER + astunparse.unparse(py_ast)) return 0 From 173b7fa1fdbd3e5b3f6373710c12ed38e3b952c8 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Tue, 3 Oct 2023 17:21:45 -0500 Subject: [PATCH 30/77] 3.11 support --- src/__main__.py | 217 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 206 insertions(+), 11 deletions(-) diff --git a/src/__main__.py b/src/__main__.py index 18cbd36..110bc84 100644 --- a/src/__main__.py +++ b/src/__main__.py @@ -61,8 +61,8 @@ class BinaryOperationDesc: "format": _DEFAULT_BIN_FORMAT, }, ast.Mod: { - "value": "", - "format": "math.fmod({left}, {right})", + "value": "%", + "format": _DEFAULT_BIN_FORMAT, }, ast.Pow: { "value": "", @@ -254,6 +254,7 @@ def __init__(self, context=None, config=None): self.config = config self.last_end_mode = TokenEndMode.LINE_FEED self.output = [] + self.dependencies = [] def visit_Assign(self, node): """Visit assign""" @@ -276,6 +277,170 @@ def visit_Assign(self, node): target=target, value=value)) + ### MATCHES ### + def visit_Match(self, node): + """Visit match""" + self.emit("match({0}, ".format(self.visit_all(node.subject, inline=True))+"{") + for case in node.cases: + if hasattr(case.pattern, "value"): + self.emit("[{0}] = function()".format(case.pattern.value.s)) + self.visit_all(case.body) + self.emit("end,") + else: + self.emit("[\"default\"] = function()") + self.visit_all(case.body) + self.emit("end,") + self.emit("})") + # example input: + # match x: + # case "10": + # print("x is 10") + # case "20": + # print("x is 20") + # case _: + # print("x is not 10 or 20") + # example output: + # match(x, { + # ["10"] = function() + # print("x is 10") + # end, + # ["20"] = function() + # print("x is 20") + # end, + # ["_"] = function() + # print("x is not 10 or 20") + # end + # }) + def visit_MatchValue(self, node): + """Visit match value""" + return self.visit_all(node.value, inline=True) + def visit_MatchCase(self, node): + """Visit match case""" + return self.visit_all(node.body) + + def visit_MatchPattern(self, node): + """Visit match pattern""" + return self.visit_all(node.pattern, inline=True) + + def visit_MatchSingleton(self, node): + """Visit match singleton""" + return self.visit_all(node.pattern, inline=True) + + def visit_MatchSequence(self, node): + """Visit match sequence""" + return self.visit_all(node.pattern, inline=True) + + def visit_MatchMapping(self, node): + """Visit match mapping""" + return self.visit_all(node.pattern, inline=True) + + def visit_MatchClass(self, node): + """Visit match class""" + return self.visit_all(node.pattern, inline=True) + + def visit_MatchAs(self, node): + """Visit match as""" + return self.visit_all(node.pattern, inline=True) + + def visit_MatchKeyword(self, node): + """Visit match keyword""" + return self.visit_all(node.pattern, inline=True) + + def visit_MatchStar(self, node): + """Visit match star""" + return self.visit_all(node.pattern, inline=True) + + def visit_MatchOr(self, node): + """Visit match or""" + return self.visit_all(node.pattern, inline=True) + + ### END MATCH ### + def visit_AsyncWith(self, node): + """Visit async with""" + """Visit with""" + self.emit("coroutine.wrap(function()") + self.visit_all(node.body) + body = self.output[-1] + lines = [] + for i in node.items: + line = "" + if i.optional_vars is not None: + line = "local {} = " + line = line.format(self.visit_all(i.optional_vars, + inline=True)) + line += self.visit_all(i.context_expr, inline=True) + lines.append(line) + for line in lines: + body.insert(0, line) + self.emit("end)()") + + def visit_Slice(self, node): + """Visit slice""" + error("syntax based slicing is not supported yet. Use slice(, , , ) instead.") + + def visit_JoinedStr(self, node): + # f"{a} {b}" + # becomes + # `{a} {b}` + + """Visit joined string""" + values = [] + for value in node.values: + if isinstance(value, ast.Str): + values.append(value.s) + else: + values.append(self.visit_all(value, inline=True)) + self.emit("`{}`".format("".join(values))) + + def visit_FormattedValue(self, node): + """Visit formatted value""" + # f"{a}" + # becomes + # {a} + self.emit("{" + (self.visit_all(node.value, inline=True)) + "}") + + def visit_Bytes(self, node): + """Visit bytes""" + # Use utf8 strings instead of bytes + self.emit("\"{}\"".format(node.s.decode("utf8"))) + + + def visit_TryStar(self, node): + """Visit try""" + self.emit("local success, result = pcall(function()") + self.visit_all(node.body) + self.emit("end)") + + def visit_Assert(self, node): + """Visit assert""" + self.emit("assert({})".format(self.visit_all(node.test, True))) + + def visit_Nonlocal(self, node): + """Visit nonlocal""" + for name in node.names: + self.context.last()["nonlocals"].add_symbol(name) + + def visit_AnnAssign(self, node): + """Visit annassign""" + target = self.visit_all(node.target, inline=True) + value = self.visit_all(node.value, inline=True) + local_keyword = "" + last_ctx = self.context.last() + if last_ctx["class_name"]: + target = ".".join([last_ctx["class_name"], target]) + if "." not in target and not last_ctx["locals"].exists(target): + local_keyword = "local " + last_ctx["locals"].add_symbol(target) + self.emit("{local}{target}: {type} = {value}".format(local=local_keyword, + target=target, + value=value, + type=self.visit_all(node.annotation, inline=True))) + self.depend("pytypes") + # example input: + # a: int = 1 + # example output: + # local a = 1 + def visit_AugAssign(self, node): """Visit augassign""" operation = BinaryOperationDesc.OPERATION[node.op.__class__] @@ -411,7 +576,7 @@ def visit_Compare(self, node): def visit_Continue(self, node): """Visit continue""" last_ctx = self.context.last() - line = "goto {}".format(last_ctx["loop_label_name"]) + line = "continue" self.emit(line) def visit_Delete(self, node): @@ -584,8 +749,6 @@ def visit_For(self, node): self.visit_all(node.body) self.context.pop() - #self.output[-1].append("::{}::".format(continue_label)) 5.2 - self.emit("end") def visit_Global(self, node): @@ -877,6 +1040,11 @@ def visit_Raise(self, node): line = "error({})".format(self.visit_all(node.exc, inline=True)) self.emit(line) + def visit_Set(self, node): + """Visit set""" + values = [self.visit_all(value, True) for value in node.elts] + self.emit('{' + ', '.join(values) + '}') + def visit_Try(self, node): """Visit try""" self.emit("xpcall(function()") @@ -910,10 +1078,35 @@ def visit_While(self, node): self.visit_all(node.body) self.context.pop() - #self.output[-1].append("::{}::".format(continue_label)) 5.2 - self.emit("end") + + def visit_SetComp(self, node): + """Visit set comprehension""" + self.emit("(function()") + self.emit("local result = set({})") + self.depend("set") + ends_count = 0 + for comp in node.generators: + line = "for {target} in {iterator} do" + values = { + "target": self.visit_all(comp.target, inline=True), + "iterator": self.visit_all(comp.iter, inline=True), + } + line = line.format(**values) + self.emit(line) + ends_count += 1 + for if_ in comp.ifs: + line = "if {} then".format(self.visit_all(if_, inline=True)) + self.emit(line) + ends_count += 1 + line = "result.add({})" + line = line.format(self.visit_all(node.elt, inline=True)) + self.emit(line) + self.emit(" ".join(["end"] * ends_count)) + self.emit("return result") + self.emit("end)()") + def visit_With(self, node): """Visit with""" self.emit("do") @@ -938,7 +1131,9 @@ def visit_With(self, node): def generic_visit(self, node): """Unknown nodes handler""" - error("Unknown node: {}".format(node)) + if node is None: + return + error("Unsupported feature: '{}'".format(node.__class__.__name__)) def visit_all(self, nodes, inline=False): """Visit all nodes in the given list""" @@ -974,8 +1169,8 @@ def depend(self, value): self.dependencies.append(value) """Header""" -HEADER = f"--// Generated by roblox-pyc v{VERSION} \\\\--\n" -PY_HEADER = f"## Generated by roblox-pyc v{VERSION} ##\n" +HEADER = f"--// Generated by roblox-py v{VERSION} \\\\--\n" +PY_HEADER = f"## Generated by roblox-py v{VERSION} ##\n" def genDependencies(dependencies): @@ -2155,7 +2350,7 @@ def usage(): sys.exit() def version(): - print("\033[1;34m" + "copyright:" + "\033[0m" + " roblox-pyc " + "\033[1m" + VERSION + "\033[0m" + " licensed under the GNU Affero General Public License by " + "\033[1m" + "@AsynchronousAI" + "\033[0m") + print("\033[1;34m" + "copyright:" + "\033[0m" + " roblox-py " + "\033[1m" + VERSION + "\033[0m" + " licensed under the GNU Affero General Public License by " + "\033[1m" + "@AsynchronousAI" + "\033[0m") sys.exit(0) """The main entry point to the translator""" From 9152490863887104883fa4edd5f1dd8119440e29 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Tue, 3 Oct 2023 17:31:23 -0500 Subject: [PATCH 31/77] Better switches --- src/__main__.py | 36 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/src/__main__.py b/src/__main__.py index 110bc84..a8bdd20 100644 --- a/src/__main__.py +++ b/src/__main__.py @@ -280,37 +280,21 @@ def visit_Assign(self, node): ### MATCHES ### def visit_Match(self, node): """Visit match""" - self.emit("match({0}, ".format(self.visit_all(node.subject, inline=True))+"{") for case in node.cases: if hasattr(case.pattern, "value"): - self.emit("[{0}] = function()".format(case.pattern.value.s)) + first = "" + if case is node.cases[0]: + first = "" + else: + first = "else" + + self.emit("{}if {} == {} then".format(first, self.visit_all(node.subject, inline=True), case.pattern.value.s)) self.visit_all(case.body) - self.emit("end,") else: - self.emit("[\"default\"] = function()") + self.emit("else") self.visit_all(case.body) - self.emit("end,") - self.emit("})") - # example input: - # match x: - # case "10": - # print("x is 10") - # case "20": - # print("x is 20") - # case _: - # print("x is not 10 or 20") - # example output: - # match(x, { - # ["10"] = function() - # print("x is 10") - # end, - # ["20"] = function() - # print("x is 20") - # end, - # ["_"] = function() - # print("x is not 10 or 20") - # end - # }) + self.emit("end") + def visit_MatchValue(self, node): """Visit match value""" return self.visit_all(node.value, inline=True) From 20320b97a70936d0e6b3859cc862b89eaed0868f Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Tue, 3 Oct 2023 17:39:21 -0500 Subject: [PATCH 32/77] Build --- .gitignore | 6 ++++- README.md | 4 ++++ build.md | 21 ----------------- rbxpy.spec | 43 +++++++++++++++++++++++++++++++++++ src/{__main__.py => rbxpy.py} | 14 ++++++------ 5 files changed, 59 insertions(+), 29 deletions(-) delete mode 100644 build.md create mode 100644 rbxpy.spec rename src/{__main__.py => rbxpy.py} (99%) diff --git a/.gitignore b/.gitignore index c713b25..7fe03c3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,8 @@ *.byte test.py -test.lua \ No newline at end of file +test.lua + +# Build +/build +/dist \ No newline at end of file diff --git a/README.md b/README.md index 99b04c7..ac64792 100644 --- a/README.md +++ b/README.md @@ -50,4 +50,8 @@ *** Python, C, C++ Compiler for Roblox. + +## Building +- `pip install pyinstaller` +- `pyinstaller src/rbxpy.py` *** \ No newline at end of file diff --git a/build.md b/build.md deleted file mode 100644 index 67382e9..0000000 --- a/build.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -description: For personal usage so I can remember what commands to use. ---- - -# Technical Guide - -## Build - -* `python3 -m build` -* `pip install dist/roblox_pyc-{version}-py3-none-any.whl --force-reinstall` - -## Config - -* open setup.cfg -* change version - -## Upload - -GitHub actions does this whenever a commit is passed on `setup.cfg.` - -* `twine upload dist/*` diff --git a/rbxpy.spec b/rbxpy.spec new file mode 100644 index 0000000..75eb2a2 --- /dev/null +++ b/rbxpy.spec @@ -0,0 +1,43 @@ +# -*- mode: python ; coding: utf-8 -*- + + +a = Analysis( + ['src/rbxpy.py'], + pathex=[], + binaries=[], + datas=[], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name='rbxpy', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=True, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +) +coll = COLLECT( + exe, + a.binaries, + a.datas, + strip=False, + upx=True, + upx_exclude=[], + name='rbxpy', +) diff --git a/src/__main__.py b/src/rbxpy.py similarity index 99% rename from src/__main__.py rename to src/rbxpy.py index a8bdd20..fff8afe 100644 --- a/src/__main__.py +++ b/src/rbxpy.py @@ -2324,13 +2324,13 @@ def error(msg): sys.exit() def usage(): - print("\n"+f"""usage: rbxpy [file] [options] > [gen] -Available options are: -{tab}-v show version information -{tab}-vd show version number only" -{tab}-ast show python ast tree before code -{tab}-s generate std.lua -{tab}-u open this""") + print("\n"+f"""usage: \033[1;33mrbxpy\033[0m [file] [options] > [gen] +\033[1mOptions:\033[0m +{TAB}\033[1m-v\033[0m show version information +{TAB}\033[1m-vd\033[0m show version number only" +{TAB}\033[1m-ast\033[0m show python ast tree before code +{TAB}\033[1m-s\033[0m generate std.lua +{TAB}\033[1m-u\033[0m open this""") sys.exit() def version(): From 8282651310675abda7dda658c2cfccd6a6c4faf6 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Tue, 3 Oct 2023 18:18:27 -0500 Subject: [PATCH 33/77] Better typechecking --- src/rbxpy.py | 350 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 335 insertions(+), 15 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index fff8afe..8562975 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -415,11 +415,24 @@ def visit_AnnAssign(self, node): if "." not in target and not last_ctx["locals"].exists(target): local_keyword = "local " last_ctx["locals"].add_symbol(target) - self.emit("{local}{target}: {type} = {value}".format(local=local_keyword, - target=target, - value=value, - type=self.visit_all(node.annotation, inline=True))) - self.depend("pytypes") + type = self.visit_all(node.annotation, inline=True) + + if type == "int" or type == "float": + type = "number" + elif type == "str": + type = "string" + elif type == "list" or type == "dict" or type == "memoryview" or type == "bytearray" or type == "set" or type == "range" or type == "frozenset" or type == "module" or type == "classobj" or type == "class" or type == "tuple": + type = "table" + elif type == "NoneType": + type = "nil" + elif type == "ellipsis" or type == "NotImplementedType" or type == "slice" or type == "classmethod" or type == "type" or type == "staticmethod": + error("The Python type '{}' can not be converted to Luau.".format(type)) + + if value != None and value != "": + self.emit("{local}{target}: {type} = {value}".format(local=local_keyword, + target=target, + value=value, + type=type)) # example input: # a: int = 1 # example output: @@ -1156,14 +1169,6 @@ def depend(self, value): HEADER = f"--// Generated by roblox-py v{VERSION} \\\\--\n" PY_HEADER = f"## Generated by roblox-py v{VERSION} ##\n" - -def genDependencies(dependencies): - total = "" - for v in dependencies: - pass - - return total - """Python to lua translator class""" class Translator: """Python to lua main class translator""" @@ -1188,10 +1193,325 @@ def translate(self, pycode): print(ast.dump(py_ast_tree)) visitor.visit(py_ast_tree) - + + DEPEND = "\n\n--// REQUIREMENTS \\\\--\n" + self.output = visitor.output + self.dependencies = visitor.dependencies + + for depend in self.dependencies: + # set + + if depend == "list": + DEPEND += """function list(t) + local result = {} + + result._is_list = true + + result._data = {} + for _, v in ipairs(t) do + table.insert(result._data, v) + end + + local methods = {} + + methods.append = function(value) + table.insert(result._data, value) + end + + methods.extend = function(iterable) + for value in iterable do + table.insert(result._data, value) + end + end + + methods.insert = function(index, value) + table.insert(result._data, index, value) + end + + methods.remove = function(value) + for i, v in ipairs(result._data) do + if value == v then + table.remove(result._data, i) + break + end + end + end + + methods.pop = function(index) + index = index or #result._data + local value = result._data[index] + table.remove(result._data, index) + return value + end + + methods.clear = function() + result._data = {} + end + + methods.index = function(value, start, end_) + start = start or 1 + end_ = end_ or #result._data + + for i = start, end_, 1 do + if result._data[i] == value then + return i + end + end + + return nil + end + + methods.count = function(value) + local cnt = 0 + for _, v in ipairs(result._data) do + if v == value then + cnt = cnt + 1 + end + end + + return cnt + end + + methods.sort = function(key, reverse) + key = key or nil + reverse = reverse or false + + table.sort(result._data, function(a, b) + if reverse then + return a < b + end + + return a > b + end) + end + + methods.reverse = function() + local new_data = {} + for i = #result._data, 1, -1 do + table.insert(new_data, result._data[i]) + end + + result._data = new_data + end + + methods.copy = function() + return list(result._data) + end + + local iterator_index = nil + + setmetatable(result, { + __index = function(self, index) + if typeof(index) == "number" then + if index < 0 then + index = #result._data + index + end + return rawget(result._data, index + 1) + end + return methods[index] + end, + __newindex = function(self, index, value) + result._data[index] = value + end, + __call = function(self, _, idx) + if idx == nil and iterator_index ~= nil then + iterator_index = nil + end + + local v = nil + iterator_index, v = next(result._data, iterator_index) + + return v + end, + }) + + return result +end""" + elif depend == "dict": + DEPEND += """function dict(t) + local result = {} + + result._is_dict = true + + result._data = {} + for k, v in pairs(t) do + result._data[k] = v + end + + local methods = {} + + local key_index = nil + + methods.clear = function() + result._data = {} + end + + methods.copy = function() + return dict(result._data) + end + + methods.get = function(key, default) + default = default or nil + if result._data[key] == nil then + return default + end + + return result._data[key] + end + + methods.items = function() + return pairs(result._data) + end + + methods.keys = function() + return function(self, idx, _) + if idx == nil and key_index ~= nil then + key_index = nil + end + + key_index, _ = next(result._data, key_index) + return key_index + end + end + + methods.pop = function(key, default) + default = default or nil + if result._data[key] ~= nil then + local value = result._data[key] + result._data[key] = nil + return key, value + end + + return key, default + end + + methods.popitem = function() + local key, value = next(result._data) + if key ~= nil then + result._data[key] = nil + end + + return key, value + end + + methods.setdefault = function(key, default) + if result._data[key] == nil then + result._data[key] = default + end + + return result._data[key] + end + + methods.update = function(t) + assert(t._is_dict) + + for k, v in t.items() do + result._data[k] = v + end + end + + methods.values = function() + return function(self, idx, _) + if idx == nil and key_index ~= nil then + key_index = nil + end + + key_index, value = next(result._data, key_index) + return value + end + end + + setmetatable(result, { + __index = function(self, index) + if typeof(index) == "string" then + -- If it starts with SLICE! then it is a slice, get the start, stop, and step values. Sometimes the 3rd value is not there, so we need to check for that + if string.sub(index, 1, 6) == "SLICE!" then + local start, stop, step = string.match(index, "SLICE!%((%d+), (%d+), (%d+)%)") + if (not stop) and (not step) and start then -- 1 value + start = string.match(index, "SLICE!%((%d+), (%d+)%)") + step = 1 + stop = -1 + elseif not step then -- 2 values + start, stop = string.match(index, "SLICE!%((%d+), (%d+)%)") + step = 1 + end + return slicefun(self, tonumber(start), tonumber(stop), tonumber(step)) + end + end + if result._data[index] ~= nil then + return result._data[index] + end + return methods[index] + end, + __newindex = function(self, index, value) + result._data[index] = value + end, + __call = function(self, _, idx) + if idx == nil and key_index ~= nil then + key_index = nil + end + + key_index, _ = next(result._data, key_index) + + return key_index + end, + }) + + return result +end""" + elif depend == "class": + DEPEND += """function class(class_init, bases) + bases = bases or {} + + local c = {} + + for _, base in ipairs(bases) do + for k, v in pairs(base) do + c[k] = v + end + end + + c._bases = bases + + c = class_init(c) + + local mt = getmetatable(c) or {} + mt.__call = function(_, ...) + local object = {} + + setmetatable(object, { + __index = function(tbl, idx) + local method = c[idx] + if typeof(method) == "function" then + return function(...) + return c[idx](object, ...) + end + end + + return method + end, + }) + + if typeof(object.__init__) == "function" then + object.__init__(...) + end + + return object + end + + setmetatable(c, mt) + + return c + end""" + else: + error("Auto-generated dependency unhandled {}, please report this issue on Discord or Github".format(depend)) + + + DEPEND += "----- CODE START -----\n" + - return HEADER+self.to_code() + return HEADER + DEPEND + self.to_code() def to_code(self, code=None, indent=0): """Create a lua code from the compiler output""" From b98ac3cd33ea45f327d96bd3db577c0b0a9a3add Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Tue, 3 Oct 2023 18:28:35 -0500 Subject: [PATCH 34/77] Dependencies --- src/rbxpy.py | 83 ++++++++++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index 8562975..1dd2e50 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -65,8 +65,8 @@ class BinaryOperationDesc: "format": _DEFAULT_BIN_FORMAT, }, ast.Pow: { - "value": "", - "format": "math.pow({left}, {right})", + "value": "^", + "format": _DEFAULT_BIN_FORMAT, }, ast.FloorDiv: { "value": "/", @@ -245,6 +245,8 @@ def get_next(): return "loop_label_{}".format(LoopCounter.COUNTER) """Node visitor""" +dependencies = [] + class NodeVisitor(ast.NodeVisitor): LUACODE = "luau" @@ -254,7 +256,6 @@ def __init__(self, context=None, config=None): self.config = config self.last_end_mode = TokenEndMode.LINE_FEED self.output = [] - self.dependencies = [] def visit_Assign(self, node): """Visit assign""" @@ -1081,8 +1082,7 @@ def visit_While(self, node): def visit_SetComp(self, node): """Visit set comprehension""" self.emit("(function()") - self.emit("local result = set({})") - self.depend("set") + self.emit("local result = {}") ends_count = 0 for comp in node.generators: line = "for {target} in {iterator} do" @@ -1097,7 +1097,7 @@ def visit_SetComp(self, node): line = "if {} then".format(self.visit_all(if_, inline=True)) self.emit(line) ends_count += 1 - line = "result.add({})" + line = "table.insert(result, {})" line = line.format(self.visit_all(node.elt, inline=True)) self.emit(line) self.emit(" ".join(["end"] * ends_count)) @@ -1163,7 +1163,7 @@ def emit(self, value): self.output.append(value) def depend(self, value): """Add dependency value to the object""" - self.dependencies.append(value) + dependencies.append(value) """Header""" HEADER = f"--// Generated by roblox-py v{VERSION} \\\\--\n" @@ -1197,9 +1197,8 @@ def translate(self, pycode): DEPEND = "\n\n--// REQUIREMENTS \\\\--\n" self.output = visitor.output - self.dependencies = visitor.dependencies - for depend in self.dependencies: + for depend in dependencies: # set if depend == "list": @@ -1462,53 +1461,55 @@ def translate(self, pycode): end""" elif depend == "class": DEPEND += """function class(class_init, bases) - bases = bases or {} + bases = bases or {} - local c = {} + local c = {} - for _, base in ipairs(bases) do - for k, v in pairs(base) do - c[k] = v - end - end + for _, base in ipairs(bases) do + for k, v in pairs(base) do + c[k] = v + end + end - c._bases = bases + c._bases = bases - c = class_init(c) + c = class_init(c) - local mt = getmetatable(c) or {} - mt.__call = function(_, ...) - local object = {} + local mt = getmetatable(c) or {} + mt.__call = function(_, ...) + local object = {} - setmetatable(object, { - __index = function(tbl, idx) - local method = c[idx] - if typeof(method) == "function" then - return function(...) - return c[idx](object, ...) - end - end + setmetatable(object, { + __index = function(tbl, idx) + local method = c[idx] + if typeof(method) == "function" then + return function(...) + return c[idx](object, ...) + end + end - return method - end, - }) + return method + end, + }) - if typeof(object.__init__) == "function" then - object.__init__(...) - end + if typeof(object.__init__) == "function" then + object.__init__(...) + end - return object - end + return object + end - setmetatable(c, mt) + setmetatable(c, mt) - return c - end""" + return c +end""" + #elif depend == "set": + # pass else: error("Auto-generated dependency unhandled {}, please report this issue on Discord or Github".format(depend)) - DEPEND += "----- CODE START -----\n" + DEPEND += "\n\n----- CODE START -----\n" return HEADER + DEPEND + self.to_code() From f94b7466d55a81809ebf6ef4834027a6f16e07a7 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Tue, 3 Oct 2023 18:31:01 -0500 Subject: [PATCH 35/77] Fixes --- src/rbxpy.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/rbxpy.py b/src/rbxpy.py index 1dd2e50..a312037 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -601,12 +601,14 @@ def visit_Dict(self, node): elements = ", ".join(elements) self.depend("dict") self.emit("dict {{{}}}".format(elements)) + self.depend("dict") def visit_DictComp(self, node): """Visit dictionary comprehension""" self.emit("(function()") self.depend("dict") self.emit("local result = dict {}") + self.depend("dict") ends_count = 0 @@ -790,6 +792,7 @@ def visit_AsyncFunctionDef(self, node): if node.args.vararg is not None: line = "local {name} = list {{...}}".format(name=node.args.vararg.arg) + self.depend("list") body.insert(0, line) arg_index = -1 @@ -928,12 +931,14 @@ def visit_List(self, node): """Visit list""" elements = [self.visit_all(item, inline=True) for item in node.elts] line = "list {{{}}}".format(", ".join(elements)) + self.depend("list") self.emit(line) def visit_ListComp(self, node): """Visit list comprehension""" self.emit("(function()") self.emit("local result = list {}") + self.depend("list") ends_count = 0 @@ -1198,6 +1203,11 @@ def translate(self, pycode): self.output = visitor.output + # Remove dupelicates from dependencies + for depend in dependencies: + if depend not in dependencies: + dependencies.append(depend) + for depend in dependencies: # set From 2daf724ec15b2c3ed14b821b23201da399b73c0b Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Tue, 3 Oct 2023 18:34:04 -0500 Subject: [PATCH 36/77] `in` operator --- src/rbxpy.py | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index a312037..fd67312 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -124,10 +124,12 @@ class CompareOperationDesc: ast.Gt: ">", ast.GtE: ">=", ast.In: { - "format": "operator_in({left}, {right})", + "format": "op_in({left}, {right})", + "depend": "in", }, ast.NotIn: { - "format": "not operator_in({left}, {right})", + "format": "not op_in({left}, {right})", + "depend": "in", }, } @@ -452,6 +454,8 @@ def visit_AugAssign(self, node): } line = "({})".format(operation["format"]) + if operation["depend"]: + self.depend(operation["depend"]) line = line.format(**values) self.emit("{target} = {line}".format(target=target, line=line)) @@ -473,6 +477,8 @@ def visit_BinOp(self, node): """Visit binary operation""" operation = BinaryOperationDesc.OPERATION[node.op.__class__] line = "({})".format(operation["format"]) + if operation["depend"]: + self.depend(operation["depend"]) values = { "left": self.visit_all(node.left, True), "right": self.visit_all(node.right, True), @@ -485,6 +491,8 @@ def visit_BoolOp(self, node): """Visit boolean operation""" operation = BooleanOperationDesc.OPERATION[node.op.__class__] line = "({})".format(operation["format"]) + if operation["depend"]: + self.depend(operation["depend"]) values = { "left": self.visit_all(node.values[0], True), "right": self.visit_all(node.values[1], True), @@ -564,6 +572,8 @@ def visit_Compare(self, node): line += "{left} {op} {right}".format(**values) elif isinstance(operation, dict): line += operation["format"].format(**values) + if operation["depend"]: + self.depend(operation["depend"]) if i < len(node.ops) - 1: left = right @@ -1031,6 +1041,8 @@ def visit_UnaryOp(self, node): value = self.visit_all(node.operand, inline=True) line = operation["format"] + if operation["depend"]: + self.depend(operation["depend"]) values = { "value": value, "operation": operation["value"], @@ -1513,10 +1525,22 @@ def translate(self, pycode): return c end""" - #elif depend == "set": - # pass + elif depend == "in": + DEPEND += """function op_in(item, items) + if type(items) == "table" then + for v in items do + if v == item then + return true + end + end + elseif type(items) == "string" and type(item) == "string" then + return string.find(items, item, 1, true) ~= nil + end + + return false +end""" else: - error("Auto-generated dependency unhandled {}, please report this issue on Discord or Github".format(depend)) + error("Auto-generated dependency unhandled '{}', please report this issue on Discord or Github".format(depend)) DEPEND += "\n\n----- CODE START -----\n" From 6f444d4cf4cda7ee68885318448dd115211f29a1 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Tue, 3 Oct 2023 18:37:38 -0500 Subject: [PATCH 37/77] Bug fix --- src/rbxpy.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index fd67312..77655ad 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -454,8 +454,8 @@ def visit_AugAssign(self, node): } line = "({})".format(operation["format"]) - if operation["depend"]: - self.depend(operation["depend"]) + #if operation["depend"]: + # self.depend(operation["depend"]) line = line.format(**values) self.emit("{target} = {line}".format(target=target, line=line)) @@ -477,8 +477,8 @@ def visit_BinOp(self, node): """Visit binary operation""" operation = BinaryOperationDesc.OPERATION[node.op.__class__] line = "({})".format(operation["format"]) - if operation["depend"]: - self.depend(operation["depend"]) + #if operation["depend"]: Binary operators do not have it + # self.depend(operation["depend"]) values = { "left": self.visit_all(node.left, True), "right": self.visit_all(node.right, True), @@ -491,8 +491,8 @@ def visit_BoolOp(self, node): """Visit boolean operation""" operation = BooleanOperationDesc.OPERATION[node.op.__class__] line = "({})".format(operation["format"]) - if operation["depend"]: - self.depend(operation["depend"]) + #if operation["depend"]: + # self.depend(operation["depend"]) values = { "left": self.visit_all(node.values[0], True), "right": self.visit_all(node.values[1], True), @@ -1041,8 +1041,8 @@ def visit_UnaryOp(self, node): value = self.visit_all(node.operand, inline=True) line = operation["format"] - if operation["depend"]: - self.depend(operation["depend"]) + #if operation["depend"]: + # self.depend(operation["depend"]) values = { "value": value, "operation": operation["value"], From 896c955d624645f7b6e5ff8ed441b1a8811fef91 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Tue, 3 Oct 2023 18:55:22 -0500 Subject: [PATCH 38/77] -f & -fn --- src/rbxpy.py | 365 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 360 insertions(+), 5 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index 77655ad..9225ad6 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -1195,7 +1195,7 @@ def __init__(self, config=None, show_ast=False): self.output = [] - def translate(self, pycode): + def translate(self, pycode, fn): """Translate python code to lua code""" try: # code that uses ast @@ -1219,6 +1219,9 @@ def translate(self, pycode): for depend in dependencies: if depend not in dependencies: dependencies.append(depend) + + if fn: + dependencies.append("fn") for depend in dependencies: # set @@ -1538,6 +1541,354 @@ def translate(self, pycode): end return false +end""" + elif depend == "fn": + DEPEND += """if game then +__name__ = if script:IsA("BaseScript") then "__main__" else script.Name +else +__name__ = nil +end +range = function(s, e) -- range() + local tb = {} + local a = 0 + local b = 0 + if not e then a=1 else a=s end + if not e then b=s else b=e end + for i = a, b do + tb[#tb+1] = i + end + return tb +end +len = function(x) return #x end -- len() +abs = math.abs -- abs() +str = tostring -- str() +int = tonumber -- int() +sum = function(tbl) --sum() + local total = 0 + for _, v in ipairs(tbl) do + total = total + v + end + return total +end +max = function(tbl) --max() + local maxValue = -math.huge + for _, v in ipairs(tbl) do + if v > maxValue then + maxValue = v + end + end + return maxValue +end +min = function(tbl) --min() + local minValue = math.huge + for _, v in ipairs(tbl) do + if v < minValue then + minValue = v + end + end + return minValue +end +reversed = function(seq) -- reversed() + local reversedSeq = {} + local length = #seq + for i = length, 1, -1 do + reversedSeq[length - i + 1] = seq[i] + end + return reversedSeq +end +split = function(str, sep) -- split + local substrings = {} + local pattern = string.format("([^%s]+)",sep or "%s") + for substring in string.gmatch(str, pattern) do + table.insert(substrings, substring) + end + return substrings +end +round = math.round -- round() +all = function (iter) -- all() + for i, v in iter do if not v then return false end end + + return true +end +any = function (iter) -- any() + for i, v in iter do + if v then return true end + end + return false +end +ord = string.byte -- ord +chr = string.char -- chr +callable = function(fun) -- callable() + if rawget(fun) ~= fun then warn("At the momement Roblox.py's function callable() does not fully support metatables.") end + return typeof(rawget(fun)) == "function" +end +float = tonumber -- float() +super = function() + error("roblox-pyc does not has a Lua implementation of the function `super`. Use `self` instead") +end +format = function(format, ...) -- format + local args = {...} + local num_args = select("#", ...) + + local formatted_string = string.gsub(format, "{(%d+)}", function(index) + index = tonumber(index) + if index >= 1 and index <= num_args then + return tostring(args[index]) + else + return "{" .. index .. "}" + end + end) + + return formatted_string +end +hex = function (value) -- hex + return string.format("%x", value) +end +id = function (obj) -- id + return print(tostring({obj}):gsub("table: ", ""):split(" ")[1]) +end +map = function (func, ...) --map + local args = {...} + local result = {} + local num_args = select("#", ...) + + local shortest_length = math.huge + for i = 1, num_args do + local arg = args[i] + local arg_length = #arg + if arg_length < shortest_length then + shortest_length = arg_length + end + end + + for i = 1, shortest_length do + local mapped_args = {} + for j = 1, num_args do + local arg = args[j] + table.insert(mapped_args, arg[i]) + end + table.insert(result, func(unpack(mapped_args))) + end + + return result +end +bool = function(x) -- bool + if x == false or x == nil or x == 0 then + return false + end + + if typeof(x) == "table" then + if x._is_list or x._is_dict then + return next(x._data) ~= nil + end + end + + return true +end +divmod = function(a, b) -- divmod + local res = { math.floor(a / b), math.fmod(a, b) } + return unpack(res) +end +slice = function (seq, start, stop, step) + local sliced = {} + local len = #seq + start = start or 1 + stop = stop or len + step = step or 1 + if start < 0 then + start = len + start + 1 + end + if stop < 0 then + stop = len + stop + 1 + end + for i = start, stop - 1, step do + table.insert(sliced, seq[i]) + end + return sliced +end +anext = function (iterator) -- anext + local status, value = pcall(iterator) + if status then + return value + end +end +ascii = function (obj) -- ascii + return string.format("%q", tostring(obj)) +end +dir = function (obj) -- dir + local result = {} + for key, _ in pairs(obj) do + table.insert(result, key) + end + return result +end +getattr = function (obj, name, default) -- getattr + local value = obj[name] + if value == nil then + return default + end + return value +end +globals = function () -- globals + return _G +end +hasattr = function (obj, name) --hasattr + return obj[name] ~= nil +end +isinstance = function (obj, class) -- isinstance + return type(obj) == class +end +issubclass = function (cls, classinfo) -- issubclass + local mt = getmetatable(cls) + while mt do + if mt.__index == classinfo then + return true + end + mt = getmetatable(mt.__index) + end + return false +end +iter = function (obj) -- iter + if type(obj) == "table" and obj.__iter__ ~= nil then + return obj.__iter__ + end + return nil +end +locals = function () -- locals + return _G +end +oct = function (num) --oct + return string.format("%o", num) +end +pow = function (base, exponent, modulo) --pow + if modulo ~= nil then + return math.pow(base, exponent) % modulo + else + return base ^ exponent + end +end +eval = function (expr, env) + return loadstring(expr)() +end +exec = loadstring +filter = function (predicate, iterable) + local result = {} + for _, value in ipairs(iterable) do + if predicate(value) then + table.insert(result, value) + end + end + return result +end +frozenset = function (...) + local elements = {...} + local frozenSet = {} + for _, element in ipairs(elements) do + frozenSet[element] = true + end + return frozenSet +end +aiter = function (iterable) -- aiter + return pairs(iterable) +end +bin = function(num: number) + local bits = {} + repeat + table.insert(bits, 1, num % 2) + num = math.floor(num / 2) + until num == 0 + return "0b" .. table.concat(bits) +end +complex = function (real, imag) -- complex + return { real = real, imag = imag } +end +deltaattr = function (object, attribute) -- delattr + object[attribute] = nil +end +enumerate = function (iterable) -- enumerate + local i = 0 + return function() + i = i + 1 + local value = iterable[i] + if value ~= nil then + return i, value + end + end +end +bytearray = function (arg) -- bytearray + if type(arg) == "string" then + local bytes = {} + for i = 1, #arg do + table.insert(bytes, string.byte(arg, i)) + end + return bytes + elseif type(arg) == "number" then + local bytes = {} + while arg > 0 do + table.insert(bytes, 1, arg % 256) + arg = math.floor(arg / 256) + end + return bytes + elseif type(arg) == "table" then + return arg -- Assuming it's already a bytearray table + else + error("Invalid argument type for bytearray()") + end +end +bytes = function (arg) -- bytes + if type(arg) == "string" then + local bytes = {} + for i = 1, #arg do + table.insert(bytes, string.byte(arg, i)) + end + return bytes + elseif type(arg) == "table" then + return arg -- Assuming it's already a bytes table + else + error("Invalid argument type for bytes()") + end +end +compile = loadstring +help = function (object) -- help + print("Help for object:", object) + print("Type:", type(object)) + print("Learn more in the official roblox documentation!") +end +memoryview = function (object) -- memoryview + if type(object) == "table" then + local buffer = table.concat(object) + return { buffer = buffer, itemsize = 1 } + else + error("Invalid argument type for memoryview()") + end +end +repr = function (object) -- repr + return tostring(object) +end +sorted = function (iterable, cmp, key, reverse) -- sorted + local sortedTable = {} + for key, value in pairs(iterable) do + table.insert(sortedTable, { key = key, value = value }) + end + table.sort(sortedTable, function(a, b) + -- Compare logic based on cmp, key, reverse parameters + return a.key < b.key + end) + local i = 0 + return function() + i = i + 1 + local entry = sortedTable[i] + if entry then + return entry.key, entry.value + end + end +end +vars = function (object) -- vars + local attributes = {} + for key, value in pairs(object) do + attributes[key] = value + end + return attributes end""" else: error("Auto-generated dependency unhandled '{}', please report this issue on Discord or Github".format(depend)) @@ -2684,7 +3035,8 @@ def usage(): {TAB}\033[1m-v\033[0m show version information {TAB}\033[1m-vd\033[0m show version number only" {TAB}\033[1m-ast\033[0m show python ast tree before code -{TAB}\033[1m-s\033[0m generate std.lua +{TAB}\033[1m-f\033[0m include standard python functions in generated code +{TAB}\033[1m-fn\033[0m do not include standard python functions in generated code {TAB}\033[1m-u\033[0m open this""") sys.exit() @@ -2699,6 +3051,7 @@ def main(): ast = False input_filename = "NONE" type = 1 # 1: py->lua, 2: lua->py + includeSTD = False for arg in args: if arg == "-v": version() @@ -2707,8 +3060,10 @@ def main(): sys.exit() elif arg == "-u": usage() - elif arg == "-s": - error("In development") + elif arg == "-f": + includeSTD = True + elif arg == "-fn": + includeSTD = False elif arg == "-ast": ast = True elif arg == "-py": @@ -2734,7 +3089,7 @@ def main(): translator = Translator(Config(".pyluaconf.yaml"), show_ast=ast) - lua_code = translator.translate(content) + lua_code = translator.translate(content, includeSTD) if not ast: print(lua_code) From 0093e1846f8e0dc220a832b2e845a63be3e37c36 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Wed, 4 Oct 2023 17:10:21 -0500 Subject: [PATCH 39/77] Type creation --- src/rbxpy.py | 93 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 34 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index 9225ad6..fc66f76 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -413,7 +413,8 @@ def visit_AnnAssign(self, node): value = self.visit_all(node.value, inline=True) local_keyword = "" last_ctx = self.context.last() - if last_ctx["class_name"]: + istype = (last_ctx["class_name"] == "") + if last_ctx["class_name"] and not istype: target = ".".join([last_ctx["class_name"], target]) if "." not in target and not last_ctx["locals"].exists(target): local_keyword = "local " @@ -436,6 +437,12 @@ def visit_AnnAssign(self, node): target=target, value=value, type=type)) + if istype: + error("Named type values cannot be added") + else: + self.emit("{target}: {type},".format(local=local_keyword, + target=target, + type=type)) # example input: # a: int = 1 # example output: @@ -517,38 +524,45 @@ def visit_Call(self, node): def visit_ClassDef(self, node): """Visit class definition""" bases = [self.visit_all(base, inline=True) for base in node.bases] + + if "type" in bases: + self.emit("type {} = {{".format(node.name, self.visit_all(node.bases[0], inline=True))) + self.context.push({"class_name": ""}) + self.visit_all(node.body) + self.context.pop() + self.emit("}") + else: + local_keyword = "" + last_ctx = self.context.last() + if not last_ctx["class_name"] and not last_ctx["locals"].exists(node.name): + local_keyword = "local " + last_ctx["locals"].add_symbol(node.name) - local_keyword = "" - last_ctx = self.context.last() - if not last_ctx["class_name"] and not last_ctx["locals"].exists(node.name): - local_keyword = "local " - last_ctx["locals"].add_symbol(node.name) - - name = node.name - if last_ctx["class_name"]: - name = ".".join([last_ctx["class_name"], name]) + name = node.name + if last_ctx["class_name"]: + name = ".".join([last_ctx["class_name"], name]) - values = { - "local": local_keyword, - "name": name, - "node_name": node.name, - } + values = { + "local": local_keyword, + "name": name, + "node_name": node.name, + } - self.emit("{local}{name} = class(function({node_name})".format(**values)) - self.depend("class") - - self.context.push({"class_name": node.name}) - self.visit_all(node.body) - self.context.pop() + self.emit("{local}{name} = class(function({node_name})".format(**values)) + self.depend("class") + + self.context.push({"class_name": node.name}) + self.visit_all(node.body) + self.context.pop() - self.output[-1].append("return {node_name}".format(**values)) + self.output[-1].append("return {node_name}".format(**values)) - self.emit("end, {{{}}})".format(", ".join(bases))) + self.emit("end, {{{}}})".format(", ".join(bases))) - # Return class object only in the top-level classes. - # Not in the nested classes. - if self.config["class"]["return_at_the_end"] and not last_ctx["class_name"]: - self.emit("return {}".format(name)) + # Return class object only in the top-level classes. + # Not in the nested classes. + if self.config["class"]["return_at_the_end"] and not last_ctx["class_name"]: + self.emit("return {}".format(name)) def visit_Compare(self, node): """Visit compare""" @@ -1029,7 +1043,7 @@ def visit_Subscript(self, node): } self.emit(line.format(**values)) - + def visit_Tuple(self, node): """Visit tuple""" elements = [self.visit_all(item, inline=True) for item in node.elts] @@ -1527,7 +1541,7 @@ def translate(self, pycode, fn): setmetatable(c, mt) return c -end""" +end""" elif depend == "in": DEPEND += """function op_in(item, items) if type(items) == "table" then @@ -2242,7 +2256,6 @@ def parse_tokens(tokens, in_body=0, in_table_construct=0, in_fn_arguments=0): # [ is beeing used as a accessor for table if in_table_construct == 0 and is_op(token, "["): key_tokens = extract_until_end_op(tokens, "]") - expression = { "type": "call", "name": "[", @@ -3097,7 +3110,10 @@ def main(): file_handler = open(input_filename, 'r') source = file_handler.read() - tokens = lexer(source) + try: + tokens = lexer(source) + except: + error("lexer error") #if kwargs["strip_comments"]: # tokens = list(filter(lambda x: x["type"] != "COMMENT", tokens)) @@ -3107,13 +3123,19 @@ def main(): # pprint(tokens) # return - ast_ = parse(tokens) + try: + ast_ = parse(tokens) + except: + error("parser error") #if kwargs["ast"]: # pprint(ast_) # return - py_ast = ast_to_py_ast(ast_) + try: + py_ast = ast_to_py_ast(ast_) + except: + error("ast error") #if kwargs["py_ast"]: # print(ast.dump(py_ast)) @@ -3123,7 +3145,10 @@ def main(): # print(astunparse.unparse(py_ast)) # return - print(PY_HEADER + astunparse.unparse(py_ast)) + try: + print(PY_HEADER + astunparse.unparse(py_ast)) + except: + error("generator error") return 0 From 6848f81a83cae4118cdc2c5062990b484f3d4f37 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Wed, 4 Oct 2023 17:32:15 -0500 Subject: [PATCH 40/77] Remove Lua->Py and replace with a parser --- src/rbxpy.py | 1173 ++------------------------------------------------ 1 file changed, 38 insertions(+), 1135 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index fc66f76..d60a987 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -import sys, ast, yaml, re, os, astunparse +import sys, ast, yaml, re, os, subprocess from pprint import pprint from pathlib import Path @@ -389,7 +389,7 @@ def visit_FormattedValue(self, node): def visit_Bytes(self, node): """Visit bytes""" # Use utf8 strings instead of bytes - self.emit("\"{}\"".format(node.s.decode("utf8"))) + self.emit("[[{}]]".format(node.s.decode("utf8"))) def visit_TryStar(self, node): @@ -1940,1098 +1940,6 @@ def add_indentation(line): def get_luainit(): # Return STDlib return """""" -""" Lua Lexer """ -# Convert source to tokens -def is_operator(char): - return char in [ - "~=", "=", "==", "(", ")", "<", "+", "-", "*", ">", "<", "not", "%", - "/", "{", "}", ",", "[", "]", "#" - ] - - -KEYWORDS = [ - "while", "do", "end", "if", "elseif", "else", "then", "function", "return", - "for", "in", -] - - -def is_keyword(word): - return word in KEYWORDS - - -def is_letter(char): - return re.search(r'[a-zA-Z]|_', char) - - -def is_num(char): - return re.search(r'[0-9]', char) - - -def extract_operator(chars): - op = "" - for letter in chars: - if not is_operator(op+letter): - break - - op = op+letter - del chars[0:len(op)] - return op - - -def extract_num(chars): - num = "" - - for letter in chars: - if not is_num(letter) and letter != ".": - break - - num = num+letter - del chars[0:len(num)] - return num - - -def extract_str(indicator, chars): - out = "" - for letter in chars[1:]: - if letter == indicator: - break - - out = out+letter - del chars[0:len(out)+2] - return out - - -def extract_word(chars): - word = "" - for letter in chars: - if not is_letter(letter) and not re.search(r'([0-9]|_)', letter): - break - - word = word+letter - del chars[0:len(word)] - return word - - -def extract_multiline_comment(chars): - string_chars = "".join(chars) - end_index = string_chars.index("--]]") - - val = string_chars[0:end_index] - del chars[0:end_index+4] - return val - - -def extract_comment(chars): - string_chars = "".join(chars) - end_index = string_chars.index("\n") - - val = string_chars[2:end_index] - del chars[0:end_index] - return val - - -def extract_multiline_str(chars): - string_chars = "".join(chars) - end_index = string_chars.index("]]") - - val = string_chars[2:end_index] - del chars[0:end_index+2] - return val - -def lexer(source): - chars = list(source) - tokens = [] - - while len(chars): - char = chars[0] - - if char == "\n": - char = chars.pop(0) - tokens.append({"type": "NL"}) - continue - - if chars[0:4] == ["-", "-", "[", "["]: - comment = extract_multiline_comment(chars) - tokens.append({"type": "MULTI-COMMENT", "value": comment}) - continue - - if chars[0:3] == ['n', 'i', 'l']: - tokens.append({"type": "NIL", "value": 'nil'}) - del chars[0:3] - continue - - if chars[0:2] == ["-", "-"]: - comment = extract_comment(chars) - tokens.append({"type": "COMMENT", "value": comment}) - continue - - if chars[0:3] == ["a", "n", "d"]: - tokens.append({"type": "OP", "value": "and"}) - del chars[0:3] - continue - - if chars[0:2] == ["o", "r"]: - tokens.append({"type": "OP", "value": "or"}) - del chars[0:2] - continue - - if chars[0:3] == ["n", "o", "t"]: - tokens.append({"type": "OP", "value": "not"}) - del chars[0:3] - continue - - if chars[0:2] == ["~", "="]: - tokens.append({"type": "OP", "value": "~="}) - del chars[0:2] - continue - - if chars[0:2] == [".", "."]: - tokens.append({"type": "OP", "value": ".."}) - del chars[0:2] - continue - - if chars[0:2] == [">", "="]: - tokens.append({"type": "OP", "value": ">="}) - del chars[0:2] - continue - - if chars[0:2] == ["<", "="]: - tokens.append({"type": "OP", "value": "<="}) - del chars[0:2] - continue - - if len(chars) >= 2 and char == "-" and is_num(chars[1]): - del chars[0:1] - num = "-"+extract_num(chars) - tokens.append({"type": "NUMBER", "value": num}) - continue - - if is_num(char): - num = extract_num(chars) - tokens.append({"type": "NUMBER", "value": num}) - continue - - if is_operator(char): - operator = extract_operator(chars) - tokens.append({"type": "OP", "value": operator}) - continue - - if char == "'": - string = extract_str("'", chars) - tokens.append({"type": "STRING", "value": string}) - continue - - if char == '"': - string = extract_str('"', chars) - tokens.append({"type": "STRING", "value": string}) - continue - - if chars[0:2] == ["[", "["]: - comment = extract_multiline_str(chars) - tokens.append({"type": "STRING", "value": comment}) - continue - - if is_letter(char): - word = extract_word(chars) - if is_keyword(word): - tokens.append({"type": "KEYWORD", "value": word}) - continue - - if word in ["true", "false"]: - tokens.append({"type": "BOOLEAN", "value": word}) - continue - - tokens.append({"type": "NAME", "value": word}) - continue - - chars.pop(0) - - return tokens - -""" Lua Parser """ -# Covert tokens to AST - -OPERATORS = [ - "+", "-", "=", "*", ">", "<", "~=", "==", "..", ">=", "<=", "%", "/", "and", "or", -] - -fn_name_index = 0 - - -def generate_function_name(): - global fn_name_index - - fn_name_index = fn_name_index + 1 - return "__fn{0}".format(fn_name_index) - - -def parse_tokens(tokens, in_body=0, in_table_construct=0, in_fn_arguments=0): - - out = [] - - while len(tokens) > 0: - token = tokens.pop(0) - - # Make sure we do not construct tuple when comma list is passed - # as function arguments/table constructor - if len(tokens) \ - and is_op(tokens[0], ",") \ - and in_fn_arguments == 0 \ - and in_table_construct == 0: - - tuple_tokens = [token] - tokens.pop(0) - - # Continue through comma list until the end - while len(tokens) > 0: - tuple_tokens.append(tokens.pop(0)) - - if len(tokens) == 0: - break - - if not is_op(tokens[0], ","): - break - - tokens.pop(0) - - out.append({"type": "tuple", "value": parse_tokens(tuple_tokens)}) - continue - - if token["type"] == "NUMBER": - out.append({"type": "number", "value": token["value"]}) - continue - - if token["type"] == "STRING": - out.append({"type": "string", "value": token["value"]}) - continue - - if token["type"] == "BOOLEAN": - out.append({"type": "boolean", "value": token["value"]}) - continue - - if token["type"] == "NIL": - out.append({"type": "nil", "value": None}) - continue - - if token["type"] == "OP" and token["value"] == "#": - assignments = extract_assignments(tokens) - out.append({ - "type": "call", - "name": "#", - "args": parse_tokens(assignments), - }) - continue - - if token["type"] == "OP" and token["value"] == "{": - table_tokens = extract_table(tokens) - table_tokens = extract_assignments_by_comma(table_tokens) - - nodes = map( - lambda x: parse_tokens(x, in_table_construct=1), - table_tokens - ) - - nodes = [x[0] for x in nodes] - - # print(table_tokens) - out.append({ - "type": "table", - "value": nodes, - }) - continue - - if token["type"] == "OP" and token["value"] == "not": - assignments = extract_assignments(tokens) - out.append({ - "type": "call", - "name": token["value"], - "args": parse_tokens(assignments), - }) - continue - - # Ignore [ if beeing used as constructor in table - if in_table_construct == 1 and is_op(token, "["): - continue - - # [ is beeing used as a accessor for table - if in_table_construct == 0 and is_op(token, "["): - key_tokens = extract_until_end_op(tokens, "]") - expression = { - "type": "call", - "name": "[", - "args": [out.pop(), parse_tokens(key_tokens)], - } - - if in_body: # Do not wrap expression if already running in one - out.append({ - "type": "expr", - "value": [expression], - }) - else: - out.append(expression) - continue - - if token["type"] == "OP" and token["value"] in OPERATORS: - assignments = extract_assignments(tokens) - - # Move function outside assignment and declare it in above scope - # with keyword ref - if token["value"] == "=" and is_lex_keyword(assignments[0], "function"): - assignments = inline_anonymous_function(assignments, out) - - out.append({ - "type": "call", - "name": token["value"], - "args": [ - out.pop(), - parse_tokens( - assignments, - in_table_construct=in_table_construct, - ) - ], - }) - continue - - if token["type"] == "NAME" and len(tokens) and is_op(tokens[0], "("): - args = extract_args(tokens) - - expression = { - "type": "call", - "name": token["value"], - "args": parse_tokens(args, in_fn_arguments=1) - } - - if in_body: # Do not wrap expression if already running in one - out.append({ - "type": "expr", - "value": [expression], - }) - else: - out.append(expression) - - continue - - if token["type"] == "KEYWORD" and token["value"] == "else": - body = extract_if_body(tokens) - - out.append({ - "type": "else", - "body": parse_tokens(body, in_body=1), - }) - continue - - if token["type"] == "KEYWORD" and token["value"] in ["if", "elseif"]: - if_nodes = extract_scope_body(tokens) - - test_nodes = extract_to_keyword(if_nodes, "then") - body = extract_if_body(if_nodes) - - out.append({ - "type": "if", - "test": parse_tokens(test_nodes), - "body": parse_tokens(body, in_body=1), - "else": parse_tokens(if_nodes), - }) - continue - - if token["type"] == "KEYWORD" and token["value"] == "for": - - - body_tokens = extract_scope_body(tokens) - iteration_tokens = extract_to_keyword(body_tokens, "do") - if contains_op(iteration_tokens, "="): - target_tokens = extract_to_op(iteration_tokens, "=") - else: - target_tokens = extract_to_keyword(iteration_tokens, "in") - - out.append({ - "type": "for", - "target": parse_tokens(target_tokens), - "iteration": parse_tokens(iteration_tokens), - "body": parse_tokens(body_tokens, in_body=1), - }) - continue - - if token["type"] == "KEYWORD" and token["value"] == "while": - while_tokens = extract_scope_body(tokens) - test_tokens = extract_to_keyword(while_tokens, "do") - - out.append({ - "type": "while", - "test": parse_tokens(test_tokens), - "body": parse_tokens(while_tokens, in_body=1), - }) - continue - - if token["type"] == "KEYWORD" and token["value"] == "return": - assignments = extract_assignments(tokens) - - if is_lex_keyword(assignments[0], "function"): - assignments = inline_anonymous_function(assignments, out) - - out.append({ - "type": "return", - "value": parse_tokens(assignments), - }) - continue - - if token["type"] == "KEYWORD" and token["value"] == "function": - function_tokens = extract_scope_body(tokens) - signature_tokens = extract_fn_signature(function_tokens) - function_name = "" - - if signature_tokens[0]["type"] == "NAME": - name_token = signature_tokens.pop(0) - function_name = name_token["value"] - else: - function_name = None - - parameter_tokens = signature_tokens[1:-1] - # Only accept name as argument - parameter_tokens = filter( - lambda x: x["type"] == "NAME", - parameter_tokens - ) - - parameter_tokens = map( - lambda x: {"type": "argument", "name": x["value"]}, - parameter_tokens - ) - - out.append({ - "type": "function", - "name": function_name, - "args": list(parameter_tokens), - "body": parse_tokens(function_tokens, in_body=1), - }) - continue - - if token["type"] == "NAME": - out.append({ - "type": "name", - "name": token["value"], - }) - continue - - return out - - -def is_op(token, op): - return token.get("type", None) == "OP" and token["value"] == op - - -def is_lex_keyword(token, keyword): - return token["type"] == "KEYWORD" and token["value"] == keyword - - -def extract_table(tokens): - out = [] - depth = 0 - - while len(tokens) > 0: - token = tokens.pop(0) - out.append(token) - - if depth > 0 and is_op(token, "}"): - depth = depth +1 - continue - - if is_op(token, "}"): - break - - return out - - -def extract_fn_signature(tokens): - out = [] - - while len(tokens) > 0: - token = tokens.pop(0) - out.append(token) - if is_op(token, ")"): - break - return out - - -def extract_scope_body(tokens): - out = [] - - depth = 0 - - while len(tokens) > 0: - token = tokens.pop(0) - out.append(token) - - if token["type"] == "KEYWORD" and token["value"] in ["if", "function"]: - depth = depth + 1 - continue - - if depth > 0 and token["type"] == "KEYWORD" and token["value"] in ["if", "end"]: - depth = depth - 1 - continue - - if depth == 0 and token["type"] == "KEYWORD" and token["value"] == "end": - break - - return out - - -def extract_if_body(tokens): - out = [] - depth = 0 - - while len(tokens) > 0: - token = tokens[0] - - if is_lex_keyword(token, "if"): - out.append(token) - tokens.pop(0) - depth = depth + 1 - continue - - if depth > 0 and is_lex_keyword(token, "end"): - out.append(token) - tokens.pop(0) - depth = depth - 1 - continue - - if depth == 0 and is_lex_keyword(token, "elseif"): - break - - if depth == 0 and is_lex_keyword(token, "else"): - break - - if depth == 0 and is_lex_keyword(token, "end"): - break - - out.append(token) - tokens.pop(0) - - return out - -def extract_until_end_op(tokens, exit_op="]"): - out = [] - - while len(tokens) > 0: - token = tokens.pop(0) - - if is_op(token, exit_op): - break - - out.append(token) - - return out - -def extract_until_end_op(tokens, exit_op="]"): - out = [] - - while len(tokens) > 0: - token = tokens.pop(0) - - if is_op(token, exit_op): - break - - out.append(token) - - return out - - -def extract_to_op(tokens, exit_op="="): - out = [] - - while len(tokens) > 0: - token = tokens.pop(0) - - if is_op(token, exit_op): - break - - out.append(token) - - return out - -def extract_to_keyword(tokens, exit_keyword="then"): - out = [] - - while len(tokens) > 0: - token = tokens.pop(0) - - if is_lex_keyword(token, exit_keyword): - break - - out.append(token) - - return out - - -def extract_assignments(tokens): - out = [] - depth = 0 - - while len(tokens) > 0: - token = tokens.pop(0) - - if is_lex_keyword(token, "function"): - out.append(token) - depth = depth + 1 - continue - - if is_op(token, "("): - out.append(token) - depth = depth + 1 - continue - - if is_op(token, "{"): - out.append(token) - depth = depth + 1 - continue - - if is_op(token, ")"): - out.append(token) - depth = depth - 1 - continue - - if is_lex_keyword(token, "end"): - out.append(token) - depth = depth - 1 - continue - - if is_op(token, "}"): - out.append(token) - depth = depth - 1 - continue - - if token["type"] == "NL" and depth == 0: - break - - out.append(token) - - return out - -def inline_anonymous_function(tokens, out): - fn_name = generate_function_name() - - fn_tokens = parse_tokens(tokens) - fn_tokens[0]["name"] = fn_name - out.insert(-1, fn_tokens[0]) - assignments = [{"type": "NAME", "value": fn_name}] - return assignments - - -def extract_args(tokens): - depth = 1 - args = [] - - tokens.pop(0) # Drop ( - - while depth != 0: - token = tokens.pop(0) - - if is_op(token, "("): - depth = depth+1 - - if is_op(token, ")"): - depth = depth-1 - - args.append(token) - - args = args[:-1] # Drop ) - return args - - -def extract_assignments_by_comma(tokens): - pairs = [[]] - depth = 0 - - while len(tokens) > 0: - token = tokens.pop(0) - - if is_op(token, "{"): - depth = depth + 1 - - if is_op(token, "("): - depth = depth + 1 - - if is_op(token, "}"): - depth = depth - 1 - - if is_op(token, ")"): - depth = depth - 1 - - if depth == 0 and is_op(token, ","): - pairs.append([]) - continue - - pairs[-1].append(token) - - return pairs - - -def contains_op(tokens, op): - for token in tokens: - if is_op(token, op): - return True - return False - - -def parse(tokens): - ast_ = parse_tokens(tokens, in_body=1) - return ast_ - -""" Python Generator """ - -def ast_to_py_ast(nodes): - ast_ = parse_nodes(nodes) - - bootstrap = [] - - ast_ = bootstrap + ast_ - - tree = ast.Module(ast_, []) - tree = ast.fix_missing_locations(tree) - - return tree - - -def parse_nodes(nodes, ctx_klass=ast.Load): - out = [] - while len(nodes) > 0: - node = nodes.pop(0) - - if node["type"] == "name" and node["name"] == "_G": - out.append( - ast.Call( - func=ast.Name(id='globals', ctx=ast.Load()), - args=[], - keywords=[], - ) - ) - continue - - if node["type"] == "tuple": - expressions = parse_nodes(node["value"], ctx_klass=ctx_klass) - - out.append( - ast.Tuple( - elts=expressions, - ctx=ctx_klass(), - ) - ) - continue - - if node["type"] == "table": - argument_nodes = [] - keyword_nodes = [] - - for x in node["value"]: - if not (x["type"] == "call" and x["name"] == "="): - argument_nodes.append(x) - continue - - keyword_nodes.append(x) - - key_nodes = [x["args"][0] for x in keyword_nodes] - # Convert name references to strings - key_nodes = [ - {"type": "string", "value": x["name"]} - if x["type"] == "name" else x - for x in key_nodes - ] - - value_nodes = [x["args"][1] for x in keyword_nodes] - value_nodes = [x[0] for x in value_nodes] - value_nodes = parse_nodes(value_nodes) - - keywords = [] - for x in (zip(key_nodes, value_nodes)): - name_node, value_node = x - name = name_node["value"] - - # Apply __ to make sure its casted in Table - if name_node["type"] == "number": - name = "__{0}".format(name) - - keywords.append( - ast.keyword(arg=name, value=value_node) - ) - - out.append( - ast.Call( - func=ast.Name(id='Table', ctx=ast.Load()), - args=parse_nodes(argument_nodes), - keywords=keywords, - ) - ) - continue - - if node["type"] == "string": - out.append(ast.Str(s=node["value"])) - continue - - if node["type"] == "boolean": - value = node["value"] - value = True if value == "true" else value - value = False if value == "false" else value - out.append(ast.NameConstant(value=value)) - continue - - if node["type"] == "number": - value = node["value"] - value = float(value) if "." in value else int(value) - - out.append(ast.Num(n=value)) - continue - - if node["type"] == "nil": - out.append(ast.NameConstant(value=None)) - continue - - if node["type"] == "return": - out.append( - ast.Return(value=parse_nodes(node["value"])[0]) - ) - continue - - if node["type"] == "assign": - out.append( - ast.Assign( - targets=[ - ast.Name(id=node["name"], ctx=ast.Store()) - ], - value=parse_nodes(node["value"])[0], - ) - ) - continue - - if node["type"] == "name": - out.append( - ast.Name(id=node["name"], ctx=ctx_klass()), - ) - continue - - if node["type"] == "expr": - out.append( - ast.Expr( - value=parse_nodes(node["value"])[0] - ) - ) - continue - - if node["type"] == "function": - body_nodes = parse_nodes(node["body"]) - out.append( - ast.FunctionDef( - name=node["name"], - args=ast.arguments( - args=[ - ast.arg( - arg=x["name"], - annotation=None, - ) for x in node["args"] - ], - posonlyargs=[], - vararg=None, - kwonlyargs=[], - kw_defaults=[], - kwarg=None, - defaults=[] - ), - body=body_nodes, - decorator_list=[], - ) - ) - continue - - if node["type"] == "if": - test_nodes = parse_nodes(node["test"]) - body_nodes = parse_nodes(node["body"]) - else_nodes = parse_nodes(node["else"]) - - out.append( - ast.If( - test=test_nodes[0], - body=body_nodes, - orelse=else_nodes, - ) - ) - continue - - if node["type"] == "for": - target_expr = parse_nodes(node["target"], ctx_klass=ast.Store) - body_expr = parse_nodes(node["body"]) - - iteration_nodes = node["iteration"] - - # Apply range constructor - if iteration_nodes[0]["type"] == "tuple": - iteration_expr = [ - ast.Call( - func=ast.Name(id='get_for_range', ctx=ast.Load()), - args=parse_nodes(iteration_nodes[0]["value"]), - keywords=[], - ) - ] - - else: - iteration_expr = parse_nodes(iteration_nodes) - - out.append( - ast.For( - target=target_expr[0], - iter=iteration_expr[0], - body=body_expr, - orelse=[] - ) - ) - continue - - if node["type"] == "while": - test_nodes = parse_nodes(node["test"]) - body_nodes = parse_nodes(node["body"]) - - out.append( - ast.While( - test=test_nodes[0], - body=body_nodes, - orelse=[], - ) - ) - - if node["type"] == "else": - body_nodes = parse_nodes(node["body"]) - out = out + body_nodes - continue - - if node["type"] == "call": - if node["name"] == "#": - out.append( - ast.Call( - func=ast.Name(id='len', ctx=ast.Load()), - args=parse_nodes(node["args"]), - keywords=[], - ) - ) - continue - - if node["name"] == "[": - value_node = node["args"][0] - value_expression = parse_nodes([value_node])[0] - - out.append( - ast.Subscript( - value=value_expression, - slice=ast.Index( - value=parse_nodes(node["args"][1])[0] - ), - ctx=ast.Load(), - ) - ) - - continue - - if node["name"] == "=": - name_arg = node["args"][0] - value_arg = node["args"][1] - - target_expr = parse_nodes([name_arg], ctx_klass=ast.Store) - value_expr = parse_nodes(value_arg) - - out.append( - ast.Assign( - targets=target_expr, - value=value_expr[0], - ) - ) - continue - - if node["name"] in ["-", "%", "+", "..", "*", "/"]: - ops = node["name"] - - arg_left = parse_nodes([node["args"][0]]) - arg_right = parse_nodes(node["args"][1]) - - ops_ref = { - "-": ast.Sub, - "%": ast.Mod, - "+": ast.Add, - "..": ast.Add, - "*": ast.Mult, - "/": ast.Div, - } - - out.append( - ast.BinOp( - left=arg_left[0], - op=ops_ref[ops](), - right=arg_right[0], - ) - ) - continue - - if node["name"] in ["and", "or"]: - ops = node["name"] - - arg_left = parse_nodes([node["args"][0]]) - arg_right = parse_nodes(node["args"][1]) - - ops_ref = { - "and": ast.And, - "or": ast.Or, - } - - out.append( - ast.BoolOp( - op=ops_ref[ops](), - values=[ - arg_left[0], - arg_right[0], - ] - ) - ) - continue - - if node["name"] in [">", "<", "~=", "==", "<=", ">="]: - ops = node["name"] - - arg_left = parse_nodes([node["args"][0]]) - arg_right = parse_nodes(node["args"][1]) - - ops_ref = { - ">": ast.Gt, - ">=": ast.GtE, - "<": ast.Lt, - "<=": ast.LtE, - "~=": ast.NotEq, - "==": ast.Eq, - } - - out.append( - ast.Compare( - left=arg_left[0], - ops=[ops_ref[ops]()], - comparators=arg_right, - ) - ) - continue - - if node["name"] == "not": - out.append( - ast.UnaryOp( - op=ast.Not(), - operand=parse_nodes(node["args"])[0] - ) - ) - continue - - out.append( - ast.Call( - func=ast.Name(id=node["name"], ctx=ast.Load()), - args=parse_nodes(node["args"], ctx_klass=ast.Load), - keywords=[] - ) - ) - continue - - return out #### INTERFACE #### def warn(msg): @@ -3084,6 +1992,8 @@ def main(): elif arg == "-lua": type = 2 else: + if input_filename != "NONE": + error("Unexpected argument: '{}'".format(arg)) input_filename = arg if type == 1: @@ -3107,48 +2017,41 @@ def main(): if not ast: print(lua_code) else: - file_handler = open(input_filename, 'r') - source = file_handler.read() - - try: - tokens = lexer(source) - except: - error("lexer error") - - #if kwargs["strip_comments"]: - # tokens = list(filter(lambda x: x["type"] != "COMMENT", tokens)) - # tokens = list(filter(lambda x: x["type"] != "MULTILINE-COMMENT", tokens)) - - #if kwargs["tokens"]: - # pprint(tokens) - # return - - try: - ast_ = parse(tokens) - except: - error("parser error") - - #if kwargs["ast"]: - # pprint(ast_) - # return - - try: - py_ast = ast_to_py_ast(ast_) - except: - error("ast error") - - #if kwargs["py_ast"]: - # print(ast.dump(py_ast)) - # return - - #if kwargs["py_code"]: - # print(astunparse.unparse(py_ast)) - # return - + command = "luac -l "+input_filename + try: - print(PY_HEADER + astunparse.unparse(py_ast)) - except: - error("generator error") + res = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + sys.exit(1) + + res = (res.decode("utf-8")) + + # Parse + commands = {} + + lines = res.split("\n") + lines.pop(0) + lines.pop(0) + lines.pop(0) + for line in lines: + data = line.split("\t") + if len(data) < 4: + continue + num, cmd, args = data[1], data[3], data[4] + if len(data) > 5: + data = data[5] + else: + data = "NONE" + + commands[num] = {"cmd": cmd, "args": args, "data": data} + res = "\n".join(lines) + + print(commands) + + # If luac.out is found then remove it + if Path("luac.out").is_file(): + os.remove("luac.out") + return 0 From f1b32c5687703c67b8e7b802086dc5d222b4f347 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Wed, 4 Oct 2023 17:59:44 -0500 Subject: [PATCH 41/77] Bug fixes --- src/rbxpy.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index d60a987..9ad18cf 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -413,7 +413,7 @@ def visit_AnnAssign(self, node): value = self.visit_all(node.value, inline=True) local_keyword = "" last_ctx = self.context.last() - istype = (last_ctx["class_name"] == "") + istype = (last_ctx["class_name"] == "TYPE") if last_ctx["class_name"] and not istype: target = ".".join([last_ctx["class_name"], target]) if "." not in target and not last_ctx["locals"].exists(target): @@ -437,10 +437,13 @@ def visit_AnnAssign(self, node): target=target, value=value, type=type)) - if istype: - error("Named type values cannot be added") else: - self.emit("{target}: {type},".format(local=local_keyword, + if istype: + self.emit("{target}: {type},".format(local=local_keyword, + target=target, + type=type)) + else: + self.emit("{local}{target} = nil".format(local=local_keyword, target=target, type=type)) # example input: @@ -527,7 +530,7 @@ def visit_ClassDef(self, node): if "type" in bases: self.emit("type {} = {{".format(node.name, self.visit_all(node.bases[0], inline=True))) - self.context.push({"class_name": ""}) + self.context.push({"class_name": "TYPE"}) self.visit_all(node.body) self.context.pop() self.emit("}") @@ -1241,7 +1244,7 @@ def translate(self, pycode, fn): # set if depend == "list": - DEPEND += """function list(t) + DEPEND += """\n\nfunction list(t) local result = {} result._is_list = true @@ -1367,7 +1370,7 @@ def translate(self, pycode, fn): return result end""" elif depend == "dict": - DEPEND += """function dict(t) + DEPEND += """\n\nfunction dict(t) local result = {} result._is_dict = true @@ -1499,7 +1502,7 @@ def translate(self, pycode, fn): return result end""" elif depend == "class": - DEPEND += """function class(class_init, bases) + DEPEND += """\n\nfunction class(class_init, bases) bases = bases or {} local c = {} @@ -1543,7 +1546,7 @@ def translate(self, pycode, fn): return c end""" elif depend == "in": - DEPEND += """function op_in(item, items) + DEPEND += """\n\nfunction op_in(item, items) if type(items) == "table" then for v in items do if v == item then @@ -1557,7 +1560,7 @@ def translate(self, pycode, fn): return false end""" elif depend == "fn": - DEPEND += """if game then + DEPEND += """\n\nif game then __name__ = if script:IsA("BaseScript") then "__main__" else script.Name else __name__ = nil @@ -1940,6 +1943,8 @@ def add_indentation(line): def get_luainit(): # Return STDlib return """""" +#class PyGenerator: + #### INTERFACE #### def warn(msg): @@ -1968,6 +1973,11 @@ def version(): """The main entry point to the translator""" def main(): """Entry point function to the translator""" + + # Enable support for ANSI escape sequences + if os.name == "nt": + os.system("cmd /c \"setx ENABLE_VIRTUAL_TERMINAL_PROCESSING 1\"") + args = sys.argv[1:] ast = False input_filename = "NONE" From 76be97f8262efa637719038b3619fa071c7e38e0 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Wed, 4 Oct 2023 18:03:00 -0500 Subject: [PATCH 42/77] Events --- src/rbxpy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index 9ad18cf..eca053d 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -755,7 +755,7 @@ def visit_FunctionDef(self, node): "name": name, "decorator": decorator_name, } - line = "{name} = {decorator}({name})".format(**values) + line = "{name} = {{['fn'] = {name}, ['event'] = {decorator}:Connect({name})}}".format(**values) self.emit(line) def visit_For(self, node): From 10e7841de215850c2f7e2872e8bc33e1e6a461bd Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Wed, 4 Oct 2023 18:16:29 -0500 Subject: [PATCH 43/77] Import services --- src/rbxpy.py | 52 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index eca053d..2463d72 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -916,24 +916,44 @@ def visit_ImportFrom(self, node): else: module = module - for name in node.names: - if name.asname is None: - if name.name == "*": - error("import * is unsupproted") + if module == "services": + for name in node.names: + if name.asname is None: + if name.name == "*": + error("import * is unsupproted") + else: + self.emit("local {name} = game:GetService(\"{name}\")".format( + name=name.name, + module=module, + )) else: - self.emit("local {name} = require(\"{module}\").{name}".format( - name=name.name, - module=module, - )) - else: - if name.name == "*": - error("import * is unsupproted") + if name.name == "*": + error("import * is unsupproted") + else: + self.emit("local {name} = game:GetService(\"{realname}\")".format( + name=name.asname, + module=module, + realname=name.name, + )) + else: + for name in node.names: + if name.asname is None: + if name.name == "*": + error("import * is unsupproted") + else: + self.emit("local {name} = require(\"{module}\").{name}".format( + name=name.name, + module=module, + )) else: - self.emit("local {name} = require(\"{module}\").{realname}".format( - name=name.asname, - module=module, - realname=name.name, - )) + if name.name == "*": + error("import * is unsupproted") + else: + self.emit("local {name} = require(\"{module}\").{realname}".format( + name=name.asname, + module=module, + realname=name.name, + )) def visit_Index(self, node): """Visit index""" From 1e9f0003470f1c4d28d9b9cfdb300ebd0e34846b Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Wed, 4 Oct 2023 18:28:48 -0500 Subject: [PATCH 44/77] Import services --- src/rbxpy.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index 2463d72..afbe5ff 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -898,13 +898,19 @@ def visit_Import(self, node): line = 'local {asname} = require("{name}")' values = {"asname": "", "name": ""} + if node.names[0].name.startswith("game."): + line = 'local {asname} = game:GetService("{name}")' + values["name"] = node.names[0].name[5:] + if node.names[0].asname is None: - values["name"] = node.names[0].name + if not node.names[0].name.startswith("game."): + values["name"] = node.names[0].name values["asname"] = values["name"] values["asname"] = values["asname"].split(".")[-1] else: values["asname"] = node.names[0].asname - values["name"] = node.names[0].name + if not node.names[0].name.startswith("game."): + values["name"] = node.names[0].name self.emit(line.format(**values)) From caeb487c373654291d01034d2b10589b806072ec Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Wed, 4 Oct 2023 18:44:55 -0500 Subject: [PATCH 45/77] strings support --- src/rbxpy.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index afbe5ff..b14a715 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -290,8 +290,11 @@ def visit_Match(self, node): first = "" else: first = "else" - - self.emit("{}if {} == {} then".format(first, self.visit_all(node.subject, inline=True), case.pattern.value.s)) + + val = case.pattern.value.s + if isinstance(val, str): + val = '"{}"'.format(val) + self.emit("{}if {} == {} then".format(first, self.visit_all(node.subject, inline=True), val)) self.visit_all(case.body) else: self.emit("else") From 1e506018a029ba46c8e9a04e300ff42bdd440eaa Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Wed, 4 Oct 2023 18:45:48 -0500 Subject: [PATCH 46/77] fix From b48bce540b4e61583b28b5076cd0b7a08347e141 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Wed, 4 Oct 2023 18:56:43 -0500 Subject: [PATCH 47/77] error --- src/rbxpy.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index b14a715..b20b226 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -297,8 +297,9 @@ def visit_Match(self, node): self.emit("{}if {} == {} then".format(first, self.visit_all(node.subject, inline=True), val)) self.visit_all(case.body) else: - self.emit("else") - self.visit_all(case.body) + # self.emit("else") + # self.visit_all(case.body) + error("Match statement requires a compile-time constant, not a variable.") self.emit("end") def visit_MatchValue(self, node): From 2e9386122e4f5ac23cad4f14a9e64c17cf179936 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Wed, 4 Oct 2023 19:17:59 -0500 Subject: [PATCH 48/77] Addition --- src/rbxpy.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index b20b226..46b145a 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -46,7 +46,8 @@ class BinaryOperationDesc: OPERATION = { ast.Add: { "value": "+", - "format": _DEFAULT_BIN_FORMAT, + "format": "(safeadd({left}, {right}))", + "depend": "safeadd", }, ast.Sub: { "value": "-", @@ -458,6 +459,9 @@ def visit_AnnAssign(self, node): def visit_AugAssign(self, node): """Visit augassign""" operation = BinaryOperationDesc.OPERATION[node.op.__class__] + + if operationp["depend"]: + self.depend(operation["depend"]) target = self.visit_all(node.target, inline=True) @@ -490,6 +494,10 @@ def visit_Attribute(self, node): def visit_BinOp(self, node): """Visit binary operation""" operation = BinaryOperationDesc.OPERATION[node.op.__class__] + + if operation["depend"]: + self.depend(operation["depend"]) + line = "({})".format(operation["format"]) #if operation["depend"]: Binary operators do not have it # self.depend(operation["depend"]) @@ -1936,6 +1944,16 @@ def translate(self, pycode, fn): attributes[key] = value end return attributes +end""" + elif depend == "safeadd": + DEPEND += """\n\nfunction safeadd(a, b) + if type(a) == "number" and type(b) == "number" then + return a + b + elseif type(a) == "string" and type(b) == "string" then + return a .. b + else + error(`Attempt to add a {type(a)} and a {type(b)}`) + end end""" else: error("Auto-generated dependency unhandled '{}', please report this issue on Discord or Github".format(depend)) From 0731ef14e4bbe876b9af09ff09c6fc36d8bed323 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Wed, 4 Oct 2023 19:52:32 -0500 Subject: [PATCH 49/77] -o --- src/rbxpy.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index 46b145a..0551dd5 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -2029,9 +2029,16 @@ def main(): args = sys.argv[1:] ast = False input_filename = "NONE" + out = "NONE" type = 1 # 1: py->lua, 2: lua->py includeSTD = False + skip = False + for arg in args: + if skip: + skip = False + continue + if arg == "-v": version() elif arg == "-vd": @@ -2047,6 +2054,9 @@ def main(): ast = True elif arg == "-py": type = 1 + elif arg == "-o": + out = args[args.index(arg)+1] + skip = True elif arg == "-lua": type = 2 else: @@ -2073,7 +2083,11 @@ def main(): lua_code = translator.translate(content, includeSTD) if not ast: - print(lua_code) + if out != "NONE": + with open(out, "w") as file: + file.write(lua_code) + else: + print(lua_code) else: command = "luac -l "+input_filename From 3ebb5ec5b2fcf624f944731169e51ac591687ecf Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Thu, 5 Oct 2023 17:26:40 -0500 Subject: [PATCH 50/77] API improvements --- src/rbxpy.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index 0551dd5..5d3822f 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -2000,8 +2000,11 @@ def warn(msg): def info(msg): sys.stderr.write("\033[1;32m" + "info: " + "\033[0m" + msg) def error(msg): - sys.stderr.write("\033[1;31m" + "error: " + "\033[0m" + msg + "\n") - sys.exit() + if proverr: + proverr(msg) + else: + sys.stderr.write("\033[1;31m" + "error: " + "\033[0m" + msg + "\n") + sys.exit() def usage(): print("\n"+f"""usage: \033[1;33mrbxpy\033[0m [file] [options] > [gen] @@ -2018,6 +2021,10 @@ def version(): print("\033[1;34m" + "copyright:" + "\033[0m" + " roblox-py " + "\033[1m" + VERSION + "\033[0m" + " licensed under the GNU Affero General Public License by " + "\033[1m" + "@AsynchronousAI" + "\033[0m") sys.exit(0) +def provideerr(err): + global proverr + proverr = err + """The main entry point to the translator""" def main(): """Entry point function to the translator""" From 423bf3ad71629ee3ec46853c968ec596c7f16086 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Thu, 5 Oct 2023 17:36:06 -0500 Subject: [PATCH 51/77] change --- src/rbxpy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rbxpy.py b/src/rbxpy.py index 5d3822f..643b427 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -1994,6 +1994,7 @@ def get_luainit(): # Return STDlib #class PyGenerator: #### INTERFACE #### +proverr = None def warn(msg): sys.stderr.write("\033[1;33m" + "warning: " + "\033[0m" + msg) From e6b3dc80d3dd2e4608a2dba78b5c2b86340d5219 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Thu, 5 Oct 2023 17:51:15 -0500 Subject: [PATCH 52/77] bug fix --- src/rbxpy.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rbxpy.py b/src/rbxpy.py index 643b427..373f579 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -260,6 +260,8 @@ def __init__(self, context=None, config=None): self.last_end_mode = TokenEndMode.LINE_FEED self.output = [] + dependencies = [] + def visit_Assign(self, node): """Visit assign""" target = self.visit_all(node.targets[0], inline=True) From a68716362ff5337c9b4543d9deb0fa40bf97f0be Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Thu, 5 Oct 2023 18:12:50 -0500 Subject: [PATCH 53/77] Bug fixes --- src/rbxpy.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index 373f579..de3bcdf 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -260,6 +260,7 @@ def __init__(self, context=None, config=None): self.last_end_mode = TokenEndMode.LINE_FEED self.output = [] + global dependencies dependencies = [] def visit_Assign(self, node): @@ -1272,10 +1273,8 @@ def translate(self, pycode, fn): self.output = visitor.output - # Remove dupelicates from dependencies - for depend in dependencies: - if depend not in dependencies: - dependencies.append(depend) + # Remove duplicates from dependencies (list) + dependencies = list(set(dependencies)) if fn: dependencies.append("fn") From 8def0b9f41127261f8844c81b82146fb1d0a4c18 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Thu, 5 Oct 2023 18:21:30 -0500 Subject: [PATCH 54/77] API fixes --- src/rbxpy.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index de3bcdf..51760e0 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -1253,14 +1253,17 @@ def __init__(self, config=None, show_ast=False): self.output = [] - def translate(self, pycode, fn): + def translate(self, pycode, fn, isAPI = False): """Translate python code to lua code""" - try: - # code that uses ast + if isAPI: py_ast_tree = ast.parse(pycode) - except SyntaxError as err: - sys.stderr.write("\033[1;31m" + "syntax error: " + "\033[0m" + str(err) + "\n") - sys.exit(1) + else: + try: + # code that uses ast + py_ast_tree = ast.parse(pycode) + except SyntaxError as err: + sys.stderr.write("\033[1;31m" + "syntax error: " + "\033[0m" + str(err) + "\n") + sys.exit(1) visitor = NodeVisitor(config=self.config) @@ -1995,18 +1998,13 @@ def get_luainit(): # Return STDlib #class PyGenerator: #### INTERFACE #### -proverr = None - def warn(msg): sys.stderr.write("\033[1;33m" + "warning: " + "\033[0m" + msg) def info(msg): sys.stderr.write("\033[1;32m" + "info: " + "\033[0m" + msg) def error(msg): - if proverr: - proverr(msg) - else: - sys.stderr.write("\033[1;31m" + "error: " + "\033[0m" + msg + "\n") - sys.exit() + sys.stderr.write("\033[1;31m" + "error: " + "\033[0m" + msg + "\n") + sys.exit() def usage(): print("\n"+f"""usage: \033[1;33mrbxpy\033[0m [file] [options] > [gen] From b8523fd5fca7b059338f8ba392c23342a07cfea9 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Thu, 5 Oct 2023 18:22:55 -0500 Subject: [PATCH 55/77] Bug fix --- src/rbxpy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rbxpy.py b/src/rbxpy.py index 51760e0..e312027 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -1277,6 +1277,7 @@ def translate(self, pycode, fn, isAPI = False): self.output = visitor.output # Remove duplicates from dependencies (list) + global dependencies dependencies = list(set(dependencies)) if fn: From 4f1ab6b749a065b72ef41dd03e4a8c64fd82f756 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Thu, 5 Oct 2023 18:31:46 -0500 Subject: [PATCH 56/77] Bug fix --- src/rbxpy.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index e312027..7906775 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -260,9 +260,6 @@ def __init__(self, context=None, config=None): self.last_end_mode = TokenEndMode.LINE_FEED self.output = [] - global dependencies - dependencies = [] - def visit_Assign(self, node): """Visit assign""" target = self.visit_all(node.targets[0], inline=True) From c1790edfbefae33b9df80cffc12539878bb5851d Mon Sep 17 00:00:00 2001 From: aqzp <72946059+AsynchronousAI@users.noreply.github.com> Date: Thu, 5 Oct 2023 22:14:07 -0500 Subject: [PATCH 57/77] Update rbxpy.py --- src/rbxpy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index 7906775..77f029a 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -460,7 +460,7 @@ def visit_AugAssign(self, node): """Visit augassign""" operation = BinaryOperationDesc.OPERATION[node.op.__class__] - if operationp["depend"]: + if operation["depend"]: self.depend(operation["depend"]) target = self.visit_all(node.target, inline=True) @@ -2133,4 +2133,4 @@ def main(): if __name__ == "__main__": - sys.exit(main()) \ No newline at end of file + sys.exit(main()) From 68abe3e96e6c7b456c746c1a99ce6c42a0242086 Mon Sep 17 00:00:00 2001 From: aqzp <72946059+AsynchronousAI@users.noreply.github.com> Date: Thu, 5 Oct 2023 22:17:17 -0500 Subject: [PATCH 58/77] Update rbxpy.py --- src/rbxpy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index 77f029a..bfba00d 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -767,7 +767,7 @@ def visit_FunctionDef(self, node): "name": name, "decorator": decorator_name, } - line = "{name} = {{['fn'] = {name}, ['event'] = {decorator}:Connect({name})}}".format(**values) + line = "{name} = {decorator}:Connect({name})".format(**values) self.emit(line) def visit_For(self, node): From d6216c3e5d798e433b1edfd707cbd8cce58fdb75 Mon Sep 17 00:00:00 2001 From: aqzp <72946059+AsynchronousAI@users.noreply.github.com> Date: Thu, 5 Oct 2023 22:18:48 -0500 Subject: [PATCH 59/77] Update rbxpy.py --- src/rbxpy.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index bfba00d..71dc2ad 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -803,10 +803,22 @@ def visit_AsyncFunctionDef(self, node): last_ctx = self.context.last() name = node.name + type = 1 # 1 = static, 2 = class + for decorator in reversed(node.decorator_list): + decorator_name = self.visit_all(decorator, inline=True) + + if decorator_name == "classmethod": + type = 2 + elif decorator_name == "staticmethod": + type = 1 if last_ctx["class_name"]: name = ".".join([last_ctx["class_name"], name]) - - arguments = [arg.arg for arg in node.args.args] + + if type == 1: + arguments = [arg.arg for arg in node.args.args] + else: + arguments = ["self"] + arguments.extend([arg.arg for arg in node.args.args]) if node.args.vararg is not None: arguments.append("...") @@ -830,8 +842,8 @@ def visit_AsyncFunctionDef(self, node): body = self.output[-1] if node.args.vararg is not None: - line = "local {name} = list {{...}}".format(name=node.args.vararg.arg) self.depend("list") + line = "local {name} = list({{...})".format(name=node.args.vararg.arg) body.insert(0, line) arg_index = -1 @@ -847,17 +859,18 @@ def visit_AsyncFunctionDef(self, node): arg_index -= 1 - self.emit("end)() end") + self.emit("end") for decorator in reversed(node.decorator_list): decorator_name = self.visit_all(decorator, inline=True) + if decorator_name == "classmethod" or decorator_name == "staticmethod": + continue values = { "name": name, "decorator": decorator_name, } - line = "{name} = {decorator}({name})".format(**values) + line = "{name} = {decorator}:Connect({name})".format(**values) self.emit(line) - def visit_Await(self, node): """Visit await""" self.emit("coroutine.await({})".format(self.visit_all(node.value, inline=True))) From de785dc510e72329d8c3ebdcb8ce23c40b5634d2 Mon Sep 17 00:00:00 2001 From: aqzp <72946059+AsynchronousAI@users.noreply.github.com> Date: Thu, 5 Oct 2023 22:28:09 -0500 Subject: [PATCH 60/77] Update rbxpy.py --- src/rbxpy.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index 71dc2ad..59d618c 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -51,47 +51,58 @@ class BinaryOperationDesc: }, ast.Sub: { "value": "-", - "format": _DEFAULT_BIN_FORMAT, + "format": _DEFAULT_BIN_FORMAT, + "depend": "", }, ast.Mult: { "value": "*", "format": _DEFAULT_BIN_FORMAT, + "depend": "", }, ast.Div: { "value": "/", "format": _DEFAULT_BIN_FORMAT, + "depend": "", }, ast.Mod: { "value": "%", "format": _DEFAULT_BIN_FORMAT, + "depend": "", }, ast.Pow: { "value": "^", "format": _DEFAULT_BIN_FORMAT, + "depend": "", }, ast.FloorDiv: { "value": "/", "format": "math.floor({left} {operation} {right})", + "depend": "", }, ast.LShift: { "value": "", "format": "bit32.lshift({left}, {right})", + "depend": "", }, ast.RShift: { "value": "", "format": "bit32.rshift({left}, {right})", + "depend": "", }, ast.BitOr: { "value": "", "format": "bit32.bor({left}, {right})", + "depend": "", }, ast.BitAnd: { "value": "", "format": "bit32.band({left}, {right})", + "depend": "", }, ast.BitXor: { "value": "", "format": "bit32.bxor({left}, {right})", + "depend": "", }, } """Boolean operation description""" From 4553fa56a3a31ec9d8cf37244eb125efb2a1dc14 Mon Sep 17 00:00:00 2001 From: aqzp <72946059+AsynchronousAI@users.noreply.github.com> Date: Thu, 5 Oct 2023 22:29:11 -0500 Subject: [PATCH 61/77] Update rbxpy.py --- src/rbxpy.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index 59d618c..ef61e92 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -1259,7 +1259,8 @@ def emit(self, value): self.output.append(value) def depend(self, value): """Add dependency value to the object""" - dependencies.append(value) + if value != "": + dependencies.append(value) """Header""" HEADER = f"--// Generated by roblox-py v{VERSION} \\\\--\n" From 56dfd0bf7bebdd3bbaa9cc9ba28a0e5cfbde692b Mon Sep 17 00:00:00 2001 From: aqzp <72946059+AsynchronousAI@users.noreply.github.com> Date: Thu, 5 Oct 2023 22:29:59 -0500 Subject: [PATCH 62/77] Update rbxpy.py --- src/rbxpy.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index ef61e92..acb3eee 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -1259,8 +1259,7 @@ def emit(self, value): self.output.append(value) def depend(self, value): """Add dependency value to the object""" - if value != "": - dependencies.append(value) + if value != "": dependencies.append(value) """Header""" HEADER = f"--// Generated by roblox-py v{VERSION} \\\\--\n" From f2cb187b4aa83819e92f8fded373b70a53621f34 Mon Sep 17 00:00:00 2001 From: aqzp <72946059+AsynchronousAI@users.noreply.github.com> Date: Thu, 5 Oct 2023 22:31:13 -0500 Subject: [PATCH 63/77] Update rbxpy.py --- src/rbxpy.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index acb3eee..86e72de 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -1259,7 +1259,8 @@ def emit(self, value): self.output.append(value) def depend(self, value): """Add dependency value to the object""" - if value != "": dependencies.append(value) + if value != "": + dependencies.append(value) """Header""" HEADER = f"--// Generated by roblox-py v{VERSION} \\\\--\n" From 397ccd1dbc23e5f864eb1f9b5c2071d52298d150 Mon Sep 17 00:00:00 2001 From: aqzp <72946059+AsynchronousAI@users.noreply.github.com> Date: Thu, 5 Oct 2023 22:32:12 -0500 Subject: [PATCH 64/77] Update rbxpy.py --- src/rbxpy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index 86e72de..c385b30 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -1260,7 +1260,7 @@ def emit(self, value): def depend(self, value): """Add dependency value to the object""" if value != "": - dependencies.append(value) + dependencies.append(value) """Header""" HEADER = f"--// Generated by roblox-py v{VERSION} \\\\--\n" From 310924f516b87d2b32f3217b39a91346af1ade14 Mon Sep 17 00:00:00 2001 From: aqzp <72946059+AsynchronousAI@users.noreply.github.com> Date: Thu, 5 Oct 2023 22:34:56 -0500 Subject: [PATCH 65/77] Update rbxpy.py --- src/rbxpy.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index c385b30..156a225 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -1258,9 +1258,8 @@ def emit(self, value): """Add translated value to the output""" self.output.append(value) def depend(self, value): - """Add dependency value to the object""" - if value != "": - dependencies.append(value) + if value != "": + dependencies.append(value) """Header""" HEADER = f"--// Generated by roblox-py v{VERSION} \\\\--\n" From 11b089e9d8bc518285b87725e9775af3766b3364 Mon Sep 17 00:00:00 2001 From: aqzp <72946059+AsynchronousAI@users.noreply.github.com> Date: Thu, 5 Oct 2023 22:39:19 -0500 Subject: [PATCH 66/77] Update rbxpy.py --- src/rbxpy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index 156a225..126270f 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -809,7 +809,7 @@ def visit_Global(self, node): def visit_AsyncFunctionDef(self, node): """Visit async function definition""" - line = "{local}function {name}({arguments}) coroutine.wrap(function()" + line = "{local}function {name}({arguments}) task.spawn(function()" last_ctx = self.context.last() @@ -870,7 +870,7 @@ def visit_AsyncFunctionDef(self, node): arg_index -= 1 - self.emit("end") + self.emit("end) end") for decorator in reversed(node.decorator_list): decorator_name = self.visit_all(decorator, inline=True) From 6c812241703490b7026e7ebc75408019734420f8 Mon Sep 17 00:00:00 2001 From: aqzp <72946059+AsynchronousAI@users.noreply.github.com> Date: Thu, 5 Oct 2023 22:39:56 -0500 Subject: [PATCH 67/77] Update rbxpy.py --- src/rbxpy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index 126270f..4292724 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -361,7 +361,7 @@ def visit_MatchOr(self, node): def visit_AsyncWith(self, node): """Visit async with""" """Visit with""" - self.emit("coroutine.wrap(function()") + self.emit("task.spawn(function()") self.visit_all(node.body) body = self.output[-1] lines = [] @@ -375,7 +375,7 @@ def visit_AsyncWith(self, node): lines.append(line) for line in lines: body.insert(0, line) - self.emit("end)()") + self.emit("end)") def visit_Slice(self, node): """Visit slice""" From 485896a9cf2ad852ed3263f5bf850eb01d0cc379 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Fri, 6 Oct 2023 17:06:10 -0500 Subject: [PATCH 68/77] Better try, except --- src/rbxpy.py | 50 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index 4292724..8b09701 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -271,6 +271,10 @@ def __init__(self, context=None, config=None): self.last_end_mode = TokenEndMode.LINE_FEED self.output = [] + def visit_YieldFrom(self, node): + """Visit yield from""" + self.emit("for _, v in {} do coroutine.yield(v) end".format(self.visit_all(node.value, inline=True))) + def visit_Assign(self, node): """Visit assign""" target = self.visit_all(node.targets[0], inline=True) @@ -652,7 +656,7 @@ def visit_Dict(self, node): self.depend("dict") self.emit("dict {{{}}}".format(elements)) self.depend("dict") - + def visit_DictComp(self, node): """Visit dictionary comprehension""" self.emit("(function()") @@ -1023,6 +1027,38 @@ def visit_List(self, node): self.depend("list") self.emit(line) + def visit_GeneratorExp(self, node): + """Visit generator expression""" + self.emit("(function()") + self.emit("local result = list {}") + self.depend("list") + + ends_count = 0 + + for comp in node.generators: + line = "for {target} in {iterator} do" + values = { + "target": self.visit_all(comp.target, inline=True), + "iterator": self.visit_all(comp.iter, inline=True), + } + line = line.format(**values) + self.emit(line) + ends_count += 1 + + for if_ in comp.ifs: + line = "if {} then".format(self.visit_all(if_, inline=True)) + self.emit(line) + ends_count += 1 + + line = "result.append({})" + line = line.format(self.visit_all(node.elt, inline=True)) + self.emit(line) + + self.emit(" ".join(["end"] * ends_count)) + + self.emit("return result") + self.emit("end)()") + def visit_ListComp(self, node): """Visit list comprehension""" self.emit("(function()") @@ -1153,11 +1189,19 @@ def visit_Try(self, node): def visit_ExceptHandler(self, node): """Visit exception handler""" - self.emit("if err:find('{}') then".format(node.type.id)) + if (node.type) != None: + if node.type.id == "Exception": + self.emit("local {} = err\n".format(node.name)) # The \n does messup the token generator, but makes cleaner code + self.emit("if err then") + else: + self.emit("if err:find('{}') then".format(node.type.id)) self.visit_all(node.body) - self.emit("end") + if (node.type) != None: + self.emit("end") + + def visit_While(self, node): """Visit while""" From ee6b8749053006f0d4a1c77b6e580d5cf269015d Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Fri, 6 Oct 2023 18:46:27 -0500 Subject: [PATCH 69/77] Exports --- src/rbxpy.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index 8b09701..024d8af 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -260,6 +260,7 @@ def get_next(): """Node visitor""" dependencies = [] +exports = [] class NodeVisitor(ast.NodeVisitor): LUACODE = "luau" @@ -295,6 +296,8 @@ def visit_Assign(self, node): self.emit("{local}{target} = {value}".format(local=local_keyword, target=target, value=value)) + + exports.append(target) ### MATCHES ### def visit_Match(self, node): @@ -784,6 +787,8 @@ def visit_FunctionDef(self, node): } line = "{name} = {decorator}:Connect({name})".format(**values) self.emit(line) + + exports.append(name) def visit_For(self, node): """Visit for loop""" @@ -886,6 +891,8 @@ def visit_AsyncFunctionDef(self, node): } line = "{name} = {decorator}:Connect({name})".format(**values) self.emit(line) + + exports.append(name) def visit_Await(self, node): """Visit await""" self.emit("coroutine.await({})".format(self.visit_all(node.value, inline=True))) @@ -1345,9 +1352,17 @@ def translate(self, pycode, fn, isAPI = False): global dependencies dependencies = list(set(dependencies)) + global exports + exports = list(set(exports)) + if fn: dependencies.append("fn") - + + FOOTER = "\n\n--// EXPORTS \\\\--\n" + FOOTER += "if not script:IsA(\"BaseScript\") then\n\treturn {\n" + for export in exports: + FOOTER += f"\t\t[\"{export}\"] = {export},\n" + FOOTER += "\t}\nend" for depend in dependencies: # set @@ -2032,7 +2047,7 @@ def translate(self, pycode, fn, isAPI = False): DEPEND += "\n\n----- CODE START -----\n" - return HEADER + DEPEND + self.to_code() + return HEADER + DEPEND + self.to_code() + FOOTER def to_code(self, code=None, indent=0): """Create a lua code from the compiler output""" From 1c406977e8e67139a41ec6c43c66549b0ace2964 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Fri, 6 Oct 2023 22:00:42 -0500 Subject: [PATCH 70/77] Fixed exports --- src/rbxpy.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index 024d8af..ecf6f44 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -1325,7 +1325,7 @@ def __init__(self, config=None, show_ast=False): self.output = [] - def translate(self, pycode, fn, isAPI = False): + def translate(self, pycode, fn, isAPI = False, export = True): """Translate python code to lua code""" if isAPI: py_ast_tree = ast.parse(pycode) @@ -1357,12 +1357,15 @@ def translate(self, pycode, fn, isAPI = False): if fn: dependencies.append("fn") - - FOOTER = "\n\n--// EXPORTS \\\\--\n" - FOOTER += "if not script:IsA(\"BaseScript\") then\n\treturn {\n" - for export in exports: - FOOTER += f"\t\t[\"{export}\"] = {export},\n" - FOOTER += "\t}\nend" + if export and exports != []: + FOOTER = "\n\n--// EXPORTS \\\\--\n" + FOOTER += "if not script:IsA(\"BaseScript\") then\n\treturn {\n" + for export in exports: + FOOTER += f"\t\t[\"{export}\"] = {export},\n" + FOOTER += "\t}\nend" + else: + FOOTER = "" + for depend in dependencies: # set From 3211031ce68445dcc36e7bece8d32c2e4801faed Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Fri, 6 Oct 2023 22:04:43 -0500 Subject: [PATCH 71/77] -ne argument --- src/rbxpy.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index ecf6f44..eed799b 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -2123,6 +2123,7 @@ def main(): out = "NONE" type = 1 # 1: py->lua, 2: lua->py includeSTD = False + export = True skip = False for arg in args: @@ -2141,6 +2142,8 @@ def main(): includeSTD = True elif arg == "-fn": includeSTD = False + elif arg == "-ne": + export = False elif arg == "-ast": ast = True elif arg == "-py": @@ -2171,7 +2174,7 @@ def main(): translator = Translator(Config(".pyluaconf.yaml"), show_ast=ast) - lua_code = translator.translate(content, includeSTD) + lua_code = translator.translate(content, includeSTD, False, export) if not ast: if out != "NONE": From 500c2bcd5fa6736b193ebfb54ef3c47e0d196267 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sat, 7 Oct 2023 10:08:33 -0500 Subject: [PATCH 72/77] use global to export --- src/rbxpy.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index eed799b..cff2e79 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -297,7 +297,7 @@ def visit_Assign(self, node): target=target, value=value)) - exports.append(target) + #exports.append(target) ### MATCHES ### def visit_Match(self, node): @@ -788,7 +788,7 @@ def visit_FunctionDef(self, node): line = "{name} = {decorator}:Connect({name})".format(**values) self.emit(line) - exports.append(name) + #exports.append(name) def visit_For(self, node): """Visit for loop""" @@ -815,6 +815,7 @@ def visit_Global(self, node): last_ctx = self.context.last() for name in node.names: last_ctx["globals"].add_symbol(name) + exports.append(name) def visit_AsyncFunctionDef(self, node): """Visit async function definition""" @@ -892,7 +893,7 @@ def visit_AsyncFunctionDef(self, node): line = "{name} = {decorator}:Connect({name})".format(**values) self.emit(line) - exports.append(name) + #exports.append(name) def visit_Await(self, node): """Visit await""" self.emit("coroutine.await({})".format(self.visit_all(node.value, inline=True))) From b3c9344b3916ad4226d92a8a23ef97fb7a1a78cf Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sat, 7 Oct 2023 14:49:42 -0500 Subject: [PATCH 73/77] fixes --- src/rbxpy.py | 90 +++++++++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index cff2e79..ce01468 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -1326,47 +1326,50 @@ def __init__(self, config=None, show_ast=False): self.output = [] - def translate(self, pycode, fn, isAPI = False, export = True): + def translate(self, pycode, fn, isAPI = False, export = True, reqfile = False): """Translate python code to lua code""" - if isAPI: - py_ast_tree = ast.parse(pycode) - else: - try: - # code that uses ast + if not reqfile: + if isAPI: py_ast_tree = ast.parse(pycode) - except SyntaxError as err: - sys.stderr.write("\033[1;31m" + "syntax error: " + "\033[0m" + str(err) + "\n") - sys.exit(1) - - visitor = NodeVisitor(config=self.config) + else: + try: + # code that uses ast + py_ast_tree = ast.parse(pycode) + except SyntaxError as err: + sys.stderr.write("\033[1;31m" + "syntax error: " + "\033[0m" + str(err) + "\n") + sys.exit(1) + + visitor = NodeVisitor(config=self.config) - if self.show_ast: - print(ast.dump(py_ast_tree)) + if self.show_ast: + print(ast.dump(py_ast_tree)) - visitor.visit(py_ast_tree) - - DEPEND = "\n\n--// REQUIREMENTS \\\\--\n" - - self.output = visitor.output - - # Remove duplicates from dependencies (list) - global dependencies - dependencies = list(set(dependencies)) - - global exports - exports = list(set(exports)) - - if fn: - dependencies.append("fn") - if export and exports != []: - FOOTER = "\n\n--// EXPORTS \\\\--\n" - FOOTER += "if not script:IsA(\"BaseScript\") then\n\treturn {\n" - for export in exports: - FOOTER += f"\t\t[\"{export}\"] = {export},\n" - FOOTER += "\t}\nend" - else: - FOOTER = "" + visitor.visit(py_ast_tree) + DEPEND = "\n\n--// REQUIREMENTS \\\\--\n" + + self.output = visitor.output + + # Remove duplicates from dependencies (list) + global dependencies + dependencies = list(set(dependencies)) + + global exports + exports = list(set(exports)) + + if fn: + dependencies.append("fn") + if export and exports != []: + FOOTER = "\n\n--// EXPORTS \\\\--\n" + FOOTER += "if not script:IsA(\"BaseScript\") then\n\treturn {\n" + for export in exports: + FOOTER += f"\t\t[\"{export}\"] = {export},\n" + FOOTER += "\t}\nend" + else: + FOOTER = "" + + if reqfile: + dependencies = ["class", "dict", "list", "in", "fn", "safeadd"] for depend in dependencies: # set @@ -2047,9 +2050,12 @@ def translate(self, pycode, fn, isAPI = False, export = True): else: error("Auto-generated dependency unhandled '{}', please report this issue on Discord or Github".format(depend)) - - DEPEND += "\n\n----- CODE START -----\n" - + + if not reqfile: + DEPEND += "\n\n----- CODE START -----\n" + else: + DEPEND += "return env" + return DEPEND return HEADER + DEPEND + self.to_code() + FOOTER @@ -2126,6 +2132,7 @@ def main(): includeSTD = False export = True skip = False + reqfile = None for arg in args: if skip: @@ -2141,6 +2148,8 @@ def main(): usage() elif arg == "-f": includeSTD = True + elif arg == "-r": + reqfile = True elif arg == "-fn": includeSTD = False elif arg == "-ne": @@ -2176,7 +2185,8 @@ def main(): translator = Translator(Config(".pyluaconf.yaml"), show_ast=ast) lua_code = translator.translate(content, includeSTD, False, export) - + if reqfile: + reqcode = translator.translate(content, True, False, False, True) if not ast: if out != "NONE": with open(out, "w") as file: From 848c3fff12442a53f74bb171f09fa5bb9e9bbcae Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sat, 7 Oct 2023 16:51:57 -0500 Subject: [PATCH 74/77] Method calls --- src/rbxpy.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index ce01468..63a84b3 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -466,9 +466,12 @@ def visit_AnnAssign(self, node): target=target, type=type)) else: - self.emit("{local}{target} = nil".format(local=local_keyword, - target=target, - type=type)) + if node.annotation.__class__.__name__ == "Call": + self.visit_Call(node.annotation, target) + else: + self.emit("{local}{target} = nil".format(local=local_keyword, + target=target, + type=type)) # example input: # a: int = 1 # example output: @@ -545,11 +548,13 @@ def visit_Break(self, node): """Visit break""" self.emit("break") - def visit_Call(self, node): + def visit_Call(self, node, method = None): """Visit function call""" line = "{name}({arguments})" name = self.visit_all(node.func, inline=True) + if method: + name = method + ":" + name arguments = [self.visit_all(arg, inline=True) for arg in node.args] self.emit(line.format(name=name, arguments=", ".join(arguments))) From 3007d698a0540f18802688bb219281cc99063b33 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sat, 7 Oct 2023 17:58:08 -0500 Subject: [PATCH 75/77] -s & -r command line args --- src/rbxpy.py | 1261 +++++++++++++++++++++++++------------------------- 1 file changed, 636 insertions(+), 625 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index 63a84b3..0b5ed74 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -2,6 +2,7 @@ import sys, ast, yaml, re, os, subprocess from pprint import pprint from pathlib import Path +from enum import Enum #### CONSTANTS #### VERSION = "3.0.0" @@ -183,9 +184,8 @@ class UnaryOperationDesc: "format": "bit32.bnot({value})", }, } -"""Token end mode""" -from enum import Enum +"""Token end mode""" class TokenEndMode(Enum): """This enum represents token end mode""" @@ -1331,8 +1331,10 @@ def __init__(self, config=None, show_ast=False): self.output = [] - def translate(self, pycode, fn, isAPI = False, export = True, reqfile = False): + def translate(self, pycode, fn, isAPI = False, export = True, reqfile = False, useRequire = False): """Translate python code to lua code""" + DEPEND = "\n\n--// REQUIREMENTS \\\\--\n" + if not reqfile: if isAPI: py_ast_tree = ast.parse(pycode) @@ -1351,8 +1353,6 @@ def translate(self, pycode, fn, isAPI = False, export = True, reqfile = False): visitor.visit(py_ast_tree) - DEPEND = "\n\n--// REQUIREMENTS \\\\--\n" - self.output = visitor.output # Remove duplicates from dependencies (list) @@ -1375,694 +1375,694 @@ def translate(self, pycode, fn, isAPI = False, export = True, reqfile = False): if reqfile: dependencies = ["class", "dict", "list", "in", "fn", "safeadd"] - for depend in dependencies: - # set - - if depend == "list": - DEPEND += """\n\nfunction list(t) - local result = {} - - result._is_list = true - - result._data = {} - for _, v in ipairs(t) do - table.insert(result._data, v) - end - - local methods = {} - - methods.append = function(value) - table.insert(result._data, value) - end - - methods.extend = function(iterable) - for value in iterable do - table.insert(result._data, value) - end - end - - methods.insert = function(index, value) - table.insert(result._data, index, value) - end - - methods.remove = function(value) - for i, v in ipairs(result._data) do - if value == v then - table.remove(result._data, i) - break - end - end - end - - methods.pop = function(index) - index = index or #result._data - local value = result._data[index] - table.remove(result._data, index) - return value - end - - methods.clear = function() - result._data = {} - end - - methods.index = function(value, start, end_) - start = start or 1 - end_ = end_ or #result._data - - for i = start, end_, 1 do - if result._data[i] == value then - return i - end - end - - return nil - end - - methods.count = function(value) - local cnt = 0 - for _, v in ipairs(result._data) do - if v == value then - cnt = cnt + 1 - end - end - - return cnt - end - - methods.sort = function(key, reverse) - key = key or nil - reverse = reverse or false - - table.sort(result._data, function(a, b) - if reverse then - return a < b - end - - return a > b - end) - end - - methods.reverse = function() - local new_data = {} - for i = #result._data, 1, -1 do - table.insert(new_data, result._data[i]) - end - - result._data = new_data - end - - methods.copy = function() - return list(result._data) - end - - local iterator_index = nil - - setmetatable(result, { - __index = function(self, index) - if typeof(index) == "number" then - if index < 0 then - index = #result._data + index - end - return rawget(result._data, index + 1) - end - return methods[index] - end, - __newindex = function(self, index, value) - result._data[index] = value - end, - __call = function(self, _, idx) - if idx == nil and iterator_index ~= nil then - iterator_index = nil - end - - local v = nil - iterator_index, v = next(result._data, iterator_index) - - return v - end, - }) - - return result -end""" - elif depend == "dict": - DEPEND += """\n\nfunction dict(t) - local result = {} - - result._is_dict = true - - result._data = {} - for k, v in pairs(t) do - result._data[k] = v - end - - local methods = {} - - local key_index = nil - - methods.clear = function() - result._data = {} - end - - methods.copy = function() - return dict(result._data) - end - - methods.get = function(key, default) - default = default or nil - if result._data[key] == nil then - return default - end - - return result._data[key] - end - - methods.items = function() - return pairs(result._data) - end - - methods.keys = function() - return function(self, idx, _) - if idx == nil and key_index ~= nil then - key_index = nil - end - - key_index, _ = next(result._data, key_index) - return key_index - end - end - - methods.pop = function(key, default) - default = default or nil - if result._data[key] ~= nil then - local value = result._data[key] - result._data[key] = nil - return key, value - end - - return key, default - end - - methods.popitem = function() - local key, value = next(result._data) - if key ~= nil then - result._data[key] = nil - end - - return key, value - end - - methods.setdefault = function(key, default) - if result._data[key] == nil then - result._data[key] = default - end - - return result._data[key] - end - - methods.update = function(t) - assert(t._is_dict) - - for k, v in t.items() do - result._data[k] = v - end - end - - methods.values = function() - return function(self, idx, _) - if idx == nil and key_index ~= nil then - key_index = nil - end - - key_index, value = next(result._data, key_index) - return value - end - end - - setmetatable(result, { - __index = function(self, index) - if typeof(index) == "string" then - -- If it starts with SLICE! then it is a slice, get the start, stop, and step values. Sometimes the 3rd value is not there, so we need to check for that - if string.sub(index, 1, 6) == "SLICE!" then - local start, stop, step = string.match(index, "SLICE!%((%d+), (%d+), (%d+)%)") - if (not stop) and (not step) and start then -- 1 value - start = string.match(index, "SLICE!%((%d+), (%d+)%)") - step = 1 - stop = -1 - elseif not step then -- 2 values - start, stop = string.match(index, "SLICE!%((%d+), (%d+)%)") - step = 1 - end - return slicefun(self, tonumber(start), tonumber(stop), tonumber(step)) - end - end - if result._data[index] ~= nil then - return result._data[index] - end - return methods[index] - end, - __newindex = function(self, index, value) - result._data[index] = value - end, - __call = function(self, _, idx) - if idx == nil and key_index ~= nil then - key_index = nil - end - - key_index, _ = next(result._data, key_index) - - return key_index - end, - }) - - return result -end""" - elif depend == "class": - DEPEND += """\n\nfunction class(class_init, bases) - bases = bases or {} - - local c = {} - - for _, base in ipairs(bases) do - for k, v in pairs(base) do - c[k] = v + if not useRequire: + for depend in dependencies: + # set + + if depend == "list": + DEPEND += """\n\nfunction list(t) + local result = {} + + result._is_list = true + + result._data = {} + for _, v in ipairs(t) do + table.insert(result._data, v) + end + + local methods = {} + + methods.append = function(value) + table.insert(result._data, value) + end + + methods.extend = function(iterable) + for value in iterable do + table.insert(result._data, value) + end + end + + methods.insert = function(index, value) + table.insert(result._data, index, value) + end + + methods.remove = function(value) + for i, v in ipairs(result._data) do + if value == v then + table.remove(result._data, i) + break + end + end + end + + methods.pop = function(index) + index = index or #result._data + local value = result._data[index] + table.remove(result._data, index) + return value end - end - c._bases = bases + methods.clear = function() + result._data = {} + end + + methods.index = function(value, start, end_) + start = start or 1 + end_ = end_ or #result._data - c = class_init(c) + for i = start, end_, 1 do + if result._data[i] == value then + return i + end + end - local mt = getmetatable(c) or {} - mt.__call = function(_, ...) - local object = {} + return nil + end + + methods.count = function(value) + local cnt = 0 + for _, v in ipairs(result._data) do + if v == value then + cnt = cnt + 1 + end + end + + return cnt + end + + methods.sort = function(key, reverse) + key = key or nil + reverse = reverse or false + + table.sort(result._data, function(a, b) + if reverse then + return a < b + end - setmetatable(object, { - __index = function(tbl, idx) - local method = c[idx] - if typeof(method) == "function" then - return function(...) - return c[idx](object, ...) + return a > b + end) + end + + methods.reverse = function() + local new_data = {} + for i = #result._data, 1, -1 do + table.insert(new_data, result._data[i]) + end + + result._data = new_data + end + + methods.copy = function() + return list(result._data) + end + + local iterator_index = nil + + setmetatable(result, { + __index = function(self, index) + if typeof(index) == "number" then + if index < 0 then + index = #result._data + index end + return rawget(result._data, index + 1) + end + return methods[index] + end, + __newindex = function(self, index, value) + result._data[index] = value + end, + __call = function(self, _, idx) + if idx == nil and iterator_index ~= nil then + iterator_index = nil end - return method + local v = nil + iterator_index, v = next(result._data, iterator_index) + + return v end, }) - if typeof(object.__init__) == "function" then - object.__init__(...) + return result + end""" + elif depend == "dict": + DEPEND += """\n\nfunction dict(t) + local result = {} + + result._is_dict = true + + result._data = {} + for k, v in pairs(t) do + result._data[k] = v end - return object - end + local methods = {} - setmetatable(c, mt) + local key_index = nil - return c -end""" - elif depend == "in": - DEPEND += """\n\nfunction op_in(item, items) - if type(items) == "table" then - for v in items do - if v == item then - return true + methods.clear = function() + result._data = {} + end + + methods.copy = function() + return dict(result._data) + end + + methods.get = function(key, default) + default = default or nil + if result._data[key] == nil then + return default end + + return result._data[key] end - elseif type(items) == "string" and type(item) == "string" then - return string.find(items, item, 1, true) ~= nil - end - return false -end""" - elif depend == "fn": - DEPEND += """\n\nif game then -__name__ = if script:IsA("BaseScript") then "__main__" else script.Name -else -__name__ = nil -end -range = function(s, e) -- range() - local tb = {} - local a = 0 - local b = 0 - if not e then a=1 else a=s end - if not e then b=s else b=e end - for i = a, b do - tb[#tb+1] = i - end - return tb -end -len = function(x) return #x end -- len() -abs = math.abs -- abs() -str = tostring -- str() -int = tonumber -- int() -sum = function(tbl) --sum() - local total = 0 - for _, v in ipairs(tbl) do - total = total + v - end - return total -end -max = function(tbl) --max() - local maxValue = -math.huge - for _, v in ipairs(tbl) do - if v > maxValue then - maxValue = v + methods.items = function() + return pairs(result._data) + end + + methods.keys = function() + return function(self, idx, _) + if idx == nil and key_index ~= nil then + key_index = nil + end + + key_index, _ = next(result._data, key_index) + return key_index + end + end + + methods.pop = function(key, default) + default = default or nil + if result._data[key] ~= nil then + local value = result._data[key] + result._data[key] = nil + return key, value + end + + return key, default + end + + methods.popitem = function() + local key, value = next(result._data) + if key ~= nil then + result._data[key] = nil + end + + return key, value + end + + methods.setdefault = function(key, default) + if result._data[key] == nil then + result._data[key] = default + end + + return result._data[key] + end + + methods.update = function(t) + assert(t._is_dict) + + for k, v in t.items() do + result._data[k] = v + end + end + + methods.values = function() + return function(self, idx, _) + if idx == nil and key_index ~= nil then + key_index = nil + end + + local key_index, value = next(result._data, key_index) + return value + end + end + + setmetatable(result, { + __index = function(self, index) + if result._data[index] ~= nil then + return result._data[index] + end + return methods[index] + end, + __newindex = function(self, index, value) + result._data[index] = value + end, + __call = function(self, _, idx) + if idx == nil and key_index ~= nil then + key_index = nil + end + + key_index, _ = next(result._data, key_index) + + return key_index + end, + }) + + return result + end""" + elif depend == "class": + DEPEND += """\n\nfunction class(class_init, bases) + bases = bases or {} + + local c = {} + + for _, base in ipairs(bases) do + for k, v in pairs(base) do + c[k] = v + end end + + c._bases = bases + + c = class_init(c) + + local mt = getmetatable(c) or {} + mt.__call = function(_, ...) + local object = {} + + setmetatable(object, { + __index = function(tbl, idx) + local method = c[idx] + if typeof(method) == "function" then + return function(...) + return c[idx](object, ...) + end + end + + return method + end, + }) + + if typeof(object.__init__) == "function" then + object.__init__(...) + end + + return object + end + + setmetatable(c, mt) + + return c + end""" + elif depend == "in": + DEPEND += """\n\nfunction op_in(item, items) + if type(items) == "table" then + for v in items do + if v == item then + return true + end + end + elseif type(items) == "string" and type(item) == "string" then + return string.find(items, item, 1, true) ~= nil + end + + return false + end""" + elif depend == "fn": + DEPEND += """\n\nif game then + __name__ = if script:IsA("BaseScript") then "__main__" else script.Name + else + __name__ = nil end - return maxValue -end -min = function(tbl) --min() - local minValue = math.huge - for _, v in ipairs(tbl) do - if v < minValue then - minValue = v + range = function(s, e) -- range() + local tb = {} + local a = 0 + local b = 0 + if not e then a=1 else a=s end + if not e then b=s else b=e end + for i = a, b do + tb[#tb+1] = i end + return tb end - return minValue -end -reversed = function(seq) -- reversed() - local reversedSeq = {} - local length = #seq - for i = length, 1, -1 do - reversedSeq[length - i + 1] = seq[i] + len = function(x) return #x end -- len() + abs = math.abs -- abs() + str = tostring -- str() + int = tonumber -- int() + sum = function(tbl) --sum() + local total = 0 + for _, v in ipairs(tbl) do + total = total + v + end + return total end - return reversedSeq -end -split = function(str, sep) -- split - local substrings = {} - local pattern = string.format("([^%s]+)",sep or "%s") - for substring in string.gmatch(str, pattern) do - table.insert(substrings, substring) + max = function(tbl) --max() + local maxValue = -math.huge + for _, v in ipairs(tbl) do + if v > maxValue then + maxValue = v + end + end + return maxValue end - return substrings -end -round = math.round -- round() -all = function (iter) -- all() - for i, v in iter do if not v then return false end end - - return true -end -any = function (iter) -- any() - for i, v in iter do - if v then return true end + min = function(tbl) --min() + local minValue = math.huge + for _, v in ipairs(tbl) do + if v < minValue then + minValue = v + end + end + return minValue end - return false -end -ord = string.byte -- ord -chr = string.char -- chr -callable = function(fun) -- callable() - if rawget(fun) ~= fun then warn("At the momement Roblox.py's function callable() does not fully support metatables.") end - return typeof(rawget(fun)) == "function" -end -float = tonumber -- float() -super = function() - error("roblox-pyc does not has a Lua implementation of the function `super`. Use `self` instead") -end -format = function(format, ...) -- format - local args = {...} - local num_args = select("#", ...) - - local formatted_string = string.gsub(format, "{(%d+)}", function(index) - index = tonumber(index) - if index >= 1 and index <= num_args then - return tostring(args[index]) - else - return "{" .. index .. "}" + reversed = function(seq) -- reversed() + local reversedSeq = {} + local length = #seq + for i = length, 1, -1 do + reversedSeq[length - i + 1] = seq[i] end - end) - - return formatted_string -end -hex = function (value) -- hex - return string.format("%x", value) -end -id = function (obj) -- id - return print(tostring({obj}):gsub("table: ", ""):split(" ")[1]) -end -map = function (func, ...) --map - local args = {...} - local result = {} - local num_args = select("#", ...) - - local shortest_length = math.huge - for i = 1, num_args do - local arg = args[i] - local arg_length = #arg - if arg_length < shortest_length then - shortest_length = arg_length + return reversedSeq + end + split = function(str, sep) -- split + local substrings = {} + local pattern = string.format("([^%s]+)",sep or "%s") + for substring in string.gmatch(str, pattern) do + table.insert(substrings, substring) end + return substrings end + round = math.round -- round() + all = function (iter) -- all() + for i, v in iter do if not v then return false end end - for i = 1, shortest_length do - local mapped_args = {} - for j = 1, num_args do - local arg = args[j] - table.insert(mapped_args, arg[i]) + return true + end + any = function (iter) -- any() + for i, v in iter do + if v then return true end end - table.insert(result, func(unpack(mapped_args))) + return false + end + ord = string.byte -- ord + chr = string.char -- chr + callable = function(fun) -- callable() + if rawget(fun) ~= fun then warn("At the momement Roblox.py's function callable() does not fully support metatables.") end + return typeof(rawget(fun)) == "function" end + float = tonumber -- float() + super = function() + error("roblox-pyc does not has a Lua implementation of the function `super`. Use `self` instead") + end + format = function(format, ...) -- format + local args = {...} + local num_args = select("#", ...) + + local formatted_string = string.gsub(format, "{(%d+)}", function(index) + index = tonumber(index) + if index >= 1 and index <= num_args then + return tostring(args[index]) + else + return "{" .. index .. "}" + end + end) - return result -end -bool = function(x) -- bool - if x == false or x == nil or x == 0 then - return false + return formatted_string end + hex = function (value) -- hex + return string.format("%x", value) + end + id = function (obj) -- id + return print(tostring({obj}):gsub("table: ", ""):split(" ")[1]) + end + map = function (func, ...) --map + local args = {...} + local result = {} + local num_args = select("#", ...) + + local shortest_length = math.huge + for i = 1, num_args do + local arg = args[i] + local arg_length = #arg + if arg_length < shortest_length then + shortest_length = arg_length + end + end - if typeof(x) == "table" then - if x._is_list or x._is_dict then - return next(x._data) ~= nil + for i = 1, shortest_length do + local mapped_args = {} + for j = 1, num_args do + local arg = args[j] + table.insert(mapped_args, arg[i]) + end + table.insert(result, func(unpack(mapped_args))) end + + return result end + bool = function(x) -- bool + if x == false or x == nil or x == 0 then + return false + end + + if typeof(x) == "table" then + if x._is_list or x._is_dict then + return next(x._data) ~= nil + end + end - return true -end -divmod = function(a, b) -- divmod - local res = { math.floor(a / b), math.fmod(a, b) } - return unpack(res) -end -slice = function (seq, start, stop, step) - local sliced = {} - local len = #seq - start = start or 1 - stop = stop or len - step = step or 1 - if start < 0 then - start = len + start + 1 + return true + end + divmod = function(a, b) -- divmod + local res = { math.floor(a / b), math.fmod(a, b) } + return unpack(res) end - if stop < 0 then - stop = len + stop + 1 + slice = function (seq, start, stop, step) + local sliced = {} + local len = #seq + start = start or 1 + stop = stop or len + step = step or 1 + if start < 0 then + start = len + start + 1 + end + if stop < 0 then + stop = len + stop + 1 + end + for i = start, stop - 1, step do + table.insert(sliced, seq[i]) + end + return sliced end - for i = start, stop - 1, step do - table.insert(sliced, seq[i]) + anext = function (iterator) -- anext + local status, value = pcall(iterator) + if status then + return value + end end - return sliced -end -anext = function (iterator) -- anext - local status, value = pcall(iterator) - if status then + ascii = function (obj) -- ascii + return string.format("%q", tostring(obj)) + end + dir = function (obj) -- dir + local result = {} + for key, _ in pairs(obj) do + table.insert(result, key) + end + return result + end + getattr = function (obj, name, default) -- getattr + local value = obj[name] + if value == nil then + return default + end return value end -end -ascii = function (obj) -- ascii - return string.format("%q", tostring(obj)) -end -dir = function (obj) -- dir - local result = {} - for key, _ in pairs(obj) do - table.insert(result, key) + globals = function () -- globals + return _G end - return result -end -getattr = function (obj, name, default) -- getattr - local value = obj[name] - if value == nil then - return default + hasattr = function (obj, name) --hasattr + return obj[name] ~= nil end - return value -end -globals = function () -- globals - return _G -end -hasattr = function (obj, name) --hasattr - return obj[name] ~= nil -end -isinstance = function (obj, class) -- isinstance - return type(obj) == class -end -issubclass = function (cls, classinfo) -- issubclass - local mt = getmetatable(cls) - while mt do - if mt.__index == classinfo then - return true + isinstance = function (obj, class) -- isinstance + return type(obj) == class + end + issubclass = function (cls, classinfo) -- issubclass + local mt = getmetatable(cls) + while mt do + if mt.__index == classinfo then + return true + end + mt = getmetatable(mt.__index) end - mt = getmetatable(mt.__index) + return false end - return false -end -iter = function (obj) -- iter - if type(obj) == "table" and obj.__iter__ ~= nil then - return obj.__iter__ + iter = function (obj) -- iter + if type(obj) == "table" and obj.__iter__ ~= nil then + return obj.__iter__ + end + return nil end - return nil -end -locals = function () -- locals - return _G -end -oct = function (num) --oct - return string.format("%o", num) -end -pow = function (base, exponent, modulo) --pow - if modulo ~= nil then - return math.pow(base, exponent) % modulo - else - return base ^ exponent + locals = function () -- locals + return _G end -end -eval = function (expr, env) - return loadstring(expr)() -end -exec = loadstring -filter = function (predicate, iterable) - local result = {} - for _, value in ipairs(iterable) do - if predicate(value) then - table.insert(result, value) + oct = function (num) --oct + return string.format("%o", num) + end + pow = function (base, exponent, modulo) --pow + if modulo ~= nil then + return math.pow(base, exponent) % modulo + else + return base ^ exponent end end - return result -end -frozenset = function (...) - local elements = {...} - local frozenSet = {} - for _, element in ipairs(elements) do - frozenSet[element] = true + eval = function (expr, env) + return loadstring(expr)() end - return frozenSet -end -aiter = function (iterable) -- aiter - return pairs(iterable) -end -bin = function(num: number) - local bits = {} - repeat - table.insert(bits, 1, num % 2) - num = math.floor(num / 2) - until num == 0 - return "0b" .. table.concat(bits) -end -complex = function (real, imag) -- complex - return { real = real, imag = imag } -end -deltaattr = function (object, attribute) -- delattr - object[attribute] = nil -end -enumerate = function (iterable) -- enumerate - local i = 0 - return function() - i = i + 1 - local value = iterable[i] - if value ~= nil then - return i, value + exec = loadstring + filter = function (predicate, iterable) + local result = {} + for _, value in ipairs(iterable) do + if predicate(value) then + table.insert(result, value) + end end + return result end -end -bytearray = function (arg) -- bytearray - if type(arg) == "string" then - local bytes = {} - for i = 1, #arg do - table.insert(bytes, string.byte(arg, i)) + frozenset = function (...) + local elements = {...} + local frozenSet = {} + for _, element in ipairs(elements) do + frozenSet[element] = true end - return bytes - elseif type(arg) == "number" then - local bytes = {} - while arg > 0 do - table.insert(bytes, 1, arg % 256) - arg = math.floor(arg / 256) + return frozenSet + end + aiter = function (iterable) -- aiter + return pairs(iterable) + end + bin = function(num: number) + local bits = {} + repeat + table.insert(bits, 1, num % 2) + num = math.floor(num / 2) + until num == 0 + return "0b" .. table.concat(bits) + end + complex = function (real, imag) -- complex + return { real = real, imag = imag } + end + deltaattr = function (object, attribute) -- delattr + object[attribute] = nil + end + enumerate = function (iterable) -- enumerate + local i = 0 + return function() + i = i + 1 + local value = iterable[i] + if value ~= nil then + return i, value + end end - return bytes - elseif type(arg) == "table" then - return arg -- Assuming it's already a bytearray table - else - error("Invalid argument type for bytearray()") end -end -bytes = function (arg) -- bytes - if type(arg) == "string" then - local bytes = {} - for i = 1, #arg do - table.insert(bytes, string.byte(arg, i)) + bytearray = function (arg) -- bytearray + if type(arg) == "string" then + local bytes = {} + for i = 1, #arg do + table.insert(bytes, string.byte(arg, i)) + end + return bytes + elseif type(arg) == "number" then + local bytes = {} + while arg > 0 do + table.insert(bytes, 1, arg % 256) + arg = math.floor(arg / 256) + end + return bytes + elseif type(arg) == "table" then + return arg -- Assuming it's already a bytearray table + else + error("Invalid argument type for bytearray()") end - return bytes - elseif type(arg) == "table" then - return arg -- Assuming it's already a bytes table - else - error("Invalid argument type for bytes()") end -end -compile = loadstring -help = function (object) -- help - print("Help for object:", object) - print("Type:", type(object)) - print("Learn more in the official roblox documentation!") -end -memoryview = function (object) -- memoryview - if type(object) == "table" then - local buffer = table.concat(object) - return { buffer = buffer, itemsize = 1 } - else - error("Invalid argument type for memoryview()") + bytes = function (arg) -- bytes + if type(arg) == "string" then + local bytes = {} + for i = 1, #arg do + table.insert(bytes, string.byte(arg, i)) + end + return bytes + elseif type(arg) == "table" then + return arg -- Assuming it's already a bytes table + else + error("Invalid argument type for bytes()") + end end -end -repr = function (object) -- repr - return tostring(object) -end -sorted = function (iterable, cmp, key, reverse) -- sorted - local sortedTable = {} - for key, value in pairs(iterable) do - table.insert(sortedTable, { key = key, value = value }) + compile = loadstring + help = function (object) -- help + print("Help for object:", object) + print("Type:", type(object)) + print("Learn more in the official roblox documentation!") end - table.sort(sortedTable, function(a, b) - -- Compare logic based on cmp, key, reverse parameters - return a.key < b.key - end) - local i = 0 - return function() - i = i + 1 - local entry = sortedTable[i] - if entry then - return entry.key, entry.value + memoryview = function (object) -- memoryview + if type(object) == "table" then + local buffer = table.concat(object) + return { buffer = buffer, itemsize = 1 } + else + error("Invalid argument type for memoryview()") end end -end -vars = function (object) -- vars - local attributes = {} - for key, value in pairs(object) do - attributes[key] = value + repr = function (object) -- repr + return tostring(object) end - return attributes -end""" - elif depend == "safeadd": - DEPEND += """\n\nfunction safeadd(a, b) - if type(a) == "number" and type(b) == "number" then - return a + b - elseif type(a) == "string" and type(b) == "string" then - return a .. b - else - error(`Attempt to add a {type(a)} and a {type(b)}`) + sorted = function (iterable, cmp, key, reverse) -- sorted + local sortedTable = {} + for key, value in pairs(iterable) do + table.insert(sortedTable, { key = key, value = value }) + end + table.sort(sortedTable, function(a, b) + -- Compare logic based on cmp, key, reverse parameters + return a.key < b.key + end) + local i = 0 + return function() + i = i + 1 + local entry = sortedTable[i] + if entry then + return entry.key, entry.value + end + end end -end""" - else: - error("Auto-generated dependency unhandled '{}', please report this issue on Discord or Github".format(depend)) - + vars = function (object) -- vars + local attributes = {} + for key, value in pairs(object) do + attributes[key] = value + end + return attributes + end""" + elif depend == "safeadd": + DEPEND += """\n\nfunction safeadd(a, b) + if type(a) == "number" and type(b) == "number" then + return a + b + elseif type(a) == "string" and type(b) == "string" then + return a .. b + else + error(`Attempt to add a {type(a)} and a {type(b)}`) + end + end""" + else: + error("Auto-generated dependency unhandled '{}', please report this issue on Discord or Github".format(depend)) if not reqfile: DEPEND += "\n\n----- CODE START -----\n" else: - DEPEND += "return env" + DEPEND += "\n\nreturn env" return DEPEND + + CODE = self.to_code() + libs = [] + + if useRequire: + DEPEND = """\n\n--// Imports \\\\-- +py = _G.rbxpy or require(game.ReplicatedStorage.rbxpy) + +""" + for i in libs: + if i in CODE: + DEPEND += f"{i} = py.{i}" + + DEPEND += "\n\n----- CODE START -----\n" + - return HEADER + DEPEND + self.to_code() + FOOTER + return HEADER + DEPEND + CODE + FOOTER def to_code(self, code=None, indent=0): """Create a lua code from the compiler output""" @@ -2138,6 +2138,7 @@ def main(): export = True skip = False reqfile = None + useRequire = False for arg in args: if skip: @@ -2153,8 +2154,10 @@ def main(): usage() elif arg == "-f": includeSTD = True - elif arg == "-r": + elif arg == "-s": reqfile = True + elif arg == "-r": + useRequire = True elif arg == "-fn": includeSTD = False elif arg == "-ne": @@ -2189,9 +2192,17 @@ def main(): translator = Translator(Config(".pyluaconf.yaml"), show_ast=ast) - lua_code = translator.translate(content, includeSTD, False, export) if reqfile: reqcode = translator.translate(content, True, False, False, True) + if out != "NONE": + with open(out, "w") as file: + file.write(reqcode) + else: + print(reqcode) + sys.exit(0) + else: + lua_code = translator.translate(content, includeSTD, False, export, False, useRequire) + if not ast: if out != "NONE": with open(out, "w") as file: From 24d3d9a7c5b6566857d1ec4414e86314d9429e47 Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sat, 7 Oct 2023 18:07:05 -0500 Subject: [PATCH 76/77] Do not need inputfile when generating library files --- src/rbxpy.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index 0b5ed74..a68d177 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -2177,23 +2177,24 @@ def main(): input_filename = arg if type == 1: - if input_filename == "NONE": + if (input_filename == "NONE") and not reqfile: usage() - if not Path(input_filename).is_file(): + if (not Path(input_filename).is_file()) and not reqfile: error( "The given filename ('{}') is not a file.".format(input_filename)) - content = None - with open(input_filename, "r") as file: - content = file.read() + if not reqfile: + content = None + with open(input_filename, "r") as file: + content = file.read() - if not content: - error("The input file is empty.") + if not content: + error("The input file is empty.") translator = Translator(Config(".pyluaconf.yaml"), show_ast=ast) if reqfile: - reqcode = translator.translate(content, True, False, False, True) + reqcode = translator.translate("", True, False, False, True) if out != "NONE": with open(out, "w") as file: file.write(reqcode) From d3afe1ddd22cac44dc985b6ed8029b92a735f35e Mon Sep 17 00:00:00 2001 From: FIRST_NAME LAST_NAME Date: Sat, 7 Oct 2023 18:09:04 -0500 Subject: [PATCH 77/77] Fully support `require`-ing --- src/rbxpy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rbxpy.py b/src/rbxpy.py index a68d177..c54fe4d 100644 --- a/src/rbxpy.py +++ b/src/rbxpy.py @@ -2048,7 +2048,7 @@ def translate(self, pycode, fn, isAPI = False, export = True, reqfile = False, u return DEPEND CODE = self.to_code() - libs = [] + libs = ["class", "dict", "list", "op_in", "safeadd", "__name__", "range", "len", "abs", "str", "int", "sum", "max", "min", "reversed", "split", "round", "all", "any", "ord", "chr", "callable", "float", "super", "format", "hex", "id", "map", "bool", "divmod", "slice", "anext", "ascii", "dir", "getattr", "globals", "hasattr", "isinstance", "issubclass", "iter", "locals", "oct", "pow", "eval", "exec", "filter", "frozenset", "aiter", "bin", "complex", "deltaattr", "enumerate", "bytearray", "bytes", "compile", "help", "memoryview", "repr", "sorted", "vars"] if useRequire: DEPEND = """\n\n--// Imports \\\\--