From 812f5107c3e896275fc7c006dbdb5b9722c2e812 Mon Sep 17 00:00:00 2001 From: Yury Kurlykov Date: Wed, 15 Apr 2020 16:15:56 +1000 Subject: [PATCH] Initial commit --- .gitignore | 108 ++++ LICENSE | 21 + README.md | 32 + grammars/JavaScriptLexer.g4 | 711 +++++++++++++++++++++++ grammars/JavaScriptParser.g4 | 511 ++++++++++++++++ jasminesnake/__init__.py | 3 + jasminesnake/__main__.py | 57 ++ jasminesnake/lex/JavaScriptBaseLexer.py | 106 ++++ jasminesnake/lex/JavaScriptBaseParser.py | 108 ++++ requirements-dev.txt | 3 + requirements.txt | 2 + setup.py | 30 + tox.ini | 10 + 13 files changed, 1702 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 grammars/JavaScriptLexer.g4 create mode 100644 grammars/JavaScriptParser.g4 create mode 100644 jasminesnake/__init__.py create mode 100644 jasminesnake/__main__.py create mode 100644 jasminesnake/lex/JavaScriptBaseLexer.py create mode 100644 jasminesnake/lex/JavaScriptBaseParser.py create mode 100644 requirements-dev.txt create mode 100644 requirements.txt create mode 100644 setup.py create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6bb9794 --- /dev/null +++ b/.gitignore @@ -0,0 +1,108 @@ +.idea + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +venv \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..269bb2c --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Yury Kurlykov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..abc9b7a --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# Jasmine Snake +Another JavaScript interpreter written on Python 3. + +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) +[![SemVer: 2.0.0](https://img.shields.io/badge/SemVer-2.0.0-F8DE7E?labelColor=23261D)](https://semver.org/spec/v2.0.0.html) + +## Requirements + +- ANTLR 4 +- Colorama + +To run tests: + +- pylint +- Tox + +You can get ANTLR [here](https://www.antlr.org/), other dependencies could be installed with pip: +```bash +pip install -r requirements.txt # Use requirements-dev.txt if you want to run tests +``` + +## Running + +```bash +antlr4 -o jasminesnake/lex -package lex -Dlanguage=Python3 grammars/*.g4 +python -m jasminesnake +``` + +## Credits + +JavaScript grammar source: +[https://github.com/antlr/grammars-v4/tree/master/javascript/javascript](https://github.com/antlr/grammars-v4/tree/master/javascript/javascript) diff --git a/grammars/JavaScriptLexer.g4 b/grammars/JavaScriptLexer.g4 new file mode 100644 index 0000000..dfcd2b2 --- /dev/null +++ b/grammars/JavaScriptLexer.g4 @@ -0,0 +1,711 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 by Bart Kiers (original author) and Alexandre Vitorelli (contributor -> ported to CSharp) + * Copyright (c) 2017-2020 by Ivan Kochurkin (Positive Technologies): + added ECMAScript 6 support, cleared and transformed to the universal grammar. + * Copyright (c) 2018 by Juan Alvarez (contributor -> ported to Go) + * Copyright (c) 2019 by Student Main (contributor -> ES2020) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +lexer grammar JavaScriptLexer; + +channels { ERROR } + +options { superClass=JavaScriptBaseLexer; } + +HashBangLine: { self.isStartOfFile()}? '#!' ~[\r\n\u2028\u2029]*; // only allowed at start +MultiLineComment: '/*' .*? '*/' -> channel(HIDDEN); +SingleLineComment: '//' ~[\r\n\u2028\u2029]* -> channel(HIDDEN); +RegularExpressionLiteral: '/' RegularExpressionFirstChar RegularExpressionChar* {self.isRegexPossible()}? '/' IdentifierPart*; + +OpenBracket: '['; +CloseBracket: ']'; +OpenParen: '('; +CloseParen: ')'; +OpenBrace: '{' {self.processOpenBrace()}; +CloseBrace: '}' {self.processCloseBrace()}; +SemiColon: ';'; +Comma: ','; +Assign: '='; +QuestionMark: '?'; +Colon: ':'; +Ellipsis: '...'; +Dot: '.'; +PlusPlus: '++'; +MinusMinus: '--'; +Plus: '+'; +Minus: '-'; +BitNot: '~'; +Not: '!'; +Multiply: '*'; +Divide: '/'; +Modulus: '%'; +Power: '**'; +NullCoalesce: '??'; +Hashtag: '#'; +RightShiftArithmetic: '>>'; +LeftShiftArithmetic: '<<'; +RightShiftLogical: '>>>'; +LessThan: '<'; +MoreThan: '>'; +LessThanEquals: '<='; +GreaterThanEquals: '>='; +Equals_: '=='; +NotEquals: '!='; +IdentityEquals: '==='; +IdentityNotEquals: '!=='; +BitAnd: '&'; +BitXOr: '^'; +BitOr: '|'; +And: '&&'; +Or: '||'; +MultiplyAssign: '*='; +DivideAssign: '/='; +ModulusAssign: '%='; +PlusAssign: '+='; +MinusAssign: '-='; +LeftShiftArithmeticAssign: '<<='; +RightShiftArithmeticAssign: '>>='; +RightShiftLogicalAssign: '>>>='; +BitAndAssign: '&='; +BitXorAssign: '^='; +BitOrAssign: '|='; +PowerAssign: '**='; +ARROW: '=>'; + +/// Null Literals + +NullLiteral: 'null'; + +/// Boolean Literals + +BooleanLiteral: 'true' + | 'false'; + +/// Numeric Literals + +DecimalLiteral: DecimalIntegerLiteral '.' [0-9] [0-9_]* ExponentPart? + | '.' [0-9] [0-9_]* ExponentPart? + | DecimalIntegerLiteral ExponentPart? + ; + +/// Numeric Literals + +HexIntegerLiteral: '0' [xX] [0-9a-fA-F] HexDigit*; +OctalIntegerLiteral: '0' [0-7]+ {not self.isStrictMode()}?; +OctalIntegerLiteral2: '0' [oO] [0-7] [_0-7]*; +BinaryIntegerLiteral: '0' [bB] [01] [_01]*; + +BigHexIntegerLiteral: '0' [xX] [0-9a-fA-F] HexDigit* 'n'; +BigOctalIntegerLiteral: '0' [oO] [0-7] [_0-7]* 'n'; +BigBinaryIntegerLiteral: '0' [bB] [01] [_01]* 'n'; +BigDecimalIntegerLiteral: DecimalIntegerLiteral 'n'; + +/// Keywords + +Break: 'break'; +Do: 'do'; +Typeof: 'typeof'; +Else: 'else'; +New: 'new'; +Var: 'var'; +Return: 'return'; +Void: 'void'; +Continue: 'continue'; +For: 'for'; +While: 'while'; +Function: 'function'; +This: 'this'; +Default: 'default'; +If: 'if'; +Throw: 'throw'; +Delete: 'delete'; +In: 'in'; +As: 'as'; +From: 'from'; + +/// Unused words in this project + +//Instanceof: 'instanceof'; +//Case: 'case'; +//Catch: 'catch'; +//Finally: 'finally'; +//Switch: 'switch'; +//Debugger: 'debugger'; +//With: 'with'; +//Try: 'try'; + +/// Future Reserved Words + +Class: 'class'; +Enum: 'enum'; +Extends: 'extends'; +Super: 'super'; +Const: 'const'; +Export: 'export'; +Import: 'import'; + +//Async: 'async'; +//Await: 'await'; + +/// The following tokens are also considered to be FutureReservedWords +/// when parsing strict mode + +Implements: 'implements' {self.isStrictMode()}?; +StrictLet: 'let' {self.isStrictMode()}?; +NonStrictLet: 'let' {not self.isStrictMode()}?; +Private: 'private' {self.isStrictMode()}?; +Public: 'public' {self.isStrictMode()}?; +Interface: 'interface' {self.isStrictMode()}?; +Package: 'package' {self.isStrictMode()}?; +Protected: 'protected' {self.isStrictMode()}?; +Static: 'static' {self.isStrictMode()}?; +//Yield: 'yield' {self.isStrictMode()}?; + +/// Identifier Names and Identifiers + +Identifier: IdentifierStart IdentifierPart*; +/// String Literals +StringLiteral: ('"' DoubleStringCharacter* '"' + | '\'' SingleStringCharacter* '\'') {self.processStringLiteral()} + ; + +// TODO: `${`tmp`}` +TemplateStringLiteral: '`' ('\\`' | ~'`')* '`'; + +WhiteSpaces: [\t\u000B\u000C\u0020\u00A0]+ -> channel(HIDDEN); + +LineTerminator: [\r\n\u2028\u2029] -> channel(HIDDEN); + +/// Comments + + +HtmlComment: '' -> channel(HIDDEN); +CDataComment: '' -> channel(HIDDEN); +UnexpectedCharacter: . -> channel(ERROR); + +// Fragment rules + +fragment DoubleStringCharacter + : ~["\\\r\n] + | '\\' EscapeSequence + | LineContinuation + ; + +fragment SingleStringCharacter + : ~['\\\r\n] + | '\\' EscapeSequence + | LineContinuation + ; + +fragment EscapeSequence + : CharacterEscapeSequence + | '0' // no digit ahead! TODO + | HexEscapeSequence + | UnicodeEscapeSequence + | ExtendedUnicodeEscapeSequence + ; + +fragment CharacterEscapeSequence + : SingleEscapeCharacter + | NonEscapeCharacter + ; + +fragment HexEscapeSequence + : 'x' HexDigit HexDigit + ; + +fragment UnicodeEscapeSequence + : 'u' HexDigit HexDigit HexDigit HexDigit + | 'u' '{' HexDigit HexDigit+ '}' + ; + +fragment ExtendedUnicodeEscapeSequence + : 'u' '{' HexDigit+ '}' + ; + +fragment SingleEscapeCharacter + : ['"\\bfnrtv] + ; + +fragment NonEscapeCharacter + : ~['"\\bfnrtv0-9xu\r\n] + ; + +fragment EscapeCharacter + : SingleEscapeCharacter + | [0-9] + | [xu] + ; + +fragment LineContinuation + : '\\' [\r\n\u2028\u2029] + ; + +fragment HexDigit + : [_0-9a-fA-F] + ; + +fragment DecimalIntegerLiteral + : '0' + | [1-9] [0-9_]* + ; + +fragment ExponentPart + : [eE] [+-]? [0-9_]+ + ; + +fragment IdentifierPart + : IdentifierStart + | UnicodeCombiningMark + | UnicodeDigit + | UnicodeConnectorPunctuation + | '\u200C' + | '\u200D' + ; + +fragment IdentifierStart + : UnicodeLetter + | [$_] + | '\\' UnicodeEscapeSequence + ; + +fragment UnicodeLetter + : [\u0041-\u005A] + | [\u0061-\u007A] + | [\u00AA] + | [\u00B5] + | [\u00BA] + | [\u00C0-\u00D6] + | [\u00D8-\u00F6] + | [\u00F8-\u021F] + | [\u0222-\u0233] + | [\u0250-\u02AD] + | [\u02B0-\u02B8] + | [\u02BB-\u02C1] + | [\u02D0-\u02D1] + | [\u02E0-\u02E4] + | [\u02EE] + | [\u037A] + | [\u0386] + | [\u0388-\u038A] + | [\u038C] + | [\u038E-\u03A1] + | [\u03A3-\u03CE] + | [\u03D0-\u03D7] + | [\u03DA-\u03F3] + | [\u0400-\u0481] + | [\u048C-\u04C4] + | [\u04C7-\u04C8] + | [\u04CB-\u04CC] + | [\u04D0-\u04F5] + | [\u04F8-\u04F9] + | [\u0531-\u0556] + | [\u0559] + | [\u0561-\u0587] + | [\u05D0-\u05EA] + | [\u05F0-\u05F2] + | [\u0621-\u063A] + | [\u0640-\u064A] + | [\u0671-\u06D3] + | [\u06D5] + | [\u06E5-\u06E6] + | [\u06FA-\u06FC] + | [\u0710] + | [\u0712-\u072C] + | [\u0780-\u07A5] + | [\u0905-\u0939] + | [\u093D] + | [\u0950] + | [\u0958-\u0961] + | [\u0985-\u098C] + | [\u098F-\u0990] + | [\u0993-\u09A8] + | [\u09AA-\u09B0] + | [\u09B2] + | [\u09B6-\u09B9] + | [\u09DC-\u09DD] + | [\u09DF-\u09E1] + | [\u09F0-\u09F1] + | [\u0A05-\u0A0A] + | [\u0A0F-\u0A10] + | [\u0A13-\u0A28] + | [\u0A2A-\u0A30] + | [\u0A32-\u0A33] + | [\u0A35-\u0A36] + | [\u0A38-\u0A39] + | [\u0A59-\u0A5C] + | [\u0A5E] + | [\u0A72-\u0A74] + | [\u0A85-\u0A8B] + | [\u0A8D] + | [\u0A8F-\u0A91] + | [\u0A93-\u0AA8] + | [\u0AAA-\u0AB0] + | [\u0AB2-\u0AB3] + | [\u0AB5-\u0AB9] + | [\u0ABD] + | [\u0AD0] + | [\u0AE0] + | [\u0B05-\u0B0C] + | [\u0B0F-\u0B10] + | [\u0B13-\u0B28] + | [\u0B2A-\u0B30] + | [\u0B32-\u0B33] + | [\u0B36-\u0B39] + | [\u0B3D] + | [\u0B5C-\u0B5D] + | [\u0B5F-\u0B61] + | [\u0B85-\u0B8A] + | [\u0B8E-\u0B90] + | [\u0B92-\u0B95] + | [\u0B99-\u0B9A] + | [\u0B9C] + | [\u0B9E-\u0B9F] + | [\u0BA3-\u0BA4] + | [\u0BA8-\u0BAA] + | [\u0BAE-\u0BB5] + | [\u0BB7-\u0BB9] + | [\u0C05-\u0C0C] + | [\u0C0E-\u0C10] + | [\u0C12-\u0C28] + | [\u0C2A-\u0C33] + | [\u0C35-\u0C39] + | [\u0C60-\u0C61] + | [\u0C85-\u0C8C] + | [\u0C8E-\u0C90] + | [\u0C92-\u0CA8] + | [\u0CAA-\u0CB3] + | [\u0CB5-\u0CB9] + | [\u0CDE] + | [\u0CE0-\u0CE1] + | [\u0D05-\u0D0C] + | [\u0D0E-\u0D10] + | [\u0D12-\u0D28] + | [\u0D2A-\u0D39] + | [\u0D60-\u0D61] + | [\u0D85-\u0D96] + | [\u0D9A-\u0DB1] + | [\u0DB3-\u0DBB] + | [\u0DBD] + | [\u0DC0-\u0DC6] + | [\u0E01-\u0E30] + | [\u0E32-\u0E33] + | [\u0E40-\u0E46] + | [\u0E81-\u0E82] + | [\u0E84] + | [\u0E87-\u0E88] + | [\u0E8A] + | [\u0E8D] + | [\u0E94-\u0E97] + | [\u0E99-\u0E9F] + | [\u0EA1-\u0EA3] + | [\u0EA5] + | [\u0EA7] + | [\u0EAA-\u0EAB] + | [\u0EAD-\u0EB0] + | [\u0EB2-\u0EB3] + | [\u0EBD-\u0EC4] + | [\u0EC6] + | [\u0EDC-\u0EDD] + | [\u0F00] + | [\u0F40-\u0F6A] + | [\u0F88-\u0F8B] + | [\u1000-\u1021] + | [\u1023-\u1027] + | [\u1029-\u102A] + | [\u1050-\u1055] + | [\u10A0-\u10C5] + | [\u10D0-\u10F6] + | [\u1100-\u1159] + | [\u115F-\u11A2] + | [\u11A8-\u11F9] + | [\u1200-\u1206] + | [\u1208-\u1246] + | [\u1248] + | [\u124A-\u124D] + | [\u1250-\u1256] + | [\u1258] + | [\u125A-\u125D] + | [\u1260-\u1286] + | [\u1288] + | [\u128A-\u128D] + | [\u1290-\u12AE] + | [\u12B0] + | [\u12B2-\u12B5] + | [\u12B8-\u12BE] + | [\u12C0] + | [\u12C2-\u12C5] + | [\u12C8-\u12CE] + | [\u12D0-\u12D6] + | [\u12D8-\u12EE] + | [\u12F0-\u130E] + | [\u1310] + | [\u1312-\u1315] + | [\u1318-\u131E] + | [\u1320-\u1346] + | [\u1348-\u135A] + | [\u13A0-\u13B0] + | [\u13B1-\u13F4] + | [\u1401-\u1676] + | [\u1681-\u169A] + | [\u16A0-\u16EA] + | [\u1780-\u17B3] + | [\u1820-\u1877] + | [\u1880-\u18A8] + | [\u1E00-\u1E9B] + | [\u1EA0-\u1EE0] + | [\u1EE1-\u1EF9] + | [\u1F00-\u1F15] + | [\u1F18-\u1F1D] + | [\u1F20-\u1F39] + | [\u1F3A-\u1F45] + | [\u1F48-\u1F4D] + | [\u1F50-\u1F57] + | [\u1F59] + | [\u1F5B] + | [\u1F5D] + | [\u1F5F-\u1F7D] + | [\u1F80-\u1FB4] + | [\u1FB6-\u1FBC] + | [\u1FBE] + | [\u1FC2-\u1FC4] + | [\u1FC6-\u1FCC] + | [\u1FD0-\u1FD3] + | [\u1FD6-\u1FDB] + | [\u1FE0-\u1FEC] + | [\u1FF2-\u1FF4] + | [\u1FF6-\u1FFC] + | [\u207F] + | [\u2102] + | [\u2107] + | [\u210A-\u2113] + | [\u2115] + | [\u2119-\u211D] + | [\u2124] + | [\u2126] + | [\u2128] + | [\u212A-\u212D] + | [\u212F-\u2131] + | [\u2133-\u2139] + | [\u2160-\u2183] + | [\u3005-\u3007] + | [\u3021-\u3029] + | [\u3031-\u3035] + | [\u3038-\u303A] + | [\u3041-\u3094] + | [\u309D-\u309E] + | [\u30A1-\u30FA] + | [\u30FC-\u30FE] + | [\u3105-\u312C] + | [\u3131-\u318E] + | [\u31A0-\u31B7] + | [\u3400-\u4DBF] + | [\u4E00-\u9FFF] + | [\uA000-\uA48C] + | [\uAC00] + | [\uD7A3] + | [\uF900-\uFA2D] + | [\uFB00-\uFB06] + | [\uFB13-\uFB17] + | [\uFB1D] + | [\uFB1F-\uFB28] + | [\uFB2A-\uFB36] + | [\uFB38-\uFB3C] + | [\uFB3E] + | [\uFB40-\uFB41] + | [\uFB43-\uFB44] + | [\uFB46-\uFBB1] + | [\uFBD3-\uFD3D] + | [\uFD50-\uFD8F] + | [\uFD92-\uFDC7] + | [\uFDF0-\uFDFB] + | [\uFE70-\uFE72] + | [\uFE74] + | [\uFE76-\uFEFC] + | [\uFF21-\uFF3A] + | [\uFF41-\uFF5A] + | [\uFF66-\uFFBE] + | [\uFFC2-\uFFC7] + | [\uFFCA-\uFFCF] + | [\uFFD2-\uFFD7] + | [\uFFDA-\uFFDC] + ; + +fragment UnicodeCombiningMark + : [\u0300-\u034E] + | [\u0360-\u0362] + | [\u0483-\u0486] + | [\u0591-\u05A1] + | [\u05A3-\u05B9] + | [\u05BB-\u05BD] + | [\u05BF] + | [\u05C1-\u05C2] + | [\u05C4] + | [\u064B-\u0655] + | [\u0670] + | [\u06D6-\u06DC] + | [\u06DF-\u06E4] + | [\u06E7-\u06E8] + | [\u06EA-\u06ED] + | [\u0711] + | [\u0730-\u074A] + | [\u07A6-\u07B0] + | [\u0901-\u0903] + | [\u093C] + | [\u093E-\u094D] + | [\u0951-\u0954] + | [\u0962-\u0963] + | [\u0981-\u0983] + | [\u09BC-\u09C4] + | [\u09C7-\u09C8] + | [\u09CB-\u09CD] + | [\u09D7] + | [\u09E2-\u09E3] + | [\u0A02] + | [\u0A3C] + | [\u0A3E-\u0A42] + | [\u0A47-\u0A48] + | [\u0A4B-\u0A4D] + | [\u0A70-\u0A71] + | [\u0A81-\u0A83] + | [\u0ABC] + | [\u0ABE-\u0AC5] + | [\u0AC7-\u0AC9] + | [\u0ACB-\u0ACD] + | [\u0B01-\u0B03] + | [\u0B3C] + | [\u0B3E-\u0B43] + | [\u0B47-\u0B48] + | [\u0B4B-\u0B4D] + | [\u0B56-\u0B57] + | [\u0B82-\u0B83] + | [\u0BBE-\u0BC2] + | [\u0BC6-\u0BC8] + | [\u0BCA-\u0BCD] + | [\u0BD7] + | [\u0C01-\u0C03] + | [\u0C3E-\u0C44] + | [\u0C46-\u0C48] + | [\u0C4A-\u0C4D] + | [\u0C55-\u0C56] + | [\u0C82-\u0C83] + | [\u0CBE-\u0CC4] + | [\u0CC6-\u0CC8] + | [\u0CCA-\u0CCD] + | [\u0CD5-\u0CD6] + | [\u0D02-\u0D03] + | [\u0D3E-\u0D43] + | [\u0D46-\u0D48] + | [\u0D4A-\u0D4D] + | [\u0D57] + | [\u0D82-\u0D83] + | [\u0DCA] + | [\u0DCF-\u0DD4] + | [\u0DD6] + | [\u0DD8-\u0DDF] + | [\u0DF2-\u0DF3] + | [\u0E31] + | [\u0E34-\u0E3A] + | [\u0E47-\u0E4E] + | [\u0EB1] + | [\u0EB4-\u0EB9] + | [\u0EBB-\u0EBC] + | [\u0EC8-\u0ECD] + | [\u0F18-\u0F19] + | [\u0F35] + | [\u0F37] + | [\u0F39] + | [\u0F3E-\u0F3F] + | [\u0F71-\u0F84] + | [\u0F86-\u0F87] + | [\u0F90-\u0F97] + | [\u0F99-\u0FBC] + | [\u0FC6] + | [\u102C-\u1032] + | [\u1036-\u1039] + | [\u1056-\u1059] + | [\u17B4-\u17D3] + | [\u18A9] + | [\u20D0-\u20DC] + | [\u20E1] + | [\u302A-\u302F] + | [\u3099-\u309A] + | [\uFB1E] + | [\uFE20-\uFE23] + ; + +fragment UnicodeDigit + : [\u0030-\u0039] + | [\u0660-\u0669] + | [\u06F0-\u06F9] + | [\u0966-\u096F] + | [\u09E6-\u09EF] + | [\u0A66-\u0A6F] + | [\u0AE6-\u0AEF] + | [\u0B66-\u0B6F] + | [\u0BE7-\u0BEF] + | [\u0C66-\u0C6F] + | [\u0CE6-\u0CEF] + | [\u0D66-\u0D6F] + | [\u0E50-\u0E59] + | [\u0ED0-\u0ED9] + | [\u0F20-\u0F29] + | [\u1040-\u1049] + | [\u1369-\u1371] + | [\u17E0-\u17E9] + | [\u1810-\u1819] + | [\uFF10-\uFF19] + ; + +fragment UnicodeConnectorPunctuation + : [\u005F] + | [\u203F-\u2040] + | [\u30FB] + | [\uFE33-\uFE34] + | [\uFE4D-\uFE4F] + | [\uFF3F] + | [\uFF65] + ; + +fragment RegularExpressionFirstChar + : ~[*\r\n\u2028\u2029\\/[] + | RegularExpressionBackslashSequence + | '[' RegularExpressionClassChar* ']' + ; + +fragment RegularExpressionChar + : ~[\r\n\u2028\u2029\\/[] + | RegularExpressionBackslashSequence + | '[' RegularExpressionClassChar* ']' + ; + +fragment RegularExpressionClassChar + : ~[\r\n\u2028\u2029\]\\] + | RegularExpressionBackslashSequence + ; + +fragment RegularExpressionBackslashSequence + : '\\' ~[\r\n\u2028\u2029] + ; \ No newline at end of file diff --git a/grammars/JavaScriptParser.g4 b/grammars/JavaScriptParser.g4 new file mode 100644 index 0000000..6ba81e9 --- /dev/null +++ b/grammars/JavaScriptParser.g4 @@ -0,0 +1,511 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 by Bart Kiers (original author) and Alexandre Vitorelli (contributor -> ported to CSharp) + * Copyright (c) 2017-2020 by Ivan Kochurkin (Positive Technologies): + added ECMAScript 6 support, cleared and transformed to the universal grammar. + * Copyright (c) 2018 by Juan Alvarez (contributor -> ported to Go) + * Copyright (c) 2019 by Student Main (contributor -> ES2020) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +parser grammar JavaScriptParser; + +options { + tokenVocab=JavaScriptLexer; + superClass=JavaScriptBaseParser; +} + +program + : HashBangLine? sourceElements? EOF + ; + +sourceElement + : statement + ; + +statement + : block + | variableStatement + | importStatement + | exportStatement + | emptyStatement + | classDeclaration + | expressionStatement + | ifStatement + | iterationStatement + | continueStatement + | breakStatement + | returnStatement + //| yieldStatement + //| withStatement + //| labelledStatement + //| switchStatement + //| throwStatement + //| tryStatement + //| debuggerStatement + | functionDeclaration + ; + +block + : '{' statementList? '}' + ; + +statementList + : statement+ + ; + +importStatement + : Import importFromBlock + ; + +importFromBlock + : importDefault? (importNamespace | moduleItems) importFrom eos + | StringLiteral eos + ; + +moduleItems + : '{' (aliasName ',')* (aliasName ','?)? '}' + ; + +importDefault + : aliasName ',' + ; + +importNamespace + : ('*' | identifierName) (As identifierName)? + ; + +importFrom + : From StringLiteral + ; + +aliasName + : identifierName (As identifierName)? + ; + +exportStatement + : Export (exportFromBlock | declaration) eos # ExportDeclaration + | Export Default singleExpression eos # ExportDefaultDeclaration + ; + +exportFromBlock + : importNamespace importFrom eos + | moduleItems importFrom? eos + ; + +declaration + : variableStatement + | classDeclaration + | functionDeclaration + ; + +variableStatement + : variableDeclarationList eos + ; + +variableDeclarationList + : varModifier variableDeclaration (',' variableDeclaration)* + ; + +variableDeclaration + : assignable ('=' singleExpression)? // ECMAScript 6: Array & Object Matching + ; + +emptyStatement + : SemiColon + ; + +expressionStatement + : {self.notOpenBraceAndNotFunction()}? expressionSequence eos + ; + +ifStatement + : If '(' expressionSequence ')' statement (Else statement)? + ; + + +iterationStatement + : Do statement While '(' expressionSequence ')' eos # DoStatement + | While '(' expressionSequence ')' statement # WhileStatement + | For '(' (expressionSequence | variableDeclarationList)? ';' expressionSequence? ';' expressionSequence? ')' statement # ForStatement + | For '(' (singleExpression | variableDeclarationList) In expressionSequence ')' statement # ForInStatement + // strange, 'of' is an identifier. and self.p("of") not work in sometime. + | For /*Await?*/ '(' (singleExpression | variableDeclarationList) identifier{self.p("of")}? expressionSequence ')' statement # ForOfStatement + ; + +varModifier // let, const - ECMAScript 6 + : Var + | let + | Const + ; + +continueStatement + : Continue ({self.notLineTerminator()}? identifier)? eos + ; + +breakStatement + : Break ({self.notLineTerminator()}? identifier)? eos + ; + +returnStatement + : Return ({self.notLineTerminator()}? expressionSequence)? eos + ; + +//yieldStatement +// : Yield ({self.notLineTerminator()}? expressionSequence)? eos +// ; +// +//withStatement +// : With '(' expressionSequence ')' statement +// ; +// +//switchStatement +// : Switch '(' expressionSequence ')' caseBlock +// ; + +//caseBlock +// : '{' caseClauses? (defaultClause caseClauses?)? '}' +// ; +// +//caseClauses +// : caseClause+ +// ; +// +//caseClause +// : Case expressionSequence ':' statementList? +// ; +// +//defaultClause +// : Default ':' statementList? +// ; +// +//labelledStatement +// : identifier ':' statement +// ; +// +//throwStatement +// : Throw {self.notLineTerminator()}? expressionSequence eos +// ; +// +//tryStatement +// : Try block (catchProduction finallyProduction? | finallyProduction) +// ; +// +//catchProduction +// : Catch ('(' assignable? ')')? block +// ; +// +//finallyProduction +// : Finally block +// ; +// +//debuggerStatement +// : Debugger eos +// ; + +functionDeclaration + : /*Async?*/ Function '*'? identifier '(' formalParameterList? ')' '{' functionBody '}' + ; + +classDeclaration + : Class identifier classTail + ; + +classTail + : (Extends singleExpression)? '{' classElement* '}' + ; + +classElement + : (Static | {self.n("static")}? identifier /*| Async*/)* (methodDefinition | assignable '=' objectLiteral ';') + | emptyStatement + | '#'? propertyName '=' singleExpression + ; + +methodDefinition + : '*'? '#'? propertyName '(' formalParameterList? ')' '{' functionBody '}' + | '*'? '#'? getter '(' ')' '{' functionBody '}' + | '*'? '#'? setter '(' formalParameterList? ')' '{' functionBody '}' + ; + +formalParameterList + : formalParameterArg (',' formalParameterArg)* (',' lastFormalParameterArg)? + | lastFormalParameterArg + ; + +formalParameterArg + : assignable ('=' singleExpression)? // ECMAScript 6: Initialization + ; + +lastFormalParameterArg // ECMAScript 6: Rest Parameter + : Ellipsis singleExpression + ; + +functionBody + : sourceElements? + ; + +sourceElements + : sourceElement+ + ; + +arrayLiteral + : ('[' elementList ']') + ; + +elementList + : ','* arrayElement? (','+ arrayElement)* ','* // Yes, everything is optional + ; + +arrayElement + : Ellipsis? singleExpression + ; + +propertyAssignment + : propertyName ':' singleExpression # PropertyExpressionAssignment + | '[' singleExpression ']' ':' singleExpression # ComputedPropertyExpressionAssignment + | /*Async?*/ '*'? propertyName '(' formalParameterList? ')' '{' functionBody '}' # FunctionProperty + | getter '(' ')' '{' functionBody '}' # PropertyGetter + | setter '(' formalParameterArg ')' '{' functionBody '}' # PropertySetter + | Ellipsis? singleExpression # PropertyShorthand + ; + +propertyName + : identifierName + | StringLiteral + | numericLiteral + | '[' singleExpression ']' + ; + +arguments + : '('(argument (',' argument)* ','?)?')' + ; + +argument + : Ellipsis? (singleExpression | identifier) + ; + +expressionSequence + : singleExpression (',' singleExpression)* + ; + +singleExpression + : anoymousFunction # FunctionExpression + | Class identifier? classTail # ClassExpression + | singleExpression '[' expressionSequence ']' # MemberIndexExpression + | singleExpression '?'? '.' '#'? identifierName # MemberDotExpression + | singleExpression arguments # ArgumentsExpression + | New singleExpression arguments? # NewExpression + | New '.' identifier # MetaExpression // new.target + | singleExpression {self.notLineTerminator()}? '++' # PostIncrementExpression + | singleExpression {self.notLineTerminator()}? '--' # PostDecreaseExpression + | Delete singleExpression # DeleteExpression + | Void singleExpression # VoidExpression + | Typeof singleExpression # TypeofExpression + | '++' singleExpression # PreIncrementExpression + | '--' singleExpression # PreDecreaseExpression + | '+' singleExpression # UnaryPlusExpression + | '-' singleExpression # UnaryMinusExpression + | '~' singleExpression # BitNotExpression + | '!' singleExpression # NotExpression +// | Await singleExpression # AwaitExpression + | singleExpression '**' singleExpression # PowerExpression + | singleExpression ('*' | '/' | '%') singleExpression # MultiplicativeExpression + | singleExpression ('+' | '-') singleExpression # AdditiveExpression + | singleExpression '??' singleExpression # CoalesceExpression + | singleExpression ('<<' | '>>' | '>>>') singleExpression # BitShiftExpression + | singleExpression ('<' | '>' | '<=' | '>=') singleExpression # RelationalExpression +// | singleExpression Instanceof singleExpression # InstanceofExpression + | singleExpression In singleExpression # InExpression + | singleExpression ('==' | '!=' | '===' | '!==') singleExpression # EqualityExpression + | singleExpression '&' singleExpression # BitAndExpression + | singleExpression '^' singleExpression # BitXOrExpression + | singleExpression '|' singleExpression # BitOrExpression + | singleExpression '&&' singleExpression # LogicalAndExpression + | singleExpression '||' singleExpression # LogicalOrExpression + | singleExpression '?' singleExpression ':' singleExpression # TernaryExpression + | singleExpression '=' singleExpression # AssignmentExpression + | singleExpression assignmentOperator singleExpression # AssignmentOperatorExpression + | Import '(' singleExpression ')' # ImportExpression + | singleExpression TemplateStringLiteral # TemplateStringExpression // ECMAScript 6 +// | yieldStatement # YieldExpression // ECMAScript 6 + | This # ThisExpression + | identifier # IdentifierExpression + | Super # SuperExpression + | literal # LiteralExpression + | arrayLiteral # ArrayLiteralExpression + | objectLiteral # ObjectLiteralExpression + | '(' expressionSequence ')' # ParenthesizedExpression + ; + +assignable + : identifier + | arrayLiteral + | objectLiteral + ; + +objectLiteral + : '{' (propertyAssignment (',' propertyAssignment)*)? ','? '}' + ; + +anoymousFunction + : functionDeclaration # FunctionDecl + | /*Async?*/ Function '*'? '(' formalParameterList? ')' '{' functionBody '}' # AnoymousFunctionDecl + | /*Async?*/ arrowFunctionParameters '=>' arrowFunctionBody # ArrowFunction + ; + +arrowFunctionParameters + : identifier + | '(' formalParameterList? ')' + ; + +arrowFunctionBody + : singleExpression + | '{' functionBody '}' + ; + +assignmentOperator + : '*=' + | '/=' + | '%=' + | '+=' + | '-=' + | '<<=' + | '>>=' + | '>>>=' + | '&=' + | '^=' + | '|=' + | '**=' + ; + +literal + : NullLiteral + | BooleanLiteral + | StringLiteral + | TemplateStringLiteral + | RegularExpressionLiteral + | numericLiteral + | bigintLiteral + ; + +numericLiteral + : DecimalLiteral +// | HexIntegerLiteral +// | OctalIntegerLiteral +// | OctalIntegerLiteral2 +// | BinaryIntegerLiteral + ; + +bigintLiteral + : BigDecimalIntegerLiteral +// | BigHexIntegerLiteral +// | BigOctalIntegerLiteral +// | BigBinaryIntegerLiteral + ; + +getter + : identifier {self.p("get")}? propertyName + ; + +setter + : identifier {self.p("set")}? propertyName + ; + +identifierName + : identifier + | reservedWord + ; + +identifier + : Identifier + | NonStrictLet +// | Async + ; + +reservedWord + : keyword + | NullLiteral + | BooleanLiteral + ; + +keyword + : Break + | Do +// | Instanceof + | Typeof +// | Case + | Else + | New + | Var +// | Catch +// | Finally + | Return + | Void + | Continue + | For +// | Switch + | While +// | Debugger + | Function + | This +// | With + | Default + | If + | Throw + | Delete + | In +// | Try + + | Class + | Enum + | Extends + | Super + | Const + | Export + | Import + | Implements + | let + | Private + | Public + | Interface + | Package + | Protected + | Static +// | Yield +// | Async +// | Await + | From + | As + ; + +let + : NonStrictLet + | StrictLet + ; + +eos + : SemiColon + | EOF + | {self.lineTerminatorAhead()}? + | {self.closeBrace()}? + ; \ No newline at end of file diff --git a/jasminesnake/__init__.py b/jasminesnake/__init__.py new file mode 100644 index 0000000..95b08c8 --- /dev/null +++ b/jasminesnake/__init__.py @@ -0,0 +1,3 @@ +__version__ = "0.0.1" + +# TODO: make it usable as a module too diff --git a/jasminesnake/__main__.py b/jasminesnake/__main__.py new file mode 100644 index 0000000..cbc911b --- /dev/null +++ b/jasminesnake/__main__.py @@ -0,0 +1,57 @@ +from jasminesnake import __version__ + +from antlr4 import * +from .lex import JavaScriptLexer, JavaScriptParser + +import argparse +import colorama + + +arg_parser = argparse.ArgumentParser( + description="Jasmine Snake, another JS interpreter in Python", + epilog="I hope you don't use it, **especially** in production.", +) + +arg_parser.add_argument("--snake", action="store_true", help="Print a snake") +args = arg_parser.parse_args() + +JSL = JavaScriptLexer.JavaScriptLexer +JSP = JavaScriptParser.JavaScriptParser + + +class WriteTreeListener(ParseTreeListener): + def visitTerminal(self, node: TerminalNode): + print("Visit Terminal: " + str(node) + " - " + repr(node)) + + +def main(): + colorama.init() + + print("Jasmine Snake v{version}".format(version=__version__)) + + if args.snake: + print( + colorama.Style.DIM + + "[snake is sleeping now, so you see this stub. pretend you see the snake, please.]" + ) + print( + colorama.Fore.BLACK + + colorama.Back.YELLOW + + "Don't tread on me!" + + colorama.Back.RESET + + colorama.Fore.RESET + ) + + print() + + input_stream = InputStream('"use strict";var a;\na=2+a;') + lexer = JSL(input_stream) + stream = CommonTokenStream(lexer) + parser = JSP(stream) + print("Created parsers") + tree = parser.program() + ParseTreeWalker.DEFAULT.walk(WriteTreeListener(), tree) + + +if __name__ == "__main__": + main() diff --git a/jasminesnake/lex/JavaScriptBaseLexer.py b/jasminesnake/lex/JavaScriptBaseLexer.py new file mode 100644 index 0000000..6f715ae --- /dev/null +++ b/jasminesnake/lex/JavaScriptBaseLexer.py @@ -0,0 +1,106 @@ +from antlr4 import * + +relativeImport = False +if __name__ is not None and "." in __name__: + relativeImport = True + + +class JavaScriptBaseLexer(Lexer): + def __init__(self, *args, **kwargs): + print("JavaScriptBaseLexerInit") + super(JavaScriptBaseLexer, self).__init__(*args, **kwargs) + + """Stores values of nested modes. By default mode is strict or + defined externally (useStrictDefault)""" + self.scopeStrictModes = [] + self.lastToken: Token = None + + """Default value of strict mode + Can be defined externally by setUseStrictDefault""" + self.useStrictDefault = False + + """Current value of strict mode + Can be defined during parsing, see StringFunctions.js and StringGlobal.js samples""" + self.useStrictCurrent = False + + def getStrictDefault(self) -> bool: + return self.useStrictDefault + + def setUseStrictDefault(self, value: bool): + self.useStrictDefault = value + self.useStrictCurrent = value + + def isStrictMode(self): + return self.useStrictCurrent + + def isStartOfFile(self): + return self.lastToken is None + + def nextToken(self) -> Token: + """Return the next token from the character stream and records this last + token in case it resides on the default channel. This recorded token + is used to determine when the lexer could possibly match a regex + literal. Also changes scopeStrictModes stack if tokenize special + string 'use strict'; + + :return the next token from the character stream.""" + next_token: Token = super(JavaScriptBaseLexer, self).nextToken() + + if next_token.channel == Token.DEFAULT_CHANNEL: + self.lastToken = next_token + + return next_token + + def processOpenBrace(self): + self.useStrictCurrent = bool(self.scopeStrictModes) and ( + True if self.scopeStrictModes[-1] else self.useStrictDefault + ) + self.scopeStrictModes.append(self.useStrictCurrent) + + def processCloseBrace(self): + self.useStrictCurrent = bool(self.scopeStrictModes) and ( + True if self.scopeStrictModes.pop(-1) else self.useStrictDefault + ) + + def processStringLiteral(self): + if relativeImport: + from .JavaScriptLexer import JavaScriptLexer + else: + from JavaScriptLexer import JavaScriptLexer + if not self.lastToken or self.lastToken.type == JavaScriptLexer.OpenBrace: + text = self.text + if text == '"use strict"' or text == "'use strict'": + if self.scopeStrictModes: + self.scopeStrictModes.pop(-1) + self.useStrictCurrent = True + self.scopeStrictModes.append(self.useStrictCurrent) + + def isRegexPossible(self) -> bool: + """Returns {@code true} if the lexer can match a regex literal. """ + if relativeImport: + from .JavaScriptLexer import JavaScriptLexer + else: + from JavaScriptLexer import JavaScriptLexer + + if not self.lastToken: + # No token has been produced yet: at the start of the input, + # no division is possible, so a regex literal _is_ possible. + return True + + if self.lastToken.type in [ + JavaScriptLexer.Identifier, + JavaScriptLexer.NullLiteral, + JavaScriptLexer.BooleanLiteral, + JavaScriptLexer.This, + JavaScriptLexer.CloseBracket, + JavaScriptLexer.CloseParen, + JavaScriptLexer.OctalIntegerLiteral, + JavaScriptLexer.DecimalLiteral, + JavaScriptLexer.HexIntegerLiteral, + JavaScriptLexer.StringLiteral, + JavaScriptLexer.PlusPlus, + JavaScriptLexer.MinusMinus, + ]: + return False + + return True diff --git a/jasminesnake/lex/JavaScriptBaseParser.py b/jasminesnake/lex/JavaScriptBaseParser.py new file mode 100644 index 0000000..1d977b5 --- /dev/null +++ b/jasminesnake/lex/JavaScriptBaseParser.py @@ -0,0 +1,108 @@ +from antlr4 import * + +relativeImport = False +if __name__ is not None and "." in __name__: + relativeImport = True + + +class JavaScriptBaseParser(Parser): + @staticmethod + def parser(): + if relativeImport: + from .JavaScriptParser import JavaScriptParser + else: + from JavaScriptParser import JavaScriptParser + return JavaScriptParser + + def p(self, s: str) -> bool: + return self.prev(s) + + def prev(self, s: str) -> bool: + return self._input.LT(-1).text == s + + def n(self, s: str) -> bool: + return self.next(s) + + def next(self, s: str) -> bool: + return self._input.LT(1).text == s + + def notLineTerminator(self) -> bool: + JavaScriptParser = self.parser() + + return not self.here(JavaScriptParser.LineTerminator) + + def notOpenBraceAndNotFunction(self) -> bool: + JavaScriptParser = self.parser() + + nextTokenType = self._input.LT(1).type + return ( + nextTokenType != JavaScriptParser.OpenBrace + and nextTokenType != JavaScriptParser.Function + ) + + def closeBrace(self) -> bool: + JavaScriptParser = self.parser() + + return self._input.LT(1).type == JavaScriptParser.CloseBrace + + def here(self, tokenType: int) -> bool: + """ + Returns {@code true} iff on the current index of the parser's + token stream a token of the given {@code type} exists on the + {@code HIDDEN} channel. + :param:type: + the type of the token on the {@code HIDDEN} channel + to check. + :return:{@code true} iff on the current index of the parser's + token stream a token of the given {@code type} exists on the + {@code HIDDEN} channel. + """ + # Get the token ahead of the current index. + assert isinstance(self.getCurrentToken(), Token) + possibleIndexEosToken: Token = self.getCurrentToken().tokenIndex - 1 + ahead = self._input.get(possibleIndexEosToken) + + # Check if the token resides on the HIDDEN channel and if it's of the + # provided type. + return (ahead.channel == Lexer.HIDDEN) and (ahead.type == tokenType) + + def lineTerminatorAhead(self) -> bool: + """ + Returns {@code true} iff on the current index of the parser's + token stream a token exists on the {@code HIDDEN} channel which + either is a line terminator, or is a multi line comment that + contains a line terminator. + + :return: {@code true} iff on the current index of the parser's + token stream a token exists on the {@code HIDDEN} channel which + either is a line terminator, or is a multi line comment that + contains a line terminator. + """ + JavaScriptParser = self.parser() + + # Get the token ahead of the current index. + possibleIndexEosToken: Token = self.getCurrentToken().tokenIndex - 1 + ahead: Token = self._input.get(possibleIndexEosToken) + + if ahead.channel != Lexer.HIDDEN: + # We're only interested in tokens on the HIDDEN channel. + return False + + if ahead.type == JavaScriptParser.LineTerminator: + # There is definitely a line terminator ahead. + return True + + if ahead.type == JavaScriptParser.WhiteSpaces: + # Get the token ahead of the current whitespaces. + possibleIndexEosToken = self.getCurrentToken().tokenIndex - 2 + ahead = self._input.get(possibleIndexEosToken) + + # Get the token's text and type. + text = ahead.text + tokenType = ahead.type + + # Check if the token is, or contains a line terminator. + return ( + tokenType == JavaScriptParser.MultiLineComment + and (text.contains("\r") or text.contains("\n")) + ) or (tokenType == JavaScriptParser.LineTerminator) diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..511556f --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,3 @@ +-r requirements.txt +tox==3.14.6 +pytest==5.4.1 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a7cddca --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +antlr4-python3-runtime +colorama==0.4.3 \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..771fbc4 --- /dev/null +++ b/setup.py @@ -0,0 +1,30 @@ +from setuptools import setup +from jasminesnake import __version__ + +with open("README.md", "r") as fh: + long_description = fh.read() + +with open("requirements.txt") as f: + requirements = f.readlines() + +setup( + name="jasminesnake", + version=__version__, + packages=["jasminesnake"], + url="https://github.com/t1meshift/js", + license="MIT", + author="Yury Kurlykov", + author_email="sh1ftr@protonmail.ch", + description="Another JavaScript interpreter written in Python", + long_description=long_description, + long_description_content_type="text/markdown", + classifiers=[ + "Programming Language :: Python :: 3", + # "Programming Language :: JavaScript", + "License :: OSI Approved :: MIT License", + "Development Status :: 2 - Pre-Alpha", + ], + python_requires=">=3.8", + install_requires=requirements, + entry_points={"console_scripts": ["jasminesnake = jasminesnake.__main__:main"]}, +) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..942c341 --- /dev/null +++ b/tox.ini @@ -0,0 +1,10 @@ +[pytest] +envlist = py38 + +[testenv] +changedir = tests +deps = + pytest +commands = + # Any commands go here + pytest --basetemp="{envtmpdir}" {posargs} \ No newline at end of file