mirror of https://github.com/t1meshift/js.git
Compare commits
12 Commits
041f4c31fb
...
c504830928
Author | SHA1 | Date |
---|---|---|
Yury Kurlykov | c504830928 | |
Yury Kurlykov | 85aa1b2a34 | |
Yury Kurlykov | 245bae4175 | |
Yury Kurlykov | 74d53ddf34 | |
Yury Kurlykov | 7a1eca4aca | |
Yury Kurlykov | b4bbc8460e | |
Yury Kurlykov | ace635c999 | |
Yury Kurlykov | 5266ebed21 | |
Yury Kurlykov | afe6c8d244 | |
Yury Kurlykov | ea5b03d93e | |
Yury Kurlykov | 42b4e40d9e | |
Yury Kurlykov | 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