mirror of https://github.com/t1meshift/js.git
				
				
				
			Compare commits
	
		
			12 Commits 
		
	
	
		
			041f4c31fb
			...
			c504830928
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
									
								
								 | 
						c504830928 | |
| 
							
							
								
									
								
								 | 
						85aa1b2a34 | |
| 
							
							
								
									
								
								 | 
						245bae4175 | |
| 
							
							
								
									
								
								 | 
						74d53ddf34 | |
| 
							
							
								
									
								
								 | 
						7a1eca4aca | |
| 
							
							
								
									
								
								 | 
						b4bbc8460e | |
| 
							
							
								
									
								
								 | 
						ace635c999 | |
| 
							
							
								
									
								
								 | 
						5266ebed21 | |
| 
							
							
								
									
								
								 | 
						afe6c8d244 | |
| 
							
							
								
									
								
								 | 
						ea5b03d93e | |
| 
							
							
								
									
								
								 | 
						42b4e40d9e | |
| 
							
							
								
									
								
								 | 
						6372a043a2 | 
| 
						 | 
				
			
			@ -10,7 +10,7 @@ import coloredlogs
 | 
			
		|||
from jasminesnake import __version__, __snake__, LOG_LEVELS
 | 
			
		||||
from .js_stream import JSBaseStream, JSStringStream, JSFileStream
 | 
			
		||||
from .lex.ErrorListeners import LogErrorListener
 | 
			
		||||
from ast import nodes, to_ascii_tree, from_parse_tree
 | 
			
		||||
from .ast import to_ascii_tree, from_parse_tree
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_argument_parser():
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,14 +4,14 @@ from enum import Enum
 | 
			
		|||
from typing import Union
 | 
			
		||||
from antlr4 import ParseTreeWalker
 | 
			
		||||
 | 
			
		||||
import lex.JavaScriptParser as Parser
 | 
			
		||||
import ast.nodes
 | 
			
		||||
import jasminesnake.lex.JavaScriptParser as Parser
 | 
			
		||||
from . import nodes
 | 
			
		||||
from .parse_tree_listeners import ASTListener
 | 
			
		||||
 | 
			
		||||
JSP = Parser.JavaScriptParser
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def from_parse_tree(tree: JSP.ProgramContext) -> ast.nodes.Program:
 | 
			
		||||
def from_parse_tree(tree: JSP.ProgramContext) -> nodes.Program:
 | 
			
		||||
    """Generate AST from ANTLR parse tree.
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +26,7 @@ def from_parse_tree(tree: JSP.ProgramContext) -> ast.nodes.Program:
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
def to_ascii_tree(
 | 
			
		||||
    node: Union[ast.nodes.Position, ast.nodes.SourceLocation, ast.nodes.Node],
 | 
			
		||||
    node: Union[nodes.Position, nodes.SourceLocation, nodes.Node],
 | 
			
		||||
    name_prefix: str = "",
 | 
			
		||||
    nesting_lvl: int = 0,
 | 
			
		||||
):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,20 +8,29 @@ The module lacks support of:
 | 
			
		|||
    * debugger statement
 | 
			
		||||
    * with statement
 | 
			
		||||
    * RegExp
 | 
			
		||||
 * ES6 features:
 | 
			
		||||
 * ES2015 features:
 | 
			
		||||
    * generators/yield statement
 | 
			
		||||
    * for-of statement
 | 
			
		||||
    * template literals
 | 
			
		||||
    * and other ES6 features :)
 | 
			
		||||
 * ES2017 features:
 | 
			
		||||
    * async/await.
 | 
			
		||||
      Basically, the whole standard consists of it, so no ES2017 support.
 | 
			
		||||
 * ES2018 features:
 | 
			
		||||
    * for-await-of statement
 | 
			
		||||
    * template literals
 | 
			
		||||
 * ES2019 features
 | 
			
		||||
    * catch binding omission.
 | 
			
		||||
      The only ES2019 feature.
 | 
			
		||||
 | 
			
		||||
More about ESTree standard:
 | 
			
		||||
https://github.com/estree/estree/
 | 
			
		||||
 | 
			
		||||
Todo:
 | 
			
		||||
    * Add support for lacking features
 | 
			
		||||
    * Make another attempt to split up this module
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from typing import List, Union, Optional, Literal as TypeLiteral, TypedDict, Any
 | 
			
		||||
from typing import List, Union, Optional, Literal as TypeLiteral, Any
 | 
			
		||||
from enum import Enum
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -30,8 +39,11 @@ from collections import OrderedDict
 | 
			
		|||
 | 
			
		||||
# 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."""
 | 
			
		||||
number = float
 | 
			
		||||
"""A type representing Number type in JavaScript."""
 | 
			
		||||
 | 
			
		||||
bigint = int
 | 
			
		||||
"""A type representing BigInt type in JavaScript."""
 | 
			
		||||
 | 
			
		||||
SourceTypeLiteral = TypeLiteral["script", "module"]
 | 
			
		||||
"""The type for the `sourceType` field."""
 | 
			
		||||
| 
						 | 
				
			
			@ -42,6 +54,9 @@ VarDeclKind = TypeLiteral["var", "let", "const"]
 | 
			
		|||
PropKind = TypeLiteral["init", "get", "set"]
 | 
			
		||||
"""A type for a `kind` field of `Property`."""
 | 
			
		||||
 | 
			
		||||
MethodDefinitionKind = TypeLiteral["constructor", "method", "get", "set"]
 | 
			
		||||
"""A type for a `kind` field of `MethodDefinition`."""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UnaryOperator(Enum):
 | 
			
		||||
    """A unary operator token."""
 | 
			
		||||
| 
						 | 
				
			
			@ -86,6 +101,7 @@ class BinaryOperator(Enum):
 | 
			
		|||
    AND = "&"
 | 
			
		||||
    IN = "in"
 | 
			
		||||
    INSTANCEOF = "instanceof"
 | 
			
		||||
    POW = "**"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AssignmentOperator(Enum):
 | 
			
		||||
| 
						 | 
				
			
			@ -103,6 +119,7 @@ class AssignmentOperator(Enum):
 | 
			
		|||
    OR = "|="
 | 
			
		||||
    XOR = "^="
 | 
			
		||||
    AND = "&="
 | 
			
		||||
    POW = "**="
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LogicalOperator(Enum):
 | 
			
		||||
| 
						 | 
				
			
			@ -110,6 +127,7 @@ class LogicalOperator(Enum):
 | 
			
		|||
 | 
			
		||||
    OR = "||"
 | 
			
		||||
    AND = "&&"
 | 
			
		||||
    NULLISH_COALESCING = "??"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Nodes forward declarations
 | 
			
		||||
| 
						 | 
				
			
			@ -145,6 +163,10 @@ class Identifier:
 | 
			
		|||
    ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Literal:
 | 
			
		||||
    ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# "Node objects" block
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -218,20 +240,6 @@ class Node:
 | 
			
		|||
        return self._fields
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# "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
 | 
			
		||||
        self._fields.update({"value": self.value})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# "Programs" block
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -559,7 +567,11 @@ class ArrayExpression(Expression):
 | 
			
		|||
class ObjectExpression(Expression):
 | 
			
		||||
    """An object expression."""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, loc: Optional[SourceLocation], properties: List[Property]):
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        loc: Optional[SourceLocation],
 | 
			
		||||
        properties: List[Union[Property, SpreadElement]],
 | 
			
		||||
    ):
 | 
			
		||||
        super().__init__("ObjectExpression", loc)
 | 
			
		||||
        self.properties = properties
 | 
			
		||||
        self._fields.update({"properties": self.properties})
 | 
			
		||||
| 
						 | 
				
			
			@ -954,6 +966,9 @@ InExpression = _generate_binary_expression(BinaryOperator.IN, """An "in" express
 | 
			
		|||
InstanceofExpression = _generate_binary_expression(
 | 
			
		||||
    BinaryOperator.INSTANCEOF, """An "instanceof" expression."""
 | 
			
		||||
)
 | 
			
		||||
PowBinaryExpression = _generate_binary_expression(
 | 
			
		||||
    BinaryOperator.POW, """A power expression, e.g. ``2**3``."""
 | 
			
		||||
)
 | 
			
		||||
SimpleAssignExpression = _generate_assignment_expression(
 | 
			
		||||
    AssignmentOperator.ASSIGN, """An assignment done with operator ``=`` expression."""
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -997,12 +1012,51 @@ AndAssignExpression = _generate_assignment_expression(
 | 
			
		|||
    AssignmentOperator.AND,
 | 
			
		||||
    """A "bit and" assignment done with operator ``&=`` expression.""",
 | 
			
		||||
)
 | 
			
		||||
PowAssignExpression = _generate_assignment_expression(
 | 
			
		||||
    AssignmentOperator.POW, """A power assignment expression, e.g. ``x**=2``."""
 | 
			
		||||
)
 | 
			
		||||
OrLogicExpression = _generate_logical_expression(
 | 
			
		||||
    LogicalOperator.OR, """An "or" logical expression."""
 | 
			
		||||
)
 | 
			
		||||
AndLogicExpression = _generate_logical_expression(
 | 
			
		||||
    LogicalOperator.AND, """An "and" logical expression."""
 | 
			
		||||
)
 | 
			
		||||
NullishCoalescingLogicExpression = _generate_logical_expression(
 | 
			
		||||
    LogicalOperator.NULLISH_COALESCING, """A nullish coalescing logical expression."""
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# "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, bigint, None],
 | 
			
		||||
    ):
 | 
			
		||||
        super().__init__("Literal", loc)
 | 
			
		||||
        self.value = value
 | 
			
		||||
        self._fields.update({"value": self.value})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BigIntLiteral(Literal):
 | 
			
		||||
    """`bigint` property is the string representation of the ``BigInt`` value. It doesn't include the suffix ``n``.
 | 
			
		||||
    In environments that don't support ``BigInt`` values, value property will be `None` as the ``BigInt`` value can't
 | 
			
		||||
    be represented natively.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        loc: Optional[SourceLocation],
 | 
			
		||||
        value: Union[str, bool, number, bigint, None],
 | 
			
		||||
        bigint: str,
 | 
			
		||||
    ):
 | 
			
		||||
        super().__init__(loc, value)
 | 
			
		||||
        self.bigint = bigint
 | 
			
		||||
        self._fields.update({"bigint": self.bigint})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# "Property" block
 | 
			
		||||
| 
						 | 
				
			
			@ -1016,7 +1070,7 @@ class Property(Node):
 | 
			
		|||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        loc: Optional[SourceLocation],
 | 
			
		||||
        key: Union[Literal, Identifier],
 | 
			
		||||
        key: Expression,
 | 
			
		||||
        value: Expression,
 | 
			
		||||
        kind: PropKind,
 | 
			
		||||
        method: bool,
 | 
			
		||||
| 
						 | 
				
			
			@ -1046,7 +1100,7 @@ class AssignmentProperty(Property):
 | 
			
		|||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        loc: Optional[SourceLocation],
 | 
			
		||||
        key: Union[Literal, Identifier],
 | 
			
		||||
        key: Expression,
 | 
			
		||||
        value: Pattern,
 | 
			
		||||
        shorthand: bool,
 | 
			
		||||
        computed: bool,
 | 
			
		||||
| 
						 | 
				
			
			@ -1067,14 +1121,18 @@ class Pattern(Node):
 | 
			
		|||
        super().__init__(node_type, loc)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ObjectPatternKeyValue(TypedDict):
 | 
			
		||||
    key: Union[Literal, Identifier]
 | 
			
		||||
    value: Pattern
 | 
			
		||||
class RestElement(Pattern):
 | 
			
		||||
    def __init__(self, loc: Optional[SourceLocation], argument: Pattern):
 | 
			
		||||
        super().__init__("RestElement", loc)
 | 
			
		||||
        self.argument = argument
 | 
			
		||||
        self._fields.update({"argument": self.argument})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ObjectPattern(Pattern):
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self, loc: Optional[SourceLocation], properties: List[ObjectPatternKeyValue]
 | 
			
		||||
        self,
 | 
			
		||||
        loc: Optional[SourceLocation],
 | 
			
		||||
        properties: List[Union[AssignmentProperty, RestElement]],
 | 
			
		||||
    ):
 | 
			
		||||
        super().__init__("ObjectPattern", loc)
 | 
			
		||||
        self.properties = properties
 | 
			
		||||
| 
						 | 
				
			
			@ -1090,6 +1148,14 @@ class ArrayPattern(Pattern):
 | 
			
		|||
        self._fields.update({"elements": self.elements})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AssignmentPattern(Pattern):
 | 
			
		||||
    def __init__(self, loc: Optional[SourceLocation], left: Pattern, right: Expression):
 | 
			
		||||
        super().__init__("AssignmentPattern", loc)
 | 
			
		||||
        self.left = left
 | 
			
		||||
        self.right = right
 | 
			
		||||
        self._fields.update({"left": self.left, "right": self.right})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# "Identifier" block
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1099,3 +1165,281 @@ class Identifier(Expression, Pattern):
 | 
			
		|||
    def __init__(self, loc: Optional[SourceLocation], name: str):
 | 
			
		||||
        super().__init__("Identifier", loc)
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self._fields.update({"name": self.name})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# "Classes" block
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MethodDefinition(Node):
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        loc: Optional[SourceLocation],
 | 
			
		||||
        key: Expression,
 | 
			
		||||
        value: FunctionExpression,
 | 
			
		||||
        kind: MethodDefinitionKind,
 | 
			
		||||
        computed: bool,
 | 
			
		||||
        static: bool,
 | 
			
		||||
    ):
 | 
			
		||||
        super().__init__("MethodDefinition", loc)
 | 
			
		||||
        self.key = key
 | 
			
		||||
        self.value = value
 | 
			
		||||
        self.kind = kind
 | 
			
		||||
        self.computed = computed
 | 
			
		||||
        self.static = static
 | 
			
		||||
        self._fields.update(
 | 
			
		||||
            {
 | 
			
		||||
                "key": self.key,
 | 
			
		||||
                "value": self.value,
 | 
			
		||||
                "kind": self.kind,
 | 
			
		||||
                "computed": self.computed,
 | 
			
		||||
                "static": self.static,
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ClassBody(Node):
 | 
			
		||||
    def __init__(self, loc: Optional[SourceLocation], body: List[MethodDefinition]):
 | 
			
		||||
        super().__init__("ClassBody", loc)
 | 
			
		||||
        self.body = body
 | 
			
		||||
        self._fields.update({"body": self.body})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Class(Node):
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        node_type: str,
 | 
			
		||||
        loc: Optional[SourceLocation],
 | 
			
		||||
        class_id: Optional[Identifier],
 | 
			
		||||
        super_class: Optional[Expression],
 | 
			
		||||
        body: ClassBody,
 | 
			
		||||
    ):
 | 
			
		||||
        super().__init__(node_type, loc)
 | 
			
		||||
        self.id = class_id
 | 
			
		||||
        self.super_class = super_class
 | 
			
		||||
        self.body = body
 | 
			
		||||
        self._fields.update(
 | 
			
		||||
            {"id": self.id, "superClass": self.super_class, "body": self.body}
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ClassDeclaration(Class, Declaration):
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        loc: Optional[SourceLocation],
 | 
			
		||||
        class_id: Identifier,
 | 
			
		||||
        super_class: Optional[Expression],
 | 
			
		||||
        body: ClassBody,
 | 
			
		||||
    ):
 | 
			
		||||
        super().__init__("ClassDeclaration", loc, class_id, super_class, body)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ClassExpression(Class, Expression):
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        loc: Optional[SourceLocation],
 | 
			
		||||
        class_id: Optional[Identifier],
 | 
			
		||||
        super_class: Optional[Expression],
 | 
			
		||||
        body: ClassBody,
 | 
			
		||||
    ):
 | 
			
		||||
        super().__init__("ClassExpression", loc, class_id, super_class, body)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MetaProperty(Expression):
 | 
			
		||||
    """`MetaProperty` node represents ``new.target`` meta property in ES2015.
 | 
			
		||||
    In the future, it will represent other meta properties as well.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self, loc: Optional[SourceLocation], meta: Identifier, meta_property: Identifier
 | 
			
		||||
    ):
 | 
			
		||||
        super().__init__("MetaProperty", loc)
 | 
			
		||||
        self.meta = (meta,)
 | 
			
		||||
        self.property = meta_property
 | 
			
		||||
        self._fields.update({"meta": self.meta, "property": self.property})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# "Modules" block
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ModuleDeclaration(Node):
 | 
			
		||||
    """A module ``import`` or ``export`` declaration."""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, node_type: str, loc: Optional[SourceLocation]):
 | 
			
		||||
        super().__init__(node_type, loc)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ModuleSpecifier(Node):
 | 
			
		||||
    """A specifier in an import or export declaration."""
 | 
			
		||||
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self, node_type: str, loc: Optional[SourceLocation], local: Identifier
 | 
			
		||||
    ):
 | 
			
		||||
        super().__init__(node_type, loc)
 | 
			
		||||
        self.local = local
 | 
			
		||||
        self._fields.update({"local": self.local})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ImportSpecifier(ModuleSpecifier):
 | 
			
		||||
    """An imported variable binding, e.g., ``{foo}`` in ``import {foo} from "mod"``
 | 
			
		||||
    or ``{foo as bar}`` in ``import {foo as bar} from "mod"``. The `imported` field
 | 
			
		||||
    refers to the name of the export imported from the module. The `local` field
 | 
			
		||||
    refers to the binding imported into the local module scope. If it is a basic named
 | 
			
		||||
    import, such as in ``import {foo} from "mod"``, both `imported` and `local` are
 | 
			
		||||
    equivalent `Identifier` nodes; in this case an `Identifier` node representing ``foo``.
 | 
			
		||||
    If it is an aliased import, such as in ``import {foo as bar} from "mod"``, the
 | 
			
		||||
    `imported` field is an `Identifier` node representing ``foo``, and the `local` field 
 | 
			
		||||
    is an `Identifier` node representing ``bar``.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self, loc: Optional[SourceLocation], local: Identifier, imported: Identifier
 | 
			
		||||
    ):
 | 
			
		||||
        super().__init__("ImportSpecifier", loc, local)
 | 
			
		||||
        self.imported = imported
 | 
			
		||||
        self._fields.update({"imported": self.imported})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ImportDefaultSpecifier(ModuleSpecifier):
 | 
			
		||||
    """A default import specifier, e.g., ``foo`` in ``import foo from "mod.js"``."""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, loc: Optional[SourceLocation], local: Identifier):
 | 
			
		||||
        super().__init__("ImportDefaultSpecifier", loc, local)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ImportNamespaceSpecifier(ModuleSpecifier):
 | 
			
		||||
    """A namespace import specifier, e.g., ``* as foo`` in ``import * as foo from "mod.js"``."""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, loc: Optional[SourceLocation], local: Identifier):
 | 
			
		||||
        super().__init__("ImportNamespaceSpecifier", loc, local)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ImportDeclaration(ModuleDeclaration):
 | 
			
		||||
    """An import declaration, e.g., ``import foo from "mod";``."""
 | 
			
		||||
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        loc: Optional[SourceLocation],
 | 
			
		||||
        specifiers: List[
 | 
			
		||||
            Union[ImportSpecifier, ImportDefaultSpecifier, ImportNamespaceSpecifier]
 | 
			
		||||
        ],
 | 
			
		||||
        source: Literal,
 | 
			
		||||
    ):
 | 
			
		||||
        super().__init__("ImportDeclaration", loc)
 | 
			
		||||
        self.specifiers = specifiers
 | 
			
		||||
        self.source = source
 | 
			
		||||
        self._fields.update({"specifiers": self.specifiers, "source": self.source})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ImportExpression(Expression):
 | 
			
		||||
    """`ImportExpression` node represents Dynamic Imports such as ``import(source)``.
 | 
			
		||||
    The `source` property is the importing source as similar to ImportDeclaration node,
 | 
			
		||||
    but it can be an arbitrary expression node.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, loc: Optional[SourceLocation], source: Expression):
 | 
			
		||||
        super().__init__("ImportExpression", loc)
 | 
			
		||||
        self.source = source
 | 
			
		||||
        self._fields.update({"source": self.source})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ExportSpecifier(ModuleSpecifier):
 | 
			
		||||
    """An exported variable binding, e.g., ``{foo}`` in ``export {foo}`` or ``{bar as foo}``
 | 
			
		||||
    in ``export {bar as foo}``. The `exported` field refers to the name exported in the module.
 | 
			
		||||
    The `local` field refers to the binding into the local module scope. If it is a basic named
 | 
			
		||||
    export, such as in ``export {foo}``, both `exported` and `local` are equivalent `Identifier`
 | 
			
		||||
    nodes; in this case an `Identifier` node representing ``foo``. If it is an aliased export,
 | 
			
		||||
    such as in ``export {bar as foo}``, the `exported` field is an `Identifier` node representing
 | 
			
		||||
    ``foo``, and the `local` field is an `Identifier` node representing ``bar``.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self, loc: Optional[SourceLocation], local: Identifier, exported: Identifier
 | 
			
		||||
    ):
 | 
			
		||||
        super().__init__("ExportSpecifier", loc, local)
 | 
			
		||||
        self.exported = exported
 | 
			
		||||
        self._fields.update({"exported": self.exported})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ExportNamedDeclaration(ModuleDeclaration):
 | 
			
		||||
    """An export named declaration, e.g., ``export {foo, bar};``, ``export {foo} from "mod";``
 | 
			
		||||
    or ``export var foo = 1;``.
 | 
			
		||||
 | 
			
		||||
    Notes:
 | 
			
		||||
        Having `declaration` populated with non-empty `specifiers` or non-null `source` results
 | 
			
		||||
        in an invalid state.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        loc: Optional[SourceLocation],
 | 
			
		||||
        declaration: Optional[Declaration],
 | 
			
		||||
        specifiers: List[ExportSpecifier],
 | 
			
		||||
        source: Optional[Literal],
 | 
			
		||||
    ):
 | 
			
		||||
        super().__init__("ExportNamedDeclaration", loc)
 | 
			
		||||
        self.declaration = declaration
 | 
			
		||||
        self.specifiers = specifiers
 | 
			
		||||
        self.source = source
 | 
			
		||||
        self._fields.update(
 | 
			
		||||
            {
 | 
			
		||||
                "declaration": self.declaration,
 | 
			
		||||
                "specifiers": self.specifiers,
 | 
			
		||||
                "source": self.source,
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AnonymousDefaultExportedFunctionDeclaration(Function):
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self, loc: Optional[SourceLocation], params: List[Pattern], body: FunctionBody
 | 
			
		||||
    ):
 | 
			
		||||
        super().__init__("FunctionDeclaration", loc, None, params, body)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AnonymousDefaultExportedClassDeclaration(Class):
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        loc: Optional[SourceLocation],
 | 
			
		||||
        super_class: Optional[Expression],
 | 
			
		||||
        body: ClassBody,
 | 
			
		||||
    ):
 | 
			
		||||
        super().__init__("ClassDeclaration", loc, None, super_class, body)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ExportDefaultDeclaration(ModuleDeclaration):
 | 
			
		||||
    """An export default declaration, e.g., ``export default function () {};`` or ``export default 1;``."""
 | 
			
		||||
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        loc: Optional[SourceLocation],
 | 
			
		||||
        declaration: Union[
 | 
			
		||||
            AnonymousDefaultExportedFunctionDeclaration,
 | 
			
		||||
            FunctionDeclaration,
 | 
			
		||||
            AnonymousDefaultExportedClassDeclaration,
 | 
			
		||||
            ClassDeclaration,
 | 
			
		||||
            Expression,
 | 
			
		||||
        ],
 | 
			
		||||
    ):
 | 
			
		||||
        super().__init__("ExportDefaultDeclaration", loc)
 | 
			
		||||
        self.declaration = declaration
 | 
			
		||||
        self._fields.update({"declaration": self.declaration})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ExportAllDeclaration(ModuleDeclaration):
 | 
			
		||||
    """An export batch declaration, e.g., ``export * from "mod";``.
 | 
			
		||||
 | 
			
		||||
    The `exported` property contains an `Identifier` when a different exported 
 | 
			
		||||
    name is specified using ``as``, e.g., ``export * as foo from "mod";``.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        loc: Optional[SourceLocation],
 | 
			
		||||
        source: Literal,
 | 
			
		||||
        exported: Optional[Identifier],
 | 
			
		||||
    ):
 | 
			
		||||
        super().__init__("ExportAllDeclaration", loc)
 | 
			
		||||
        self.source = source
 | 
			
		||||
        self.exported = exported
 | 
			
		||||
        self._fields.update({"source": self.source, "exported": self.exported})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,32 +1,38 @@
 | 
			
		|||
"""Parse tree listeners.
 | 
			
		||||
 | 
			
		||||
Basically, you should use ASTListener(source_type: SourceTypeLiteral) in most cases.
 | 
			
		||||
 | 
			
		||||
Todo:
 | 
			
		||||
    * Fill `source` field in SourceLocation and pass it to each `_get_source_location()` call.
 | 
			
		||||
    * Compare `SourceLocation` creation behavior with the one in Acorn/ESPrima
 | 
			
		||||
"""
 | 
			
		||||
import logging
 | 
			
		||||
from typing import Optional, List, Union
 | 
			
		||||
import antlr4.ParserRuleContext
 | 
			
		||||
 | 
			
		||||
from lex.JavaScriptParser import JavaScriptParser
 | 
			
		||||
from lex.JavaScriptParserListener import JavaScriptParserListener as JSBaseListener
 | 
			
		||||
from ..lex.JavaScriptParser import JavaScriptParser
 | 
			
		||||
from ..lex.JavaScriptParserListener import JavaScriptParserListener as JSBaseListener
 | 
			
		||||
 | 
			
		||||
import ast.nodes
 | 
			
		||||
from . import nodes
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _get_source_location(
 | 
			
		||||
    ctx: antlr4.ParserRuleContext, source: Optional[str]
 | 
			
		||||
) -> ast.nodes.SourceLocation:
 | 
			
		||||
) -> 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)
 | 
			
		||||
    start_pos = nodes.Position(ctx.start.line, ctx.start.column)
 | 
			
		||||
    end_pos = 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)
 | 
			
		||||
    return nodes.SourceLocation(source=source, start=start_pos, end=end_pos)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AssignableListener(JSBaseListener):
 | 
			
		||||
    _result: Union[
 | 
			
		||||
        ast.nodes.Identifier, ast.nodes.ObjectPattern, ast.nodes.ArrayPattern
 | 
			
		||||
    ]
 | 
			
		||||
    _result: Union[nodes.Identifier, nodes.ObjectPattern, nodes.ArrayPattern]
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def result(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -39,19 +45,19 @@ class AssignableListener(JSBaseListener):
 | 
			
		|||
    def enterIdentifier(self, ctx: JavaScriptParser.IdentifierContext):
 | 
			
		||||
        logging.debug("Entered section Identifier")
 | 
			
		||||
        loc = _get_source_location(ctx, None)
 | 
			
		||||
        self._result = ast.nodes.Identifier(loc, ctx.getText())
 | 
			
		||||
        self._result = nodes.Identifier(loc, ctx.getText())
 | 
			
		||||
 | 
			
		||||
    def enterArrayLiteral(self, ctx: JavaScriptParser.ArrayLiteralContext):
 | 
			
		||||
        logging.debug("Entered section ArrayLiteral")
 | 
			
		||||
        pass  # TODO
 | 
			
		||||
        raise NotImplementedError("ArrayLiteral assignment")  # TODO
 | 
			
		||||
 | 
			
		||||
    def enterObjectLiteral(self, ctx: JavaScriptParser.ObjectLiteralContext):
 | 
			
		||||
        logging.debug("Entered section ObjectLiteral")
 | 
			
		||||
        pass  # TODO
 | 
			
		||||
        raise NotImplementedError("ObjectLiteral assignment")  # TODO
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VariableDeclarationListener(JSBaseListener):
 | 
			
		||||
    _var_decl: ast.nodes.VariableDeclarator
 | 
			
		||||
    _var_decl: nodes.VariableDeclarator
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def var_declarator(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -63,17 +69,21 @@ class VariableDeclarationListener(JSBaseListener):
 | 
			
		|||
        loc = _get_source_location(ctx, None)
 | 
			
		||||
        assign_listener = AssignableListener()
 | 
			
		||||
        ctx.assignable().enterRule(assign_listener)
 | 
			
		||||
 | 
			
		||||
        if ctx.singleExpression() is not None:
 | 
			
		||||
            raise NotImplementedError("VariableDeclarator initialization")
 | 
			
		||||
 | 
			
		||||
        # ctx.singleExpression().enterRule(expression_listener)  # FIXME No ExpressionListener yet
 | 
			
		||||
        self._var_decl = ast.nodes.VariableDeclarator(
 | 
			
		||||
            loc, assign_listener.result, None
 | 
			
		||||
        )  # FIXME
 | 
			
		||||
        init = None  # value from ExpressionListener
 | 
			
		||||
 | 
			
		||||
        self._var_decl = nodes.VariableDeclarator(loc, assign_listener.result, init)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StatementListener(JSBaseListener):
 | 
			
		||||
    _stmt: ast.nodes.Statement
 | 
			
		||||
    _stmt: nodes.Statement
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def statement(self) -> ast.nodes.Statement:
 | 
			
		||||
    def statement(self) -> nodes.Statement:
 | 
			
		||||
        """Statement AST node generated after parse tree walking."""
 | 
			
		||||
 | 
			
		||||
        return self._stmt
 | 
			
		||||
| 
						 | 
				
			
			@ -87,14 +97,14 @@ class StatementListener(JSBaseListener):
 | 
			
		|||
        """Listener for BlockStatement."""
 | 
			
		||||
        logging.debug("Entered section Block")
 | 
			
		||||
 | 
			
		||||
        stmt_list: List[ast.nodes.Statement] = []
 | 
			
		||||
        stmt_list: List[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)
 | 
			
		||||
        loc = _get_source_location(ctx, None)
 | 
			
		||||
        self._stmt = nodes.BlockStatement(loc, stmt_list)
 | 
			
		||||
 | 
			
		||||
    def enterVariableStatement(self, ctx: JavaScriptParser.VariableStatementContext):
 | 
			
		||||
        logging.debug("Entered section VariableStatement")
 | 
			
		||||
| 
						 | 
				
			
			@ -106,8 +116,8 @@ class StatementListener(JSBaseListener):
 | 
			
		|||
        """Listener for VariableDeclaration."""
 | 
			
		||||
        logging.debug("Entered section VariableDeclaration")
 | 
			
		||||
 | 
			
		||||
        var_modifier: ast.nodes.VarDeclKind = ctx.varModifier().getText()
 | 
			
		||||
        var_decls: List[ast.nodes.VariableDeclarator] = []
 | 
			
		||||
        var_modifier: nodes.VarDeclKind = ctx.varModifier().getText()
 | 
			
		||||
        var_decls: List[nodes.VariableDeclarator] = []
 | 
			
		||||
 | 
			
		||||
        for var_decl in ctx.variableDeclaration():
 | 
			
		||||
            var_decl_listener = VariableDeclarationListener()
 | 
			
		||||
| 
						 | 
				
			
			@ -115,11 +125,13 @@ class StatementListener(JSBaseListener):
 | 
			
		|||
            var_decls.append(var_decl_listener.var_declarator)
 | 
			
		||||
 | 
			
		||||
        loc = _get_source_location(ctx, None)
 | 
			
		||||
        self._stmt = ast.nodes.VariableDeclaration(loc, var_modifier, var_decls)
 | 
			
		||||
        self._stmt = nodes.VariableDeclaration(loc, var_modifier, var_decls)
 | 
			
		||||
 | 
			
		||||
    def enterEmptyStatement(self, ctx: JavaScriptParser.EmptyStatementContext):
 | 
			
		||||
        """Listener for EmptyStatement."""
 | 
			
		||||
        logging.debug("Entered section EmptyStatement")
 | 
			
		||||
        loc = _get_source_location(ctx, None)
 | 
			
		||||
        self._stmt = nodes.EmptyStatement(loc)
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def enterExpressionStatement(
 | 
			
		||||
| 
						 | 
				
			
			@ -129,11 +141,12 @@ class StatementListener(JSBaseListener):
 | 
			
		|||
        TODO: check up expression containers.
 | 
			
		||||
        """
 | 
			
		||||
        logging.debug("Entered section ExpressionStatement")
 | 
			
		||||
        pass
 | 
			
		||||
        raise NotImplementedError("ExpressionStatement")
 | 
			
		||||
 | 
			
		||||
    def enterIfStatement(self, ctx: JavaScriptParser.IfStatementContext):
 | 
			
		||||
        """Listener for IfStatement."""
 | 
			
		||||
        logging.debug("Entered section IfStatement")
 | 
			
		||||
        raise NotImplementedError("ExpressionStatement")  # FIXME
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def enterFunctionDeclaration(
 | 
			
		||||
| 
						 | 
				
			
			@ -141,7 +154,7 @@ class StatementListener(JSBaseListener):
 | 
			
		|||
    ):
 | 
			
		||||
        """Listener for FunctionDeclaration."""
 | 
			
		||||
        logging.debug("Entered section FunctionDeclaration")
 | 
			
		||||
        pass
 | 
			
		||||
        raise NotImplementedError("FunctionDeclaration")
 | 
			
		||||
 | 
			
		||||
    # TODO: import/export, ClassDeclaration, iter statements, continue. break, return
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -149,10 +162,10 @@ class StatementListener(JSBaseListener):
 | 
			
		|||
class SourceElementListener(JSBaseListener):
 | 
			
		||||
    """The proxy between Program and Statement."""
 | 
			
		||||
 | 
			
		||||
    _elems: List[ast.nodes.Statement] = []
 | 
			
		||||
    _elems: List[nodes.Statement] = []
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def source_elements(self) -> List[ast.nodes.Statement]:
 | 
			
		||||
    def source_elements(self) -> List[nodes.Statement]:
 | 
			
		||||
        """Source elements AST nodes generated after parse tree walking."""
 | 
			
		||||
 | 
			
		||||
        return self._elems
 | 
			
		||||
| 
						 | 
				
			
			@ -168,22 +181,22 @@ class SourceElementListener(JSBaseListener):
 | 
			
		|||
class ASTListener(JSBaseListener):
 | 
			
		||||
    """AST listener."""
 | 
			
		||||
 | 
			
		||||
    _program_node: Optional[ast.nodes.Program] = None
 | 
			
		||||
    _source_type: ast.nodes.SourceTypeLiteral
 | 
			
		||||
    _program_node: Optional[nodes.Program] = None
 | 
			
		||||
    _source_type: nodes.SourceTypeLiteral
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def program_node(self) -> ast.nodes.Program:
 | 
			
		||||
    def program_node(self) -> 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"):
 | 
			
		||||
    def __init__(self, source_type: nodes.SourceTypeLiteral = "script"):
 | 
			
		||||
        """AST listener constructor.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            source_type (ast.nodes.SourceTypeLiteral): source type. Could be `script` or `module`. Set to
 | 
			
		||||
            source_type (nodes.SourceTypeLiteral): source type. Could be `script` or `module`. Set to
 | 
			
		||||
                `script` by default.
 | 
			
		||||
        """
 | 
			
		||||
        self._source_type = source_type
 | 
			
		||||
| 
						 | 
				
			
			@ -204,6 +217,6 @@ class ASTListener(JSBaseListener):
 | 
			
		|||
            elem.enterRule(source_elem_listener)
 | 
			
		||||
 | 
			
		||||
        loc = _get_source_location(ctx, None)  # FIXME add source name
 | 
			
		||||
        self._program_node = ast.nodes.Program(
 | 
			
		||||
        self._program_node = nodes.Program(
 | 
			
		||||
            loc, self._source_type, source_elem_listener.source_elements
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
*
 | 
			
		||||
!.gitignore
 | 
			
		||||
!JavaScriptBaseLexer.py
 | 
			
		||||
!JavaScriptBaseParser.py
 | 
			
		||||
!ErrorListeners.py
 | 
			
		||||
!__init__.py
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
"""Lexer/parser module.
 | 
			
		||||
Consists mostly of auto-generated files spewed by ANTLR.
 | 
			
		||||
"""
 | 
			
		||||
		Loading…
	
		Reference in New Issue