Compare commits

...

12 Commits

Author SHA1 Message Date
Yury Kurlykov c504830928
Add ES2020 nodes 2020-04-28 18:41:51 +10:00
Yury Kurlykov 85aa1b2a34
Add ES2018 nodes 2020-04-28 16:55:26 +10:00
Yury Kurlykov 245bae4175
Make Property ES6+ compliant 2020-04-28 16:51:14 +10:00
Yury Kurlykov 74d53ddf34
Add ES2016 nodes
A whole standard just for power operator? ECMA must be kidding.
2020-04-28 16:05:37 +10:00
Yury Kurlykov 7a1eca4aca
Add ES2015 nodes 2020-04-28 15:54:16 +10:00
Yury Kurlykov b4bbc8460e
Raise NotImplementedError on WIP nodes 2020-04-28 15:53:28 +10:00
Yury Kurlykov ace635c999
Implement EmptyStatement node generation
I don't even know what does the "procrastination" word mean. `EmptyStatement` is the most useful node ever.
2020-04-28 15:52:35 +10:00
Yury Kurlykov 5266ebed21
Add .gitignore to repo
It was there since day one, but I forgot to exclude it from the gitignore itself. Well, it happens.
2020-04-28 12:04:52 +10:00
Yury Kurlykov afe6c8d244
Add __init__.py in lex module
First of all, it has a docstring.
2020-04-28 12:01:35 +10:00
Yury Kurlykov ea5b03d93e
Fix import statements
I just found out my project couldn't run anywhere but IDE.
2020-04-28 12:00:06 +10:00
Yury Kurlykov 42b4e40d9e
Add `name` field in Identifier 2020-04-28 11:20:15 +10:00
Yury Kurlykov 6372a043a2
Fix ImportError
It is not the best idea to name a module just like the native one.
2020-04-28 11:19:40 +10:00
6 changed files with 432 additions and 66 deletions

View File

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

View File

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

View File

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

View File

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

6
jasminesnake/lex/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
*
!.gitignore
!JavaScriptBaseLexer.py
!JavaScriptBaseParser.py
!ErrorListeners.py
!__init__.py

View File

@ -0,0 +1,3 @@
"""Lexer/parser module.
Consists mostly of auto-generated files spewed by ANTLR.
"""