Initial commit

master
Yury Kurlykov 2020-04-15 16:15:56 +10:00
commit 812f5107c3
Signed by: t1meshift
GPG Key ID: B133F3167ABF94D8
13 changed files with 1702 additions and 0 deletions

108
.gitignore vendored Normal file
View File

@ -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

21
LICENSE Normal file
View File

@ -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.

32
README.md Normal file
View File

@ -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)

711
grammars/JavaScriptLexer.g4 Normal file
View File

@ -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: '<![CDATA[' .*? ']]>' -> 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]
;

View File

@ -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
| <assoc=right> 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
| <assoc=right> singleExpression '=' singleExpression # AssignmentExpression
| <assoc=right> 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()}?
;

3
jasminesnake/__init__.py Normal file
View File

@ -0,0 +1,3 @@
__version__ = "0.0.1"
# TODO: make it usable as a module too

57
jasminesnake/__main__.py Normal file
View File

@ -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()

View File

@ -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

View File

@ -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)

3
requirements-dev.txt Normal file
View File

@ -0,0 +1,3 @@
-r requirements.txt
tox==3.14.6
pytest==5.4.1

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
antlr4-python3-runtime
colorama==0.4.3

30
setup.py Normal file
View File

@ -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"]},
)

10
tox.ini Normal file
View File

@ -0,0 +1,10 @@
[pytest]
envlist = py38
[testenv]
changedir = tests
deps =
pytest
commands =
# Any commands go here
pytest --basetemp="{envtmpdir}" {posargs}