From 7f68959590df0bc48f10d635156206e807e5f6be Mon Sep 17 00:00:00 2001 From: Yury Kurlykov Date: Mon, 27 Apr 2020 23:07:15 +1000 Subject: [PATCH] Make a huge load of changes - Remove visitors - Add logging - Add file read - Make ESTree nodes work - Move JS input streams to its own module - Implement parse tree listeners (WIP) - Add custom error listener (WIP) --- README.md | 2 +- jasminesnake/__init__.py | 28 +- jasminesnake/__main__.py | 105 ++- jasminesnake/ast/__init__.py | 28 +- jasminesnake/ast/nodes.py | 988 +++++++++++++++++++++++ jasminesnake/ast/nodes/__init__.py | 73 -- jasminesnake/ast/nodes/declarations.py | 52 -- jasminesnake/ast/nodes/expressions.py | 669 --------------- jasminesnake/ast/nodes/functions.py | 31 - jasminesnake/ast/nodes/identifiers.py | 13 - jasminesnake/ast/nodes/literals.py | 14 - jasminesnake/ast/nodes/operator_enums.py | 72 -- jasminesnake/ast/nodes/patterns.py | 39 - jasminesnake/ast/nodes/programs.py | 15 - jasminesnake/ast/nodes/statements.py | 158 ---- jasminesnake/ast/parse_tree_listeners.py | 149 ++++ jasminesnake/ast/visitors.py | 12 - jasminesnake/js_stream.py | 110 +++ jasminesnake/lex/ErrorListeners.py | 37 + jasminesnake/lex/JavaScriptBaseLexer.py | 3 +- jasminesnake/lex/JavaScriptBaseParser.py | 1 + requirements.txt | 6 +- 22 files changed, 1418 insertions(+), 1187 deletions(-) create mode 100644 jasminesnake/ast/nodes.py delete mode 100644 jasminesnake/ast/nodes/__init__.py delete mode 100644 jasminesnake/ast/nodes/declarations.py delete mode 100644 jasminesnake/ast/nodes/expressions.py delete mode 100644 jasminesnake/ast/nodes/functions.py delete mode 100644 jasminesnake/ast/nodes/identifiers.py delete mode 100644 jasminesnake/ast/nodes/literals.py delete mode 100644 jasminesnake/ast/nodes/operator_enums.py delete mode 100644 jasminesnake/ast/nodes/patterns.py delete mode 100644 jasminesnake/ast/nodes/programs.py delete mode 100644 jasminesnake/ast/nodes/statements.py create mode 100644 jasminesnake/ast/parse_tree_listeners.py delete mode 100644 jasminesnake/ast/visitors.py create mode 100644 jasminesnake/js_stream.py create mode 100644 jasminesnake/lex/ErrorListeners.py diff --git a/README.md b/README.md index bfbc774..97053b4 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ pip install -r requirements.txt # Use requirements-dev.txt if you want to run te ## Running ```bash -antlr4 -o jasminesnake/lex -package lex -Dlanguage=Python3 grammars/*.g4 +antlr4 -Xexact-output-dir -o jasminesnake/lex -package lex -Dlanguage=Python3 -listener grammars/*.g4 python -m jasminesnake ``` diff --git a/jasminesnake/__init__.py b/jasminesnake/__init__.py index b059a6f..5ee1111 100644 --- a/jasminesnake/__init__.py +++ b/jasminesnake/__init__.py @@ -1,8 +1,13 @@ -__version__ = "0.0.1" -__snake__ = """ +"""Pylint tells me this module should have a docstring. +So here it is. +""" +import logging + +__version__ = "0.0.2" +__snake__ = r""" _________ _________ - / \ / \\ - / /~~~~~\ \ / /~~~~~\ \\ + / \ / \ + / /~~~~~\ \ / /~~~~~\ \ | | | | | | | | | | | | | | | | | | | | | | | | / @@ -12,4 +17,19 @@ __snake__ = """ | ~~~~~~~~~ ~~~~~~~~ ^ """ + +LOG_LEVELS = { + 0: {"level": logging.CRITICAL, "format": u"[%(asctime)s] %(message)s"}, + 1: { + "level": logging.ERROR, + "format": u"[%(asctime)s] [%(levelname)s] %(message)s", + }, + 2: {"level": logging.WARN, "format": u"[%(asctime)s] [%(levelname)s] %(message)s"}, + 3: {"level": logging.INFO, "format": u"[%(asctime)s] [%(levelname)s] %(message)s"}, + 4: { + "level": logging.DEBUG, + "format": u"[%(asctime)s] [%(levelname)s] %(filename)s:%(lineno)d: %(message)s", + }, +} + # TODO: make it usable as a module too diff --git a/jasminesnake/__main__.py b/jasminesnake/__main__.py index 3275dda..48ab47f 100644 --- a/jasminesnake/__main__.py +++ b/jasminesnake/__main__.py @@ -1,34 +1,53 @@ -from jasminesnake import __version__, __snake__ - -from antlr4 import * -from .lex import JavaScriptLexer, JavaScriptParser - +"""Pylint tells me this module should have a docstring. +So here it is. +""" +import sys import argparse +import logging import colorama +import coloredlogs + +from jasminesnake import __version__, __snake__, LOG_LEVELS +from .js_stream import JSBaseStream, JSStringStream, JSFileStream +from .lex.ErrorListeners import LogErrorListener +import ast -arg_parser = argparse.ArgumentParser( - description="Jasmine Snake, another JS interpreter in Python", - epilog="I hope you don't use it, **especially** in production.", -) +def create_argument_parser(): + _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() + _arg_parser.add_argument("--snake", action="store_true", help="print a snake") + _arg_parser.add_argument( + "--verbose", + "-v", + action="count", + default=0, + help="be more verbose. up to 4 (-vvvv) could be handled, more are ignored", + ) + _arg_parser.add_argument( + "infile", + type=str, + help='JS input file. use "-" to read input from stdin.', + nargs="?", + ) -JSL = JavaScriptLexer.JavaScriptLexer -JSP = JavaScriptParser.JavaScriptParser - - -class WriteTreeListener(ParseTreeListener): - def visitTerminal(self, node: TerminalNode): - print("Visit Terminal: " + str(node) + " - " + repr(node)) + return _arg_parser def main(): + # Init colorama colorama.init() - print("Jasmine Snake v{version}".format(version=__version__)) + # Init logging + log_level = min(args.verbose, 4) # Ignore verbosity values more than 4 + coloredlogs.install( + level=LOG_LEVELS[log_level]["level"], fmt=LOG_LEVELS[log_level]["format"] + ) + # Print the snake if an argument is present if args.snake: print(colorama.Style.DIM + __snake__ + colorama.Style.RESET_ALL) print( @@ -39,22 +58,48 @@ def main(): + colorama.Fore.RESET ) + # Read JS code from file or stdin + if args.infile is not None: + stream: JSBaseStream + + if args.infile == "-": + input_str = sys.stdin.read() + stream = JSStringStream(input_str) + + else: + stream = JSFileStream(args.infile, LogErrorListener()) + + tree = stream.parse() + ast_tree = ast.from_parse_tree(tree) + + # TODO: run logic + sys.exit(0) + + print("Jasmine Snake v{version}".format(version=__version__)) + print( + colorama.Fore.YELLOW + + "Notice that only single-line statements are supported." + + colorama.Fore.RESET + ) print() - input_stream = InputStream("var a;\n{a=2+a;}") - lexer = JSL(input_stream) - stream = CommonTokenStream(lexer) + try: + while True: + input_str = input("> ") + logging.debug("Got input %s", input_str) - stream.fill() - for token in stream.tokens: - print("Token: {}".format(str(token))) + stream = JSStringStream(input_str, LogErrorListener()) + tree = stream.parse() + logging.debug("Got tree %s", tree.toStringTree(stream.parser.ruleNames)) - parser = JSP(stream) - print("Created parsers") - tree = parser.program() - print(tree.toStringTree(parser.ruleNames)) - # ParseTreeWalker.DEFAULT.walk(WriteTreeListener(), tree) + ast_tree = ast.from_parse_tree(tree) + # TODO: run logic + except EOFError: + print("Ctrl-D received, shutting down...") + sys.exit(0) if __name__ == "__main__": + arg_parser = create_argument_parser() + args = arg_parser.parse_args() main() diff --git a/jasminesnake/ast/__init__.py b/jasminesnake/ast/__init__.py index 420d815..39a40df 100644 --- a/jasminesnake/ast/__init__.py +++ b/jasminesnake/ast/__init__.py @@ -1,3 +1,29 @@ """AST module.""" -from . import nodes, visitors +from antlr4 import ParseTreeWalker +from tree_format import format_tree + +import lex.JavaScriptParser as Parser +import ast.nodes +from .parse_tree_listeners import ASTListener + +JSP = Parser.JavaScriptParser + + +def from_parse_tree(tree: JSP.ProgramContext) -> ast.nodes.Program: + """Generate AST from ANTLR parse tree. + + Args: + tree (JSP.ProgramContext): ANTLR parse tree. + + Returns: + `Program` AST node, which is the root node. + """ + ast_listener = ASTListener() + ParseTreeWalker.DEFAULT.walk(ast_listener, tree) + return ast_listener.program_node + + +# Delete temporary imports +del JSP +del Parser diff --git a/jasminesnake/ast/nodes.py b/jasminesnake/ast/nodes.py new file mode 100644 index 0000000..3c055ed --- /dev/null +++ b/jasminesnake/ast/nodes.py @@ -0,0 +1,988 @@ +"""The module with AST nodes declaration. They are ESTree compliant. + +The module lacks support of: + * ES5 features: + * labelled statements + * switch statements + * try-catch statements + * debugger statement + * with statement + * RegExp + * ES6 features: + * generators/yield statement + * for-of statement + * template literals + * and other ES6 features :) + +More about ESTree standard: +https://github.com/estree/estree/ + +Todo: + * Add support for lacking features +""" + +from typing import List, Union, Optional, Literal as TypeLiteral, TypedDict +from enum import Enum + +# The Lord sees I actually wanted to split it up, but ESTree hierarchy is so messed up... No. It's actually *fucked up* +# that much that I couldn't even resolve circular dependencies in the submodules. I have to reap what I've sown. + +# Custom types used in the nodes + +number = Union[int, float] +"""A type union consisting of int and float Python types. Consider it as Number type from JavaScript.""" + +SourceTypeLiteral = TypeLiteral["script", "module"] +"""The type for the `sourceType` field.""" + +VarDeclKind = TypeLiteral["var", "let", "const"] +"""The type for the `kind` field of `VariableDeclaration`.""" + +PropKind = TypeLiteral["init", "get", "set"] +"""A type for a `kind` field of `Property`.""" + + +class UnaryOperator(Enum): + """A unary operator token.""" + + MINUS = "-" + PLUS = "+" + NOT_LOGIC = "!" + NOT_BIT = "~" + TYPEOF = "typeof" + VOID = "void" + DELETE = "delete" + + +class UpdateOperator(Enum): + """An update (increment or decrement) operator token.""" + + INCREMENT = "++" + DECREMENT = "--" + + +class BinaryOperator(Enum): + """A binary operator token.""" + + EQ = "==" + NEQ = "!=" + EQ_IDENTITY = "===" + NEQ_IDENTITY = "!==" + LT = "<" + LTE = "<=" + GT = ">" + GTE = ">=" + SHL = "<<" + SHR = ">>" + SHR_LOGIC = ">>>" + ADD = "+" + SUB = "-" + MUL = "*" + DIV = "/" + MOD = "%" + OR = "|" + XOR = "^" + AND = "&" + IN = "in" + INSTANCEOF = "instanceof" + + +class AssignmentOperator(Enum): + """An assignment operator token.""" + + ASSIGN = "=" + ADD = "+=" + SUB = "-=" + MUL = "*=" + DIV = "/=" + MOD = "%=" + SHL = "<<=" + SHR = ">>=" + SHR_LOGIC = ">>>=" + OR = "|=" + XOR = "^=" + AND = "&=" + + +class LogicalOperator(Enum): + """A logical operator token.""" + + OR = "||" + AND = "&&" + + +# Nodes forward declarations +class Expression: + ... + + +class Pattern: + ... + + +class Directive: + ... + + +class Statement: + ... + + +class FunctionBody: + ... + + +class VariableDeclaration: + ... + + +class Property: + ... + + +# "Node objects" block + + +class Position: + """The class for an object consisting of a line number (1-indexed) and a column number (0-indexed).""" + + def __init__(self, line: int, column: int): + if line < 1 or column < 0: + raise ValueError( + "L{}:C{} is not valid ESTree position!".format(line, column) + ) + + self.line = line + self.column = column + + +class SourceLocation: + """ + The class for the source location information of a node. + + Consists of a start position (the position of the first character of the parsed source region) and an end + position (the position of the first character after the parsed source region). + + See Also: + Position + """ + + def __init__(self, source: Optional[str], start: Position, end: Position): + self.source = source + self.start = start + self.end = end + + +class Node: + """ESTree AST nodes are represented as Node objects, which may have any prototype inheritance but which implement + this interface. + + The `type` field is a string representing the AST variant type. Each subtype of `Node` is documented below with + the specific string of its `type` field. You can use this field to determine which interface a node implements. + + The `loc` field represents the source location information of the node. If the node contains no information about + the source location, the field is `None`; otherwise it contains a `SourceLocation` object. + + See Also: + SourceLocation + """ + + def __init__(self, node_type: str, loc: Optional[SourceLocation]): + self.type = node_type + self.loc = loc + + +# "Identifier" block + + +class Identifier(Expression, Pattern): + """An identifier. Note that an identifier may be an expression or a destructuring pattern.""" + + def __init__(self, loc: Optional[SourceLocation], name: str): + super(Identifier, self).__init__("Identifier", loc) + self.name = name + + +# "Literal" block + + +class Literal(Expression): + """A literal token. Note that a literal can be an expression.""" + + def __init__( + self, loc: Optional[SourceLocation], value: Union[str, bool, number, None] + ): + super().__init__("Literal", loc) + self.value = value + + +# "Programs" block + + +class Program(Node): + """A complete program source tree.""" + + def __init__( + self, + loc: Optional[SourceLocation], + source_type: SourceTypeLiteral, + body: List[Union[Directive, Statement]], + ): + super().__init__("Program", loc) + self.body = body + self.source_type = source_type + + +# "Functions" block + + +class Function(Node): + """A function declaration or expression. + + See Also: + FunctionDeclaration + FunctionExpression + FunctionBody + """ + + def __init__( + self, + node_type: str, + loc: Optional[SourceLocation], + function_id: Optional[Identifier], + params: List[Pattern], + body: FunctionBody, + ): + super().__init__(node_type, loc) + self.id = function_id + self.params = params + self.body = body + + +# "Statements" block + + +class Statement(Node): + """Any statement.""" + + def __init__(self, node_type: str, loc: Optional[SourceLocation]): + super().__init__(node_type, loc) + + +class EmptyStatement(Statement): + """An empty statement, i.e., a solitary semicolon.""" + + def __init__(self, loc: Optional[SourceLocation]): + super().__init__("EmptyStatement", loc) + + +class BlockStatement(Statement): + """A block statement, i.e., a sequence of statements surrounded by braces.""" + + def __init__(self, loc: Optional[SourceLocation], body: List[Statement]): + super().__init__("BlockStatement", loc) + self.body = body + + +class ExpressionStatement(Statement): + """An expression statement, i.e., a statement consisting of a single expression.""" + + def __init__(self, loc: Optional[SourceLocation], expression: Expression): + super().__init__("ExpressionStatement", loc) + self.expression = expression + + +class Directive(Node): + """A directive from the directive prologue of a script or function. The `directive` property is the raw string + source of the directive without quotes. + """ + + def __init__( + self, loc: Optional[SourceLocation], expression: Literal, directive: str + ): + super().__init__("Directive", loc) + self.expression = expression + self.directive = directive + + +class FunctionBody(BlockStatement): + """The body of a function, which is a block statement that may begin with directives.""" + + def __init__( + self, loc: Optional[SourceLocation], body: List[Union[Directive, Statement]] + ): + super().__init__(loc, body) + + +class ReturnStatement(Statement): + """A `return` statement.""" + + def __init__(self, loc: Optional[SourceLocation], argument: Optional[Expression]): + super().__init__("ReturnStatement", loc) + self.argument = argument + + +class BreakStatement(Statement): + """A `break` statement.""" + + def __init__(self, loc: Optional[SourceLocation], label: Optional[Identifier]): + super().__init__("BreakStatement", loc) + self.label = label + + +class ContinueStatement(Statement): + """A `continue` statement.""" + + def __init__(self, loc: Optional[SourceLocation], label: Optional[Identifier]): + super().__init__("ContinueStatement", loc) + self.label = label + + +class IfStatement(Statement): + """An `if` statement.""" + + def __init__( + self, + loc: Optional[SourceLocation], + test: Expression, + consequent: Statement, + alternate: Optional[Statement], + ): + super().__init__("IfStatement", loc) + self.test = test + self.consequent = consequent + self.alternate = alternate + + +class WhileStatement(Statement): + """A `while` statement.""" + + def __init__( + self, loc: Optional[SourceLocation], test: Expression, body: Statement + ): + super().__init__("WhileStatement", loc) + self.test = test + self.body = body + + +class DoWhileStatement(Statement): + """A `do`/`while` statement.""" + + def __init__( + self, loc: Optional[SourceLocation], body: Statement, test: Expression + ): + super().__init__("DoWhileStatement", loc) + self.body = body + self.test = test + + +class ForStatement(Statement): + """A `for` statement.""" + + def __init__( + self, + loc: Optional[SourceLocation], + init: Union[VariableDeclaration, Expression, None], + test: Optional[Expression], + update: Optional[Expression], + body: Statement, + ): + super().__init__("ForStatement", loc) + self.init = init + self.test = test + self.update = update + self.body = body + + +class ForInStatement(Statement): + """A `for`/`in` statement.""" + + def __init__( + self, + loc: Optional[SourceLocation], + left: Union[VariableDeclaration, Pattern], + right: Expression, + body: Statement, + ): + super().__init__("ForInStatement", loc) + self.left = left + self.right = right + self.body = body + + +# "Declarations" block + + +class Declaration(Statement): + """Any declaration node. Note that declarations are considered statements; this is because declarations can + appear in any statement context. """ + + def __init__(self, node_type: str, loc: Optional[SourceLocation]): + super().__init__(node_type, loc) + + +class FunctionDeclaration(Function, Declaration): + """A function declaration. Note that unlike in the parent interface `Function`, the `id` cannot be `None`.""" + + def __init__( + self, + loc: Optional[SourceLocation], + function_id: Identifier, + params: List[Pattern], + body: FunctionBody, + ): + super().__init__("FunctionDeclaration", loc, function_id, params, body) + + +class VariableDeclarator(Node): + """A variable declarator.""" + + def __init__( + self, loc: Optional[SourceLocation], var_id: Pattern, init: Optional[Exception] + ): + super().__init__("VariableDeclarator", loc) + self.id = var_id + self.init = init + + +class VariableDeclaration(Declaration): + """A variable declaration.""" + + def __init__( + self, + loc: Optional[SourceLocation], + kind: VarDeclKind, + declarations: List[VariableDeclarator], + ): + super().__init__("VariableDeclaration", loc) + self.declarations = declarations + self.kind = kind + + +# "Expressions" block + + +class Expression(Node): + """Any expression node. Since the left-hand side of an assignment may be any expression in general, an expression + can also be a pattern. + + See Also: + Pattern + """ + + def __init__(self, node_type: str, loc: Optional[SourceLocation]): + super().__init__(node_type, loc) + + +class Super(Node): + """A ``super`` pseudo-expression.""" + + def __init__(self, loc: Optional[SourceLocation]): + super().__init__("Super", loc) + + +class SpreadElement(Node): + """Spread expression, e.g., ``[head, ...iter, tail]``, ``f(head, ...iter, ...tail)``.""" + + def __init__(self, loc: Optional[SourceLocation], argument: Expression): + super().__init__("SpreadElement", loc) + self.argument = argument + + +class ThisExpression(Expression): + """A `this` expression.""" + + def __init__(self, loc: Optional[SourceLocation]): + super().__init__("ThisExpression", loc) + + +class ArrayExpression(Expression): + """An array expression. An element might be `None` if it represents a hole in a sparse array. E.g. ``[1,,2]``.""" + + def __init__( + self, + loc: Optional[SourceLocation], + elements: List[Union[Expression, SpreadElement, None]], + ): + super().__init__("ArrayExpression", loc) + self.elements = elements + + +class ObjectExpression(Expression): + """An object expression.""" + + def __init__(self, loc: Optional[SourceLocation], properties: List[Property]): + super().__init__("ObjectExpression", loc) + self.properties = properties + + +class FunctionExpression(Function, Expression): + """A function expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + function_id: Optional[Identifier], + params: List[Pattern], + body: FunctionBody, + ): + super().__init__("FunctionExpression", loc, function_id, params, body) + + +class ArrowFunctionExpression(Function, Expression): + """A fat arrow function expression, e.g., ``let foo = (bar) => { /* body */ }``.""" + + def __init__( + self, + loc: Optional[SourceLocation], + params: List[Pattern], + body: Union[FunctionBody, Expression], + expression: bool, + ): + super().__init__("ArrowFunctionExpression", loc, None, params, body) + self.expression = expression + + +class UnaryExpression(Expression): + """A unary operator expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + operator: UnaryOperator, + prefix: bool, + argument: Expression, + ): + super().__init__("UnaryExpression", loc) + self.operator = operator + self.prefix = prefix + self.argument = argument + + +class UpdateExpression(Expression): + """An update (increment or decrement) operator expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + operator: UpdateOperator, + argument: Expression, + prefix: bool, + ): + super().__init__("UpdateExpression", loc) + self.operator = operator + self.argument = argument + self.prefix = prefix + + +class BinaryExpression(Expression): + """A binary operator expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + operator: BinaryOperator, + left: Expression, + right: Expression, + ): + super().__init__("BinaryExpression", loc) + self.operator = operator + self.left = left + self.right = right + + +class AssignmentExpression(Expression): + """An assignment operator expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + operator: AssignmentOperator, + left: Union[ + Pattern, Expression + ], # Left for backwards compatibility with pre-ES6 code, should be `Pattern` + right: Expression, + ): + super().__init__("AssignmentExpression", loc) + self.operator = operator + self.left = left + self.right = right + + +class LogicalExpression(Expression): + """A logical operator expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + operator: LogicalOperator, + left: Union[Pattern, Expression], + right: Expression, + ): + super().__init__("LogicalExpression", loc) + self.operator = operator + self.left = left + self.right = right + + +class MemberExpression(Expression, Pattern): + """A member expression. If `computed` is ``True``, the node corresponds to a computed (``a[b]``) member + expression and `property` is an `Expression`. If `computed` is `False`, the node corresponds to a static + (``a.b``) member expression and `property` is an `Identifier`. """ + + def __init__( + self, + loc: Optional[SourceLocation], + member_object: Union[Expression, Super], + member_property: Expression, + computed: bool, + ): + super().__init__("MemberExpression", loc) + self.object = member_object + self.property = member_property + self.computed = computed + + +class ConditionalExpression(Expression): + """A conditional expression, i.e., a ternary ``?``/``:`` expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + test: Expression, + alternate: Expression, + consequent: Expression, + ): + super().__init__("ConditionalExpression", loc) + self.test = test + self.alternate = alternate + self.consequent = consequent + + +class CallExpression(Expression): + """A function or method call expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + callee: Union[Expression, Super], + arguments: List[Union[Expression, SpreadElement]], + ): + super().__init__("CallExpression", loc) + self.callee = callee + self.arguments = arguments + + +class NewExpression(Expression): + """A ``new`` expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + callee: Expression, + arguments: List[Union[Expression, SpreadElement]], + ): + super().__init__("NewExpression", loc) + self.callee = callee + self.arguments = arguments + + +class SequenceExpression(Expression): + """A sequence expression, i.e., a comma-separated sequence of expressions.""" + + def __init__(self, loc: Optional[SourceLocation], expressions: List[Expression]): + super().__init__("SequenceExpression", loc) + self.expressions = expressions + + +def _generate_unary_expression(operator: UnaryOperator, docstring: str): + """Internal function to generate unary expression AST node. + + Implying that all UnaryExpression nodes are prefix. + """ + + class Expr(UnaryExpression): + __doc__ = docstring + + def __init__(self, loc: Optional[SourceLocation], argument: Expression): + super().__init__(loc, operator, True, argument) + + return Expr + + +def _generate_update_expression(operator: UpdateOperator, prefix: bool, docstring: str): + """Internal function to generate update expression AST node.""" + + class Expr(UpdateExpression): + __doc__ = docstring + + def __init__(self, loc: Optional[SourceLocation], argument: Expression): + super().__init__(loc, operator, argument, prefix) + + return Expr + + +def _generate_binary_expression(operator: BinaryOperator, docstring: str): + """Internal function to generate binary expression AST node.""" + + class Expr(BinaryExpression): + __doc__ = docstring + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, operator, left, right) + + return Expr + + +def _generate_assignment_expression(operator: AssignmentOperator, docstring: str): + """Internal function to generate assignment expression AST node.""" + + class Expr(AssignmentExpression): + __doc__ = docstring + + def __init__( + self, + loc: Optional[SourceLocation], + left: Union[Pattern, Expression], + right: Expression, + ): + super().__init__(loc, operator, left, right) + + return Expr + + +def _generate_logical_expression(operator: LogicalOperator, docstring: str): + """Internal function to generate logical expression AST node.""" + + class Expr(LogicalExpression): + __doc__ = docstring + + def __init__( + self, + loc: Optional[SourceLocation], + left: Union[Pattern, Expression], + right: Expression, + ): + super().__init__(loc, operator, left, right) + + return Expr + + +UnaryMinusExpression = _generate_unary_expression( + UnaryOperator.MINUS, """A unary minus expression.""" +) +UnaryPlusExpression = _generate_unary_expression( + UnaryOperator.PLUS, """A unary plus expression.""" +) +UnaryLogicNotExpression = _generate_unary_expression( + UnaryOperator.NOT_LOGIC, """A unary logic "not" expression.""" +) +UnaryBitNotExpression = _generate_unary_expression( + UnaryOperator.NOT_BIT, """A unary bit "not" expression.""" +) +TypeofExpression = _generate_unary_expression( + UnaryOperator.TYPEOF, """A `typeof` expression.""" +) +VoidExpression = _generate_unary_expression( + UnaryOperator.VOID, """A `void` expression.""" +) +DeleteExpression = _generate_unary_expression( + UnaryOperator.DELETE, """A `delete` expression.""" +) +PreIncrementExpression = _generate_update_expression( + UpdateOperator.INCREMENT, True, """A pre-increment expression.""" +) +PostIncrementExpression = _generate_update_expression( + UpdateOperator.INCREMENT, False, """A post-increment expression.""" +) +PreDecrementExpression = _generate_update_expression( + UpdateOperator.DECREMENT, True, """A pre-decrement expression.""" +) +PostDecrementExpression = _generate_update_expression( + UpdateOperator.DECREMENT, False, """A post-decrement expression.""" +) +EqualityExpression = _generate_binary_expression( + BinaryOperator.EQ, """An equality expression.""" +) +NotEqualityExpression = _generate_binary_expression( + BinaryOperator.NEQ, """A "not equality" expression.""" +) +IdentityEqualityExpression = _generate_binary_expression( + BinaryOperator.EQ_IDENTITY, """An identity equality expression.""" +) +NotIdentityEqualityExpression = _generate_binary_expression( + BinaryOperator.NEQ_IDENTITY, """A "not identity equality" expression.""" +) +LowerThanRelationExpression = _generate_binary_expression( + BinaryOperator.LT, """A "lower than" expression.""" +) +LowerThanEqualRelationExpression = _generate_binary_expression( + BinaryOperator.LTE, """A "lower than or equal" expression.""" +) +GreaterThanRelationExpression = _generate_binary_expression( + BinaryOperator.GT, """A "greater than" expression.""" +) +GreaterThanEqualRelationExpression = _generate_binary_expression( + BinaryOperator.GTE, """A "greater than or equal" expression.""" +) +LeftBitShiftExpression = _generate_binary_expression( + BinaryOperator.SHL, """A "left bit shift" expression.""" +) +RightBitShiftExpression = _generate_binary_expression( + BinaryOperator.SHR, """A "right bit shift" expression.""" +) +LogicRightBitShiftExpression = _generate_binary_expression( + BinaryOperator.SHR_LOGIC, """A "logical right bit shift" expression.""" +) +AddArithmeticExpression = _generate_binary_expression( + BinaryOperator.ADD, """An addition arithmetical expression.""" +) +SubArithmeticExpression = _generate_binary_expression( + BinaryOperator.SUB, """A subtraction arithmetical expression.""" +) +MulArithmeticExpression = _generate_binary_expression( + BinaryOperator.MUL, """A multiplication arithmetical expression.""" +) +DivArithmeticExpression = _generate_binary_expression( + BinaryOperator.DIV, """A division arithmetical expression.""" +) +ModArithmeticExpression = _generate_binary_expression( + BinaryOperator.MOD, """A modulo arithmetical expression.""" +) +OrBitExpression = _generate_binary_expression( + BinaryOperator.OR, """An "or" bit expression.""" +) +XorBitExpression = _generate_binary_expression( + BinaryOperator.XOR, """A "xor" bit expression.""" +) +AndBitExpression = _generate_binary_expression( + BinaryOperator.AND, """An "and" bit expression.""" +) +InExpression = _generate_binary_expression(BinaryOperator.IN, """An "in" expression.""") +InstanceofExpression = _generate_binary_expression( + BinaryOperator.INSTANCEOF, """An "instanceof" expression.""" +) +SimpleAssignExpression = _generate_assignment_expression( + AssignmentOperator.ASSIGN, """An assignment done with operator ``=`` expression.""" +) +AddAssignExpression = _generate_assignment_expression( + AssignmentOperator.ADD, + """An addition assignment done with operator ``+=`` expression.""", +) +SubAssignExpression = _generate_assignment_expression( + AssignmentOperator.SUB, + """A subtraction assignment done with operator ``-=`` expression.""", +) +MulAssignExpression = _generate_assignment_expression( + AssignmentOperator.MUL, + """A multiplication assignment done with operator ``*=`` expression.""", +) +ModAssignExpression = _generate_assignment_expression( + AssignmentOperator.DIV, + """A modulo assignment done with operator ``%=`` expression.""", +) +ShlAssignExpression = _generate_assignment_expression( + AssignmentOperator.SHL, + """A left shift assignment done with operator ``<<=`` expression.""", +) +ShrAssignExpression = _generate_assignment_expression( + AssignmentOperator.SHR, + """A right shift assignment done with operator ``>>=`` expression.""", +) +LogicShrAssignExpression = _generate_assignment_expression( + AssignmentOperator.SHR_LOGIC, + """A logical right shift assignment done with operator ``>>>=`` expression.""", +) +OrAssignExpression = _generate_assignment_expression( + AssignmentOperator.OR, + """A "bit or" assignment done with operator ``|=`` expression.""", +) +XorAssignExpression = _generate_assignment_expression( + AssignmentOperator.XOR, + """A "bit xor" assignment done with operator ``^=`` expression.""", +) +AndAssignExpression = _generate_assignment_expression( + AssignmentOperator.AND, + """A "bit and" assignment done with operator ``&=`` expression.""", +) +OrLogicExpression = _generate_logical_expression( + LogicalOperator.OR, """An "or" logical expression.""" +) +AndLogicExpression = _generate_logical_expression( + LogicalOperator.AND, """An "and" logical expression.""" +) + + +# "Property" block + + +class Property(Node): + """A literal property in an object expression can have either a string or number as its `value`. Ordinary + property initializers have a `kind` value ``"init"``; getters and setters have the kind values ``"get"`` and + ``"set"``, respectively. """ + + def __init__( + self, + loc: Optional[SourceLocation], + key: Union[Literal, Identifier], + value: Expression, + kind: PropKind, + method: bool, + shorthand: bool, + computed: bool, + ): + super().__init__("Property", loc) + self.key = key + self.value = value + self.kind = kind + self.method = method + self.shorthand = shorthand + self.computed = computed + + +class AssignmentProperty(Property): + def __init__( + self, + loc: Optional[SourceLocation], + key: Union[Literal, Identifier], + value: Pattern, + shorthand: bool, + computed: bool, + ): + super().__init__(loc, key, value, "init", False, shorthand, computed) + + +# "Patterns" block +# +# Destructuring binding and assignment are not part of ES5, but all binding positions accept Pattern +# to allow for destructuring in ES6. Nevertheless, for ES5, the only Pattern subtype is Identifier. + + +class Pattern(Node): + """A pattern.""" + + def __init__(self, node_type: str, loc: Optional[SourceLocation]): + super().__init__(node_type, loc) + + +class ObjectPatternKeyValue(TypedDict): + key: Union[Literal, Identifier] + value: Pattern + + +class ObjectPattern(Pattern): + def __init__( + self, loc: Optional[SourceLocation], properties: List[ObjectPatternKeyValue] + ): + super().__init__("ObjectPattern", loc) + self.properties = properties + + +class ArrayPattern(Pattern): + def __init__( + self, loc: Optional[SourceLocation], elements: List[Optional[Pattern]] + ): + super().__init__("ArrayPattern", loc) + self.elements = elements diff --git a/jasminesnake/ast/nodes/__init__.py b/jasminesnake/ast/nodes/__init__.py deleted file mode 100644 index ed63f0b..0000000 --- a/jasminesnake/ast/nodes/__init__.py +++ /dev/null @@ -1,73 +0,0 @@ -"""The module with AST nodes declaration. They are ESTree compliant. - -The module lacks support of: - * ES5 features: - * labelled statements - * switch statements - * try-catch statements - * debugger statement - * with statement - * RegExp - * ES6+ - -More about ESTree standard: -https://github.com/estree/estree/ - -Todo: - * Add support for lacking features -""" - -from typing import Optional, Union - - -class Position: - """The class for an object consisting of a line number (1-indexed) and a column number (0-indexed).""" - - def __init__(self, line: int, column: int): - if line < 1 or column < 0: - raise ValueError( - "L{}:C{} is not valid ESTree position!".format(line, column) - ) - - self.line = line - self.column = column - - -class SourceLocation: - """ - The class for the source location information of a node. - - Consists of a start position (the position of the first character of the parsed source region) and an end - position (the position of the first character after the parsed source region). - - See Also: - Position - """ - - def __init__(self, source: Optional[str], start: Position, end: Position): - self.source = source - self.start = start - self.end = end - - -class Node: - """ESTree AST nodes are represented as Node objects, which may have any prototype inheritance but which implement - this interface. - - The `type` field is a string representing the AST variant type. Each subtype of `Node` is documented below with - the specific string of its `type` field. You can use this field to determine which interface a node implements. - - The `loc` field represents the source location information of the node. If the node contains no information about - the source location, the field is `None`; otherwise it contains a `SourceLocation` object. - - See Also: - SourceLocation - """ - - def __init__(self, node_type: str, loc: Optional[SourceLocation]): - self.type = node_type - self.loc = loc - - -number = Union[int, float] -"""A type union consisting of int and float Python types. Consider it as Number type from JavaScript.""" diff --git a/jasminesnake/ast/nodes/declarations.py b/jasminesnake/ast/nodes/declarations.py deleted file mode 100644 index be14894..0000000 --- a/jasminesnake/ast/nodes/declarations.py +++ /dev/null @@ -1,52 +0,0 @@ -"""The module of AST nodes for declarations.""" - -from typing import List - -from . import * -from .identifiers import Identifier -from .patterns import Pattern -from .statements import Statement, FunctionBody -from .functions import Function - - -class Declaration(Statement): - """Any declaration node. Note that declarations are considered statements; this is because declarations can - appear in any statement context. """ - - def __init__(self, node_type: str, loc: Optional[SourceLocation]): - super().__init__(node_type, loc) - - -class FunctionDeclaration(Function, Declaration): - """A function declaration. Note that unlike in the parent interface `Function`, the `id` cannot be `None`.""" - - def __init__( - self, - loc: Optional[SourceLocation], - function_id: Identifier, - params: List[Pattern], - body: FunctionBody, - ): - super().__init__("FunctionDeclaration", loc, function_id, params, body) - - -class VariableDeclarator(Node): - """A variable declarator.""" - - def __init__( - self, loc: Optional[SourceLocation], var_id: Pattern, init: Optional[Exception] - ): - super().__init__("VariableDeclarator", loc) - self.id = var_id - self.init = init - - -class VariableDeclaration(Declaration): - """A variable declaration.""" - - def __init__( - self, loc: Optional[SourceLocation], declarations: List[VariableDeclarator] - ): - super().__init__("VariableDeclaration", loc) - self.declarations = declarations - self.kind = "var" diff --git a/jasminesnake/ast/nodes/expressions.py b/jasminesnake/ast/nodes/expressions.py deleted file mode 100644 index 37b2717..0000000 --- a/jasminesnake/ast/nodes/expressions.py +++ /dev/null @@ -1,669 +0,0 @@ -"""The module of AST nodes for expressions.""" - -from typing import List, Literal as TypeLiteral - -from . import * -from .functions import Function -from .identifiers import Identifier -from .literals import Literal -from .operator_enums import ( - UnaryOperator, - UpdateOperator, - BinaryOperator, - AssignmentOperator, - LogicalOperator, -) -from .patterns import Pattern -from .statements import FunctionBody - - -class Expression(Node): - """Any expression node. Since the left-hand side of an assignment may be any expression in general, an expression - can also be a pattern. - - See Also: - Pattern - """ - - def __init__(self, node_type: str, loc: Optional[SourceLocation]): - super().__init__(node_type, loc) - - -class ThisExpression(Expression): - """A `this` expression.""" - - def __init__(self, loc: Optional[SourceLocation]): - super().__init__("ThisExpression", loc) - - -class ArrayExpression(Expression): - """An array expression. An element might be `None` if it represents a hole in a sparse array. E.g. ``[1,,2]``.""" - - def __init__( - self, loc: Optional[SourceLocation], elements: List[Optional[Expression]] - ): - super().__init__("ArrayExpression", loc) - self.elements = elements - - -PropKind = TypeLiteral["init", "get", "set"] -"""A type for a `kind` field of `Property`.""" - - -class Property(Node): - """A literal property in an object expression can have either a string or number as its `value`. Ordinary - property initializers have a `kind` value ``"init"``; getters and setters have the kind values ``"get"`` and - ``"set"``, respectively. """ - - def __init__( - self, - loc: Optional[SourceLocation], - key: Union[Literal, Identifier], - value: Expression, - kind: PropKind, - ): - super().__init__("Property", loc) - self.key = key - self.value = value - self.kind = kind - - -class ObjectExpression(Expression): - """An object expression.""" - - def __init__(self, loc: Optional[SourceLocation], properties: List[Property]): - super().__init__("ObjectExpression", loc) - self.properties = properties - - -class FunctionExpression(Function, Expression): - """A function expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - function_id: Optional[Identifier], - params: List[Pattern], - body: FunctionBody, - ): - super().__init__("FunctionExpression", loc, function_id, params, body) - - -class UnaryExpression(Expression): - """A unary operator expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - operator: UnaryOperator, - prefix: bool, - argument: Expression, - ): - super().__init__("UnaryExpression", loc) - self.operator = operator - self.prefix = prefix - self.argument = argument - - -class UpdateExpression(Expression): - """An update (increment or decrement) operator expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - operator: UpdateOperator, - argument: Expression, - prefix: bool, - ): - super().__init__("UpdateExpression", loc) - self.operator = operator - self.argument = argument - self.prefix = prefix - - -class BinaryExpression(Expression): - """A binary operator expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - operator: BinaryOperator, - left: Expression, - right: Expression, - ): - super().__init__("BinaryExpression", loc) - self.operator = operator - self.left = left - self.right = right - - -class AssignmentExpression(Expression): - """An assignment operator expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - operator: AssignmentOperator, - left: Union[Pattern, Expression], - right: Expression, - ): - super().__init__("AssignmentExpression", loc) - self.operator = operator - self.left = left - self.right = right - - -class LogicalExpression(Expression): - """A logical operator expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - operator: LogicalOperator, - left: Union[Pattern, Expression], - right: Expression, - ): - super().__init__("LogicalExpression", loc) - self.operator = operator - self.left = left - self.right = right - - -class UnaryMinusExpression(UnaryExpression): - """A unary minus expression.""" - - def __init__(self, loc: Optional[SourceLocation], argument: Expression): - super().__init__(loc, UnaryOperator.MINUS, True, argument) - - -class UnaryPlusExpression(UnaryExpression): - """A unary plus expression.""" - - def __init__(self, loc: Optional[SourceLocation], argument: Expression): - super().__init__(loc, UnaryOperator.PLUS, True, argument) - - -class UnaryLogicNotExpression(UnaryExpression): - """A unary logic "not" expression.""" - - def __init__(self, loc: Optional[SourceLocation], argument: Expression): - super().__init__(loc, UnaryOperator.NOT_LOGIC, True, argument) - - -class UnaryBitNotExpression(UnaryExpression): - """A unary bit "not" expression.""" - - def __init__(self, loc: Optional[SourceLocation], argument: Expression): - super().__init__(loc, UnaryOperator.NOT_BIT, True, argument) - - -class TypeofExpression(UnaryExpression): - """A `typeof` expression.""" - - def __init__(self, loc: Optional[SourceLocation], argument: Expression): - super().__init__(loc, UnaryOperator.TYPEOF, True, argument) - - -class VoidExpression(UnaryExpression): - """A `void` expression.""" - - def __init__(self, loc: Optional[SourceLocation], argument: Expression): - super().__init__(loc, UnaryOperator.VOID, True, argument) - - -class DeleteExpression(UnaryExpression): - """A `delete` expression.""" - - def __init__(self, loc: Optional[SourceLocation], argument: Expression): - super().__init__(loc, UnaryOperator.DELETE, True, argument) - - -class PreIncrementExpression(UpdateExpression): - """A pre-increment expression.""" - - def __init__( - self, loc: Optional[SourceLocation], argument: Expression, - ): - super().__init__(loc, UpdateOperator.INCREMENT, argument, True) - - -class PostIncrementExpression(UpdateExpression): - """A post-increment expression.""" - - def __init__( - self, loc: Optional[SourceLocation], argument: Expression, - ): - super().__init__(loc, UpdateOperator.INCREMENT, argument, False) - - -class PreDecrementExpression(UpdateExpression): - """A pre-decrement expression.""" - - def __init__( - self, loc: Optional[SourceLocation], argument: Expression, - ): - super().__init__(loc, UpdateOperator.DECREMENT, argument, True) - - -class PostDecrementExpression(UpdateExpression): - """A post-decrement expression.""" - - def __init__( - self, loc: Optional[SourceLocation], argument: Expression, - ): - super().__init__(loc, UpdateOperator.DECREMENT, argument, False) - - -class EqualityExpression(BinaryExpression): - """An equality expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.EQ, left, right) - - -class NotEqualityExpression(BinaryExpression): - """A "not equality" expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.NEQ, left, right) - - -class IdentityEqualityExpression(BinaryExpression): - """An identity equality expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.EQ_IDENTITY, left, right) - - -class NotIdentityEqualityExpression(BinaryExpression): - """A "not identity equality" expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.NEQ_IDENTITY, left, right) - - -class LowerThanRelationExpression(BinaryExpression): - """A "lower than" expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.LT, left, right) - - -class LowerThanEqualRelationExpression(BinaryExpression): - """A "lower than or equal" expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.LTE, left, right) - - -class GreaterThanRelationExpression(BinaryExpression): - """A "greater than" expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.GT, left, right) - - -class GreaterThanEqualRelationExpression(BinaryExpression): - """A "greater than or equal" expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.GTE, left, right) - - -class LeftBitShiftExpression(BinaryExpression): - """A "left bit shift" expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.SHL, left, right) - - -class RightBitShiftExpression(BinaryExpression): - """A "right bit shift" expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.SHR, left, right) - - -class LogicRightBitShiftExpression(BinaryExpression): - """A "logical right bit shift" expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.SHR_LOGIC, left, right) - - -class AddArithmeticExpression(BinaryExpression): - """An addition arithmetical expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.ADD, left, right) - - -class SubArithmeticExpression(BinaryExpression): - """A subtraction arithmetical expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.SUB, left, right) - - -class MulArithmeticExpression(BinaryExpression): - """A multiplication arithmetical expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.MUL, left, right) - - -class DivArithmeticExpression(BinaryExpression): - """A division arithmetical expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.DIV, left, right) - - -class ModArithmeticExpression(BinaryExpression): - """A modulo arithmetical expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.MOD, left, right) - - -class OrBitExpression(BinaryExpression): - """An "or" bit expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.OR, left, right) - - -class XorBitExpression(BinaryExpression): - """A "xor" bit expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.XOR, left, right) - - -class AndBitExpression(BinaryExpression): - """An "and" bit expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.AND, left, right) - - -class InExpression(BinaryExpression): - """An "in" expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.IN, left, right) - - -class InstanceofExpression(BinaryExpression): - """An "instanceof" expression.""" - - def __init__( - self, loc: Optional[SourceLocation], left: Expression, right: Expression - ): - super().__init__(loc, BinaryOperator.INSTANCEOF, left, right) - - -class SimpleAssignExpression(AssignmentExpression): - """An assignment done with operator ``=`` expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - left: Union[Pattern, Expression], - right: Expression, - ): - super().__init__(loc, AssignmentOperator.ASSIGN, left, right) - - -class AddAssignExpression(AssignmentExpression): - """An addition assignment done with operator ``+=`` expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - left: Union[Pattern, Expression], - right: Expression, - ): - super().__init__(loc, AssignmentOperator.ADD, left, right) - - -class SubAssignExpression(AssignmentExpression): - """A subtraction assignment done with operator ``-=`` expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - left: Union[Pattern, Expression], - right: Expression, - ): - super().__init__(loc, AssignmentOperator.SUB, left, right) - - -class MulAssignExpression(AssignmentExpression): - """A multiplication assignment done with operator ``*=`` expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - left: Union[Pattern, Expression], - right: Expression, - ): - super().__init__(loc, AssignmentOperator.MUL, left, right) - - -class ModAssignExpression(AssignmentExpression): - """A modulo assignment done with operator ``%=`` expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - left: Union[Pattern, Expression], - right: Expression, - ): - super().__init__(loc, AssignmentOperator.MOD, left, right) - - -class ShlAssignExpression(AssignmentExpression): - """A left shift assignment done with operator ``<<=`` expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - left: Union[Pattern, Expression], - right: Expression, - ): - super().__init__(loc, AssignmentOperator.SHL, left, right) - - -class ShrAssignExpression(AssignmentExpression): - """A right shift assignment done with operator ``>>=`` expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - left: Union[Pattern, Expression], - right: Expression, - ): - super().__init__(loc, AssignmentOperator.SHR, left, right) - - -class LogicShrAssignExpression(AssignmentExpression): - """A logical right shift assignment done with operator ``>>>=`` expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - left: Union[Pattern, Expression], - right: Expression, - ): - super().__init__(loc, AssignmentOperator.SHR_LOGIC, left, right) - - -class OrAssignExpression(AssignmentExpression): - """A "bit or" assignment done with operator ``|=`` expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - left: Union[Pattern, Expression], - right: Expression, - ): - super().__init__(loc, AssignmentOperator.OR, left, right) - - -class XorAssignExpression(AssignmentExpression): - """A "bit xor" assignment done with operator ``^=`` expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - left: Union[Pattern, Expression], - right: Expression, - ): - super().__init__(loc, AssignmentOperator.XOR, left, right) - - -class AndAssignExpression(AssignmentExpression): - """A "bit and" assignment done with operator ``&=`` expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - left: Union[Pattern, Expression], - right: Expression, - ): - super().__init__(loc, AssignmentOperator.AND, left, right) - - -class OrLogicExpression(LogicalExpression): - """An "or" logical expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - left: Union[Pattern, Expression], - right: Expression, - ): - super().__init__(loc, LogicalOperator.OR, left, right) - - -class AndLogicExpression(LogicalExpression): - """An "and" logical expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - left: Union[Pattern, Expression], - right: Expression, - ): - super().__init__(loc, LogicalOperator.AND, left, right) - - -class MemberExpression(Expression, Pattern): - """A member expression. If `computed` is ``True``, the node corresponds to a computed (``a[b]``) member - expression and `property` is an `Expression`. If `computed` is `False`, the node corresponds to a static - (``a.b``) member expression and `property` is an `Identifier`. """ - - def __init__( - self, - loc: Optional[SourceLocation], - member_object: Expression, - member_property: Expression, - computed: bool, - ): - super().__init__("MemberExpression", loc) - self.object = member_object - self.property = member_property - self.computed = computed - - -class ConditionalExpression(Expression): - """A conditional expression, i.e., a ternary ``?``/``:`` expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - test: Expression, - alternate: Expression, - consequent: Expression, - ): - super().__init__("ConditionalExpression", loc) - self.test = test - self.alternate = alternate - self.consequent = consequent - - -class CallExpression(Expression): - """A function or method call expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - callee: Expression, - arguments: List[Expression], - ): - super().__init__("CallExpression", loc) - self.callee = callee - self.arguments = arguments - - -class NewExpression(Expression): - """A ``new`` expression.""" - - def __init__( - self, - loc: Optional[SourceLocation], - callee: Expression, - arguments: List[Expression], - ): - super().__init__("NewExpression", loc) - self.callee = callee - self.arguments = arguments - - -class SequenceExpression(Expression): - """A sequence expression, i.e., a comma-separated sequence of expressions.""" - - def __init__(self, loc: Optional[SourceLocation], expressions: List[Expression]): - super().__init__("SequenceExpression", loc) - self.expressions = expressions diff --git a/jasminesnake/ast/nodes/functions.py b/jasminesnake/ast/nodes/functions.py deleted file mode 100644 index ecd55c5..0000000 --- a/jasminesnake/ast/nodes/functions.py +++ /dev/null @@ -1,31 +0,0 @@ -"""The module of AST nodes for functions.""" - -from typing import List - -from . import * -from .identifiers import Identifier -from .patterns import Pattern -from .statements import FunctionBody - - -class Function(Node): - """A function declaration or expression. - - See Also: - FunctionDeclaration - FunctionExpression - FunctionBody - """ - - def __init__( - self, - node_type: str, - loc: Optional[SourceLocation], - function_id: Optional[Identifier], - params: List[Pattern], - body: FunctionBody, - ): - super().__init__(node_type, loc) - self.id = function_id - self.params = params - self.body = body diff --git a/jasminesnake/ast/nodes/identifiers.py b/jasminesnake/ast/nodes/identifiers.py deleted file mode 100644 index dd03a02..0000000 --- a/jasminesnake/ast/nodes/identifiers.py +++ /dev/null @@ -1,13 +0,0 @@ -"""The module of AST nodes for identifiers.""" - -from . import * -from .patterns import Pattern -from .expressions import Expression - - -class Identifier(Expression, Pattern): - """An identifier. Note that an identifier may be an expression or a destructuring pattern.""" - - def __init__(self, loc: Optional[SourceLocation], name: str): - super(Identifier, self).__init__("Identifier", loc) - self.name = name diff --git a/jasminesnake/ast/nodes/literals.py b/jasminesnake/ast/nodes/literals.py deleted file mode 100644 index 4a15075..0000000 --- a/jasminesnake/ast/nodes/literals.py +++ /dev/null @@ -1,14 +0,0 @@ -"""The module of AST nodes for literals.""" - -from . import * -from .expressions import Expression - - -class Literal(Expression): - """A literal token. Note that a literal can be an expression.""" - - def __init__( - self, loc: Optional[SourceLocation], value: Union[str, bool, number, None] - ): - super().__init__("Literal", loc) - self.value = value diff --git a/jasminesnake/ast/nodes/operator_enums.py b/jasminesnake/ast/nodes/operator_enums.py deleted file mode 100644 index 8c299cb..0000000 --- a/jasminesnake/ast/nodes/operator_enums.py +++ /dev/null @@ -1,72 +0,0 @@ -"""The module representing enums for JavaScript operators.""" - -from enum import Enum - - -class UnaryOperator(Enum): - """A unary operator token.""" - - MINUS = "-" - PLUS = "+" - NOT_LOGIC = "!" - NOT_BIT = "~" - TYPEOF = "typeof" - VOID = "void" - DELETE = "delete" - - -class UpdateOperator(Enum): - """An update (increment or decrement) operator token.""" - - INCREMENT = "++" - DECREMENT = "--" - - -class BinaryOperator(Enum): - """A binary operator token.""" - - EQ = "==" - NEQ = "!=" - EQ_IDENTITY = "===" - NEQ_IDENTITY = "!==" - LT = "<" - LTE = "<=" - GT = ">" - GTE = ">=" - SHL = "<<" - SHR = ">>" - SHR_LOGIC = ">>>" - ADD = "+" - SUB = "-" - MUL = "*" - DIV = "/" - MOD = "%" - OR = "|" - XOR = "^" - AND = "&" - IN = "in" - INSTANCEOF = "instanceof" - - -class AssignmentOperator(Enum): - """An assignment operator token.""" - - ASSIGN = "=" - ADD = "+=" - SUB = "-=" - MUL = "*=" - DIV = "/=" - MOD = "%=" - SHL = "<<=" - SHR = ">>=" - SHR_LOGIC = ">>>=" - OR = "|=" - XOR = "^=" - AND = "&=" - - -class LogicalOperator(Enum): - """A logical operator token.""" - - OR = "||" - AND = "&&" diff --git a/jasminesnake/ast/nodes/patterns.py b/jasminesnake/ast/nodes/patterns.py deleted file mode 100644 index 51c9357..0000000 --- a/jasminesnake/ast/nodes/patterns.py +++ /dev/null @@ -1,39 +0,0 @@ -"""The module of AST nodes for patterns. - -Destructuring binding and assignment are not part of ES5, but all binding positions accept `Pattern` to allow for -destructuring in ES6. Nevertheless, for ES5, the only `Pattern` subtype is `Identifier`. - -See Also: - Identifier -""" - -from typing import TypedDict, Union, List -from . import * -from .literals import Literal -from .identifiers import Identifier - - -class Pattern(Node): - """A pattern.""" - - def __init__(self, node_type: str, loc: Optional[SourceLocation]): - super().__init__(node_type, loc) - - -class ObjectKeyValue(TypedDict): - key: Union[Literal, Identifier] - value: Pattern - - -class ObjectPattern(Pattern): - def __init__(self, loc: Optional[SourceLocation], properties: List[ObjectKeyValue]): - super().__init__("ObjectPattern", loc) - self.properties = properties - - -class ArrayPattern(Pattern): - def __init__( - self, loc: Optional[SourceLocation], elements: List[Optional[Pattern]] - ): - super().__init__("ArrayPattern", loc) - self.elements = elements diff --git a/jasminesnake/ast/nodes/programs.py b/jasminesnake/ast/nodes/programs.py deleted file mode 100644 index 7577cc9..0000000 --- a/jasminesnake/ast/nodes/programs.py +++ /dev/null @@ -1,15 +0,0 @@ -"""The module of Program AST node.""" - -from typing import List -from . import * -from .statements import Statement, Directive - - -class Program(Node): - """A complete program source tree.""" - - def __init__( - self, loc: Optional[SourceLocation], body: List[Union[Directive, Statement]] - ): - super().__init__("Program", loc) - self.body = body diff --git a/jasminesnake/ast/nodes/statements.py b/jasminesnake/ast/nodes/statements.py deleted file mode 100644 index 25e6b64..0000000 --- a/jasminesnake/ast/nodes/statements.py +++ /dev/null @@ -1,158 +0,0 @@ -"""The module of AST nodes for statements.""" - -from typing import List - -from . import * -from .expressions import Expression -from .literals import Literal -from .identifiers import Identifier -from .declarations import VariableDeclaration -from .patterns import Pattern - - -class Statement(Node): - """Any statement.""" - - def __init__(self, node_type: str, loc: Optional[SourceLocation]): - super().__init__(node_type, loc) - - -class EmptyStatement(Statement): - """An empty statement, i.e., a solitary semicolon.""" - - def __init__(self, loc: Optional[SourceLocation]): - super().__init__("EmptyStatement", loc) - - -class BlockStatement(Statement): - """A block statement, i.e., a sequence of statements surrounded by braces.""" - - def __init__(self, loc: Optional[SourceLocation], body: List[Statement]): - super().__init__("BlockStatement", loc) - self.body = body - - -class ExpressionStatement(Statement): - """An expression statement, i.e., a statement consisting of a single expression.""" - - def __init__(self, loc: Optional[SourceLocation], expression: Expression): - super().__init__("ExpressionStatement", loc) - self.expression = expression - - -class Directive(Node): - """A directive from the directive prologue of a script or function. The `directive` property is the raw string - source of the directive without quotes. - """ - - def __init__( - self, loc: Optional[SourceLocation], expression: Literal, directive: str - ): - super().__init__("Directive", loc) - self.expression = expression - self.directive = directive - - -class FunctionBody(BlockStatement): - """The body of a function, which is a block statement that may begin with directives.""" - - def __init__( - self, loc: Optional[SourceLocation], body: List[Union[Directive, Statement]] - ): - super().__init__(loc, body) - - -class ReturnStatement(Statement): - """A `return` statement.""" - - def __init__(self, loc: Optional[SourceLocation], argument: Optional[Expression]): - super().__init__("ReturnStatement", loc) - self.argument = argument - - -class BreakStatement(Statement): - """A `break` statement.""" - - def __init__(self, loc: Optional[SourceLocation], label: Optional[Identifier]): - super().__init__("BreakStatement", loc) - self.label = label - - -class ContinueStatement(Statement): - """A `continue` statement.""" - - def __init__(self, loc: Optional[SourceLocation], label: Optional[Identifier]): - super().__init__("ContinueStatement", loc) - self.label = label - - -class IfStatement(Statement): - """An `if` statement.""" - - def __init__( - self, - loc: Optional[SourceLocation], - test: Expression, - consequent: Statement, - alternate: Optional[Statement], - ): - super().__init__("IfStatement", loc) - self.test = test - self.consequent = consequent - self.alternate = alternate - - -class WhileStatement(Statement): - """A `while` statement.""" - - def __init__( - self, loc: Optional[SourceLocation], test: Expression, body: Statement - ): - super().__init__("WhileStatement", loc) - self.test = test - self.body = body - - -class DoWhileStatement(Statement): - """A `do`/`while` statement.""" - - def __init__( - self, loc: Optional[SourceLocation], body: Statement, test: Expression - ): - super().__init__("DoWhileStatement", loc) - self.body = body - self.test = test - - -class ForStatement(Statement): - """A `for` statement.""" - - def __init__( - self, - loc: Optional[SourceLocation], - init: Union[VariableDeclaration, Expression, None], - test: Optional[Expression], - update: Optional[Expression], - body: Statement, - ): - super().__init__("ForStatement", loc) - self.init = init - self.test = test - self.update = update - self.body = body - - -class ForInStatement(Statement): - """A `for`/`in` statement.""" - - def __init__( - self, - loc: Optional[SourceLocation], - left: Union[VariableDeclaration, Pattern], - right: Expression, - body: Statement, - ): - super().__init__("ForInStatement", loc) - self.left = left - self.right = right - self.body = body diff --git a/jasminesnake/ast/parse_tree_listeners.py b/jasminesnake/ast/parse_tree_listeners.py new file mode 100644 index 0000000..955cc54 --- /dev/null +++ b/jasminesnake/ast/parse_tree_listeners.py @@ -0,0 +1,149 @@ +import logging +from typing import Optional, List +import antlr4.ParserRuleContext + +from lex.JavaScriptParser import JavaScriptParser +from lex.JavaScriptParserListener import JavaScriptParserListener as JSBaseListener + +import ast.nodes + + +def _get_source_location( + ctx: antlr4.ParserRuleContext, source: Optional[str] +) -> ast.nodes.SourceLocation: + """Internal function to obtain `SourceObject` from parser context.""" + start_pos = ast.nodes.Position(ctx.start.line, ctx.start.column) + end_pos = ast.nodes.Position(ctx.stop.line, ctx.stop.column) + + # If an end is not on a newline, shift end position column by 1 + # to match exact token end, not the last character + if end_pos.column != 0: + end_pos.column += 1 + + return ast.nodes.SourceLocation(source=source, start=start_pos, end=end_pos) + + +class StatementListener(JSBaseListener): + _stmt: ast.nodes.Statement + + @property + def statement(self) -> ast.nodes.Statement: + """Statement AST node generated after parse tree walking.""" + + return self._stmt + + def enterStatement(self, ctx: JavaScriptParser.StatementContext): + """Obtain an actual statement.""" + logging.debug("Entered section Statement") + ctx.getChild(0).enterRule(self) + + def enterBlock(self, ctx: JavaScriptParser.BlockContext): + """Listener for BlockStatement.""" + logging.debug("Entered section Block") + + stmt_list: List[ast.nodes.Statement] = [] + for stmt in ctx.statementList().children: + stmt_listener = StatementListener() + stmt.enterRule(stmt_listener) + stmt_list.append(stmt_listener.statement) + + loc = _get_source_location(ctx, None) # FIXME source param is None + self._stmt = ast.nodes.BlockStatement(loc, stmt_list) + + def enterVariableDeclarationList( + self, ctx: JavaScriptParser.VariableDeclarationListContext + ): + """Listener for VariableDeclaration.""" + logging.debug("Entered section VariableDeclaration") + pass + + def enterEmptyStatement(self, ctx: JavaScriptParser.EmptyStatementContext): + """Listener for EmptyStatement.""" + logging.debug("Entered section EmptyStatement") + pass + + def enterExpressionStatement( + self, ctx: JavaScriptParser.ExpressionStatementContext + ): + """Listener for ExpressionStatement. + TODO: check up expression containers. + """ + logging.debug("Entered section ExpressionStatement") + pass + + def enterIfStatement(self, ctx: JavaScriptParser.IfStatementContext): + """Listener for IfStatement.""" + logging.debug("Entered section IfStatement") + pass + + def enterFunctionDeclaration( + self, ctx: JavaScriptParser.FunctionDeclarationContext + ): + """Listener for FunctionDeclaration.""" + logging.debug("Entered section FunctionDeclaration") + pass + + # TODO: import/export, ClassDeclaration, iter statements, continue. break, return + + +class SourceElementListener(JSBaseListener): + """The proxy between Program and Statement.""" + + _elems: List[ast.nodes.Statement] = [] + + @property + def source_elements(self) -> List[ast.nodes.Statement]: + """Source elements AST nodes generated after parse tree walking.""" + + return self._elems + + def enterSourceElement(self, ctx: JavaScriptParser.SourceElementContext): + logging.debug("Entered section Source Element") + stmt_listener = StatementListener() + stmt = ctx.statement() + stmt.enterRule(stmt_listener) + self._elems.append(stmt_listener.statement) + + +class ASTListener(JSBaseListener): + """AST listener.""" + + _program_node: Optional[ast.nodes.Program] = None + _source_type: ast.nodes.SourceTypeLiteral + + @property + def program_node(self) -> ast.nodes.Program: + """The `Program` AST node generated after parse tree walking.""" + if self._program_node is None: + raise ValueError("Program AST node is None, did you run the listener?") + + return self._program_node + + def __init__(self, source_type: ast.nodes.SourceTypeLiteral = "script"): + """AST listener constructor. + + Args: + source_type (ast.nodes.SourceTypeLiteral): source type. Could be `script` or `module`. Set to + `script` by default. + """ + self._source_type = source_type + + def enterProgram(self, ctx: JavaScriptParser.ProgramContext): + logging.debug("Entered section Program") + logging.debug("JS source type: %s", self._source_type) + + hashbang = ctx.HashBangLine() + if hashbang is not None: + hashbang_exec = hashbang.getText()[2:] + logging.debug('Found a hashbang "%s"', hashbang_exec) + # TODO treat it somehow + + source_elem_listener = SourceElementListener() + + for elem in ctx.sourceElements().children: + elem.enterRule(source_elem_listener) + + loc = _get_source_location(ctx, None) # FIXME add source name + self._program_node = ast.nodes.Program( + loc, self._source_type, source_elem_listener.source_elements + ) diff --git a/jasminesnake/ast/visitors.py b/jasminesnake/ast/visitors.py deleted file mode 100644 index 07bb9b6..0000000 --- a/jasminesnake/ast/visitors.py +++ /dev/null @@ -1,12 +0,0 @@ -from lex import JavaScriptParserVisitor, JavaScriptParser -import ast.nodes - -JSBaseVisitor = JavaScriptParserVisitor.JavaScriptParserVisitor -JSParser = JavaScriptParser.JavaScriptParser - - -class JSASTVisitor(JSBaseVisitor): - def visitVariableDeclarationList( - self, ctx: JSParser.VariableDeclarationListContext - ): - pass diff --git a/jasminesnake/js_stream.py b/jasminesnake/js_stream.py new file mode 100644 index 0000000..0e12351 --- /dev/null +++ b/jasminesnake/js_stream.py @@ -0,0 +1,110 @@ +"""A module for JavaScript code stream creation and its parsing. """ +from antlr4 import InputStream, CommonTokenStream, FileStream, StdinStream +from antlr4.error.ErrorListener import ErrorListener + +from .lex import JavaScriptLexer, JavaScriptParser + +JSL = JavaScriptLexer.JavaScriptLexer +JSP = JavaScriptParser.JavaScriptParser + + +class JSBaseStream: + """JavaScript stream base class. + + Notes: + Do not instantiate the base class. + + See Also: + JSFileStream + JSStringStream + JSStdinStream + """ + + _input_stream: InputStream = None + _error_listener = None + lexer = None + parser = None + + def __init__(self, error_listener): + if self is JSBaseStream: + raise TypeError( + "JSReader is a base class, you should instantiate its subclasses instead." + ) + + self._error_listener = error_listener + + def parse(self) -> JSP.ProgramContext: + """Parse the stream. + + Returns: + Program context. + """ + self.lexer = JSL(self._input_stream) + stream = CommonTokenStream(self.lexer) + self.parser = JSP(stream) + + # Register error listener if present + if self._error_listener is not None: + self.parser.removeErrorListeners() + self.parser.addErrorListener(self._error_listener) + + return self.parser.program() + + +class JSStringStream(JSBaseStream): + """JavaScript string stream. + + See Also: + JSBaseStream + JSFileStream + JSStdinStream + """ + + def __init__(self, string: str, error_listener: ErrorListener = None): + """Instantiate a string stream. + + Args: + string (str): The string with JavaScript code. + error_listener (ErrorListener): The custom error listener. Uses default one if not set or set to None. + """ + super().__init__(error_listener) + self._input_stream = InputStream(string) + + +class JSStdinStream(JSBaseStream): + """JavaScript stdin stream. + + See Also: + JSBaseStream + JSFileStream + JSStringStream + """ + + def __init__(self, error_listener: ErrorListener = None): + """Instantiate a string stream. + + Args: + error_listener (ErrorListener): The custom error listener. Uses default one if not set or set to None. + """ + super().__init__(error_listener) + self._input_stream = StdinStream("utf-8") + + +class JSFileStream(JSBaseStream): + """JavaScript file stream. + + See Also: + JSBaseStream + JSStringStream + JSStdinStream + """ + + def __init__(self, path: str, error_listener: ErrorListener = None): + """Instantiate a string stream. + + Args: + path (str): The path to the file with JavaScript code. + error_listener (ErrorListener): The custom error listener. Uses default one if not set or set to None. + """ + super().__init__(error_listener) + self._input_stream = FileStream(path) diff --git a/jasminesnake/lex/ErrorListeners.py b/jasminesnake/lex/ErrorListeners.py new file mode 100644 index 0000000..b1332fe --- /dev/null +++ b/jasminesnake/lex/ErrorListeners.py @@ -0,0 +1,37 @@ +from antlr4.error.ErrorListener import ErrorListener +import logging + + +class LogErrorListener(ErrorListener): + def __init__(self): + super().__init__() + + def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e): + logging.debug( + "{}\n{}\n{}\n{}\n{}".format(offendingSymbol, line, column, msg, e) + ) + + def reportAmbiguity( + self, recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs + ): + logging.debug( + "{}\n{}\n{}\n{}\n{}\n{}".format( + dfa, startIndex, stopIndex, exact, ambigAlts, configs + ) + ) + + def reportAttemptingFullContext( + self, recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs + ): + logging.debug( + "{}; {}; {}; {}; {}".format( + dfa, startIndex, stopIndex, conflictingAlts, configs + ) + ) + + def reportContextSensitivity( + self, recognizer, dfa, startIndex, stopIndex, prediction, configs + ): + logging.debug( + "{}; {}; {}; {}; {}".format(dfa, startIndex, stopIndex, prediction, configs) + ) diff --git a/jasminesnake/lex/JavaScriptBaseLexer.py b/jasminesnake/lex/JavaScriptBaseLexer.py index 6f715ae..2c5729e 100644 --- a/jasminesnake/lex/JavaScriptBaseLexer.py +++ b/jasminesnake/lex/JavaScriptBaseLexer.py @@ -1,4 +1,5 @@ from antlr4 import * +import logging relativeImport = False if __name__ is not None and "." in __name__: @@ -7,7 +8,7 @@ if __name__ is not None and "." in __name__: class JavaScriptBaseLexer(Lexer): def __init__(self, *args, **kwargs): - print("JavaScriptBaseLexerInit") + logging.debug("JavaScriptBaseLexerInit") super(JavaScriptBaseLexer, self).__init__(*args, **kwargs) """Stores values of nested modes. By default mode is strict or diff --git a/jasminesnake/lex/JavaScriptBaseParser.py b/jasminesnake/lex/JavaScriptBaseParser.py index 1d977b5..e6ec82b 100644 --- a/jasminesnake/lex/JavaScriptBaseParser.py +++ b/jasminesnake/lex/JavaScriptBaseParser.py @@ -1,4 +1,5 @@ from antlr4 import * +import logging relativeImport = False if __name__ is not None and "." in __name__: diff --git a/requirements.txt b/requirements.txt index a7cddca..3dd26e5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,4 @@ -antlr4-python3-runtime -colorama==0.4.3 \ No newline at end of file +antlr4-python3-runtime==4.8 +colorama==0.4.3 +coloredlogs==14.0 +tree_format==0.1.2 \ No newline at end of file