From dc7502aa57c87d8ab7dc63f6d353f76eda1f66a4 Mon Sep 17 00:00:00 2001 From: Yury Kurlykov Date: Sun, 26 Apr 2020 23:10:26 +1000 Subject: [PATCH] Implement ES5 part of JavaScript AST --- jasminesnake/ast/__init__.py | 3 + jasminesnake/ast/nodes/__init__.py | 73 +++ jasminesnake/ast/nodes/declarations.py | 52 ++ jasminesnake/ast/nodes/expressions.py | 669 +++++++++++++++++++++++ jasminesnake/ast/nodes/functions.py | 31 ++ jasminesnake/ast/nodes/identifiers.py | 13 + jasminesnake/ast/nodes/literals.py | 14 + jasminesnake/ast/nodes/operator_enums.py | 72 +++ jasminesnake/ast/nodes/patterns.py | 39 ++ jasminesnake/ast/nodes/programs.py | 15 + jasminesnake/ast/nodes/statements.py | 158 ++++++ jasminesnake/ast/visitors.py | 12 + 12 files changed, 1151 insertions(+) create mode 100644 jasminesnake/ast/__init__.py create mode 100644 jasminesnake/ast/nodes/__init__.py create mode 100644 jasminesnake/ast/nodes/declarations.py create mode 100644 jasminesnake/ast/nodes/expressions.py create mode 100644 jasminesnake/ast/nodes/functions.py create mode 100644 jasminesnake/ast/nodes/identifiers.py create mode 100644 jasminesnake/ast/nodes/literals.py create mode 100644 jasminesnake/ast/nodes/operator_enums.py create mode 100644 jasminesnake/ast/nodes/patterns.py create mode 100644 jasminesnake/ast/nodes/programs.py create mode 100644 jasminesnake/ast/nodes/statements.py create mode 100644 jasminesnake/ast/visitors.py diff --git a/jasminesnake/ast/__init__.py b/jasminesnake/ast/__init__.py new file mode 100644 index 0000000..420d815 --- /dev/null +++ b/jasminesnake/ast/__init__.py @@ -0,0 +1,3 @@ +"""AST module.""" + +from . import nodes, visitors diff --git a/jasminesnake/ast/nodes/__init__.py b/jasminesnake/ast/nodes/__init__.py new file mode 100644 index 0000000..ed63f0b --- /dev/null +++ b/jasminesnake/ast/nodes/__init__.py @@ -0,0 +1,73 @@ +"""The module with AST nodes declaration. They are ESTree compliant. + +The module lacks support of: + * ES5 features: + * labelled statements + * switch statements + * try-catch statements + * debugger statement + * with statement + * RegExp + * ES6+ + +More about ESTree standard: +https://github.com/estree/estree/ + +Todo: + * Add support for lacking features +""" + +from typing import Optional, Union + + +class Position: + """The class for an object consisting of a line number (1-indexed) and a column number (0-indexed).""" + + def __init__(self, line: int, column: int): + if line < 1 or column < 0: + raise ValueError( + "L{}:C{} is not valid ESTree position!".format(line, column) + ) + + self.line = line + self.column = column + + +class SourceLocation: + """ + The class for the source location information of a node. + + Consists of a start position (the position of the first character of the parsed source region) and an end + position (the position of the first character after the parsed source region). + + See Also: + Position + """ + + def __init__(self, source: Optional[str], start: Position, end: Position): + self.source = source + self.start = start + self.end = end + + +class Node: + """ESTree AST nodes are represented as Node objects, which may have any prototype inheritance but which implement + this interface. + + The `type` field is a string representing the AST variant type. Each subtype of `Node` is documented below with + the specific string of its `type` field. You can use this field to determine which interface a node implements. + + The `loc` field represents the source location information of the node. If the node contains no information about + the source location, the field is `None`; otherwise it contains a `SourceLocation` object. + + See Also: + SourceLocation + """ + + def __init__(self, node_type: str, loc: Optional[SourceLocation]): + self.type = node_type + self.loc = loc + + +number = Union[int, float] +"""A type union consisting of int and float Python types. Consider it as Number type from JavaScript.""" diff --git a/jasminesnake/ast/nodes/declarations.py b/jasminesnake/ast/nodes/declarations.py new file mode 100644 index 0000000..be14894 --- /dev/null +++ b/jasminesnake/ast/nodes/declarations.py @@ -0,0 +1,52 @@ +"""The module of AST nodes for declarations.""" + +from typing import List + +from . import * +from .identifiers import Identifier +from .patterns import Pattern +from .statements import Statement, FunctionBody +from .functions import Function + + +class Declaration(Statement): + """Any declaration node. Note that declarations are considered statements; this is because declarations can + appear in any statement context. """ + + def __init__(self, node_type: str, loc: Optional[SourceLocation]): + super().__init__(node_type, loc) + + +class FunctionDeclaration(Function, Declaration): + """A function declaration. Note that unlike in the parent interface `Function`, the `id` cannot be `None`.""" + + def __init__( + self, + loc: Optional[SourceLocation], + function_id: Identifier, + params: List[Pattern], + body: FunctionBody, + ): + super().__init__("FunctionDeclaration", loc, function_id, params, body) + + +class VariableDeclarator(Node): + """A variable declarator.""" + + def __init__( + self, loc: Optional[SourceLocation], var_id: Pattern, init: Optional[Exception] + ): + super().__init__("VariableDeclarator", loc) + self.id = var_id + self.init = init + + +class VariableDeclaration(Declaration): + """A variable declaration.""" + + def __init__( + self, loc: Optional[SourceLocation], declarations: List[VariableDeclarator] + ): + super().__init__("VariableDeclaration", loc) + self.declarations = declarations + self.kind = "var" diff --git a/jasminesnake/ast/nodes/expressions.py b/jasminesnake/ast/nodes/expressions.py new file mode 100644 index 0000000..37b2717 --- /dev/null +++ b/jasminesnake/ast/nodes/expressions.py @@ -0,0 +1,669 @@ +"""The module of AST nodes for expressions.""" + +from typing import List, Literal as TypeLiteral + +from . import * +from .functions import Function +from .identifiers import Identifier +from .literals import Literal +from .operator_enums import ( + UnaryOperator, + UpdateOperator, + BinaryOperator, + AssignmentOperator, + LogicalOperator, +) +from .patterns import Pattern +from .statements import FunctionBody + + +class Expression(Node): + """Any expression node. Since the left-hand side of an assignment may be any expression in general, an expression + can also be a pattern. + + See Also: + Pattern + """ + + def __init__(self, node_type: str, loc: Optional[SourceLocation]): + super().__init__(node_type, loc) + + +class ThisExpression(Expression): + """A `this` expression.""" + + def __init__(self, loc: Optional[SourceLocation]): + super().__init__("ThisExpression", loc) + + +class ArrayExpression(Expression): + """An array expression. An element might be `None` if it represents a hole in a sparse array. E.g. ``[1,,2]``.""" + + def __init__( + self, loc: Optional[SourceLocation], elements: List[Optional[Expression]] + ): + super().__init__("ArrayExpression", loc) + self.elements = elements + + +PropKind = TypeLiteral["init", "get", "set"] +"""A type for a `kind` field of `Property`.""" + + +class Property(Node): + """A literal property in an object expression can have either a string or number as its `value`. Ordinary + property initializers have a `kind` value ``"init"``; getters and setters have the kind values ``"get"`` and + ``"set"``, respectively. """ + + def __init__( + self, + loc: Optional[SourceLocation], + key: Union[Literal, Identifier], + value: Expression, + kind: PropKind, + ): + super().__init__("Property", loc) + self.key = key + self.value = value + self.kind = kind + + +class ObjectExpression(Expression): + """An object expression.""" + + def __init__(self, loc: Optional[SourceLocation], properties: List[Property]): + super().__init__("ObjectExpression", loc) + self.properties = properties + + +class FunctionExpression(Function, Expression): + """A function expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + function_id: Optional[Identifier], + params: List[Pattern], + body: FunctionBody, + ): + super().__init__("FunctionExpression", loc, function_id, params, body) + + +class UnaryExpression(Expression): + """A unary operator expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + operator: UnaryOperator, + prefix: bool, + argument: Expression, + ): + super().__init__("UnaryExpression", loc) + self.operator = operator + self.prefix = prefix + self.argument = argument + + +class UpdateExpression(Expression): + """An update (increment or decrement) operator expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + operator: UpdateOperator, + argument: Expression, + prefix: bool, + ): + super().__init__("UpdateExpression", loc) + self.operator = operator + self.argument = argument + self.prefix = prefix + + +class BinaryExpression(Expression): + """A binary operator expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + operator: BinaryOperator, + left: Expression, + right: Expression, + ): + super().__init__("BinaryExpression", loc) + self.operator = operator + self.left = left + self.right = right + + +class AssignmentExpression(Expression): + """An assignment operator expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + operator: AssignmentOperator, + left: Union[Pattern, Expression], + right: Expression, + ): + super().__init__("AssignmentExpression", loc) + self.operator = operator + self.left = left + self.right = right + + +class LogicalExpression(Expression): + """A logical operator expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + operator: LogicalOperator, + left: Union[Pattern, Expression], + right: Expression, + ): + super().__init__("LogicalExpression", loc) + self.operator = operator + self.left = left + self.right = right + + +class UnaryMinusExpression(UnaryExpression): + """A unary minus expression.""" + + def __init__(self, loc: Optional[SourceLocation], argument: Expression): + super().__init__(loc, UnaryOperator.MINUS, True, argument) + + +class UnaryPlusExpression(UnaryExpression): + """A unary plus expression.""" + + def __init__(self, loc: Optional[SourceLocation], argument: Expression): + super().__init__(loc, UnaryOperator.PLUS, True, argument) + + +class UnaryLogicNotExpression(UnaryExpression): + """A unary logic "not" expression.""" + + def __init__(self, loc: Optional[SourceLocation], argument: Expression): + super().__init__(loc, UnaryOperator.NOT_LOGIC, True, argument) + + +class UnaryBitNotExpression(UnaryExpression): + """A unary bit "not" expression.""" + + def __init__(self, loc: Optional[SourceLocation], argument: Expression): + super().__init__(loc, UnaryOperator.NOT_BIT, True, argument) + + +class TypeofExpression(UnaryExpression): + """A `typeof` expression.""" + + def __init__(self, loc: Optional[SourceLocation], argument: Expression): + super().__init__(loc, UnaryOperator.TYPEOF, True, argument) + + +class VoidExpression(UnaryExpression): + """A `void` expression.""" + + def __init__(self, loc: Optional[SourceLocation], argument: Expression): + super().__init__(loc, UnaryOperator.VOID, True, argument) + + +class DeleteExpression(UnaryExpression): + """A `delete` expression.""" + + def __init__(self, loc: Optional[SourceLocation], argument: Expression): + super().__init__(loc, UnaryOperator.DELETE, True, argument) + + +class PreIncrementExpression(UpdateExpression): + """A pre-increment expression.""" + + def __init__( + self, loc: Optional[SourceLocation], argument: Expression, + ): + super().__init__(loc, UpdateOperator.INCREMENT, argument, True) + + +class PostIncrementExpression(UpdateExpression): + """A post-increment expression.""" + + def __init__( + self, loc: Optional[SourceLocation], argument: Expression, + ): + super().__init__(loc, UpdateOperator.INCREMENT, argument, False) + + +class PreDecrementExpression(UpdateExpression): + """A pre-decrement expression.""" + + def __init__( + self, loc: Optional[SourceLocation], argument: Expression, + ): + super().__init__(loc, UpdateOperator.DECREMENT, argument, True) + + +class PostDecrementExpression(UpdateExpression): + """A post-decrement expression.""" + + def __init__( + self, loc: Optional[SourceLocation], argument: Expression, + ): + super().__init__(loc, UpdateOperator.DECREMENT, argument, False) + + +class EqualityExpression(BinaryExpression): + """An equality expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.EQ, left, right) + + +class NotEqualityExpression(BinaryExpression): + """A "not equality" expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.NEQ, left, right) + + +class IdentityEqualityExpression(BinaryExpression): + """An identity equality expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.EQ_IDENTITY, left, right) + + +class NotIdentityEqualityExpression(BinaryExpression): + """A "not identity equality" expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.NEQ_IDENTITY, left, right) + + +class LowerThanRelationExpression(BinaryExpression): + """A "lower than" expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.LT, left, right) + + +class LowerThanEqualRelationExpression(BinaryExpression): + """A "lower than or equal" expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.LTE, left, right) + + +class GreaterThanRelationExpression(BinaryExpression): + """A "greater than" expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.GT, left, right) + + +class GreaterThanEqualRelationExpression(BinaryExpression): + """A "greater than or equal" expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.GTE, left, right) + + +class LeftBitShiftExpression(BinaryExpression): + """A "left bit shift" expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.SHL, left, right) + + +class RightBitShiftExpression(BinaryExpression): + """A "right bit shift" expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.SHR, left, right) + + +class LogicRightBitShiftExpression(BinaryExpression): + """A "logical right bit shift" expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.SHR_LOGIC, left, right) + + +class AddArithmeticExpression(BinaryExpression): + """An addition arithmetical expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.ADD, left, right) + + +class SubArithmeticExpression(BinaryExpression): + """A subtraction arithmetical expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.SUB, left, right) + + +class MulArithmeticExpression(BinaryExpression): + """A multiplication arithmetical expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.MUL, left, right) + + +class DivArithmeticExpression(BinaryExpression): + """A division arithmetical expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.DIV, left, right) + + +class ModArithmeticExpression(BinaryExpression): + """A modulo arithmetical expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.MOD, left, right) + + +class OrBitExpression(BinaryExpression): + """An "or" bit expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.OR, left, right) + + +class XorBitExpression(BinaryExpression): + """A "xor" bit expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.XOR, left, right) + + +class AndBitExpression(BinaryExpression): + """An "and" bit expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.AND, left, right) + + +class InExpression(BinaryExpression): + """An "in" expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.IN, left, right) + + +class InstanceofExpression(BinaryExpression): + """An "instanceof" expression.""" + + def __init__( + self, loc: Optional[SourceLocation], left: Expression, right: Expression + ): + super().__init__(loc, BinaryOperator.INSTANCEOF, left, right) + + +class SimpleAssignExpression(AssignmentExpression): + """An assignment done with operator ``=`` expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + left: Union[Pattern, Expression], + right: Expression, + ): + super().__init__(loc, AssignmentOperator.ASSIGN, left, right) + + +class AddAssignExpression(AssignmentExpression): + """An addition assignment done with operator ``+=`` expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + left: Union[Pattern, Expression], + right: Expression, + ): + super().__init__(loc, AssignmentOperator.ADD, left, right) + + +class SubAssignExpression(AssignmentExpression): + """A subtraction assignment done with operator ``-=`` expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + left: Union[Pattern, Expression], + right: Expression, + ): + super().__init__(loc, AssignmentOperator.SUB, left, right) + + +class MulAssignExpression(AssignmentExpression): + """A multiplication assignment done with operator ``*=`` expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + left: Union[Pattern, Expression], + right: Expression, + ): + super().__init__(loc, AssignmentOperator.MUL, left, right) + + +class ModAssignExpression(AssignmentExpression): + """A modulo assignment done with operator ``%=`` expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + left: Union[Pattern, Expression], + right: Expression, + ): + super().__init__(loc, AssignmentOperator.MOD, left, right) + + +class ShlAssignExpression(AssignmentExpression): + """A left shift assignment done with operator ``<<=`` expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + left: Union[Pattern, Expression], + right: Expression, + ): + super().__init__(loc, AssignmentOperator.SHL, left, right) + + +class ShrAssignExpression(AssignmentExpression): + """A right shift assignment done with operator ``>>=`` expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + left: Union[Pattern, Expression], + right: Expression, + ): + super().__init__(loc, AssignmentOperator.SHR, left, right) + + +class LogicShrAssignExpression(AssignmentExpression): + """A logical right shift assignment done with operator ``>>>=`` expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + left: Union[Pattern, Expression], + right: Expression, + ): + super().__init__(loc, AssignmentOperator.SHR_LOGIC, left, right) + + +class OrAssignExpression(AssignmentExpression): + """A "bit or" assignment done with operator ``|=`` expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + left: Union[Pattern, Expression], + right: Expression, + ): + super().__init__(loc, AssignmentOperator.OR, left, right) + + +class XorAssignExpression(AssignmentExpression): + """A "bit xor" assignment done with operator ``^=`` expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + left: Union[Pattern, Expression], + right: Expression, + ): + super().__init__(loc, AssignmentOperator.XOR, left, right) + + +class AndAssignExpression(AssignmentExpression): + """A "bit and" assignment done with operator ``&=`` expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + left: Union[Pattern, Expression], + right: Expression, + ): + super().__init__(loc, AssignmentOperator.AND, left, right) + + +class OrLogicExpression(LogicalExpression): + """An "or" logical expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + left: Union[Pattern, Expression], + right: Expression, + ): + super().__init__(loc, LogicalOperator.OR, left, right) + + +class AndLogicExpression(LogicalExpression): + """An "and" logical expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + left: Union[Pattern, Expression], + right: Expression, + ): + super().__init__(loc, LogicalOperator.AND, left, right) + + +class MemberExpression(Expression, Pattern): + """A member expression. If `computed` is ``True``, the node corresponds to a computed (``a[b]``) member + expression and `property` is an `Expression`. If `computed` is `False`, the node corresponds to a static + (``a.b``) member expression and `property` is an `Identifier`. """ + + def __init__( + self, + loc: Optional[SourceLocation], + member_object: Expression, + member_property: Expression, + computed: bool, + ): + super().__init__("MemberExpression", loc) + self.object = member_object + self.property = member_property + self.computed = computed + + +class ConditionalExpression(Expression): + """A conditional expression, i.e., a ternary ``?``/``:`` expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + test: Expression, + alternate: Expression, + consequent: Expression, + ): + super().__init__("ConditionalExpression", loc) + self.test = test + self.alternate = alternate + self.consequent = consequent + + +class CallExpression(Expression): + """A function or method call expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + callee: Expression, + arguments: List[Expression], + ): + super().__init__("CallExpression", loc) + self.callee = callee + self.arguments = arguments + + +class NewExpression(Expression): + """A ``new`` expression.""" + + def __init__( + self, + loc: Optional[SourceLocation], + callee: Expression, + arguments: List[Expression], + ): + super().__init__("NewExpression", loc) + self.callee = callee + self.arguments = arguments + + +class SequenceExpression(Expression): + """A sequence expression, i.e., a comma-separated sequence of expressions.""" + + def __init__(self, loc: Optional[SourceLocation], expressions: List[Expression]): + super().__init__("SequenceExpression", loc) + self.expressions = expressions diff --git a/jasminesnake/ast/nodes/functions.py b/jasminesnake/ast/nodes/functions.py new file mode 100644 index 0000000..ecd55c5 --- /dev/null +++ b/jasminesnake/ast/nodes/functions.py @@ -0,0 +1,31 @@ +"""The module of AST nodes for functions.""" + +from typing import List + +from . import * +from .identifiers import Identifier +from .patterns import Pattern +from .statements import FunctionBody + + +class Function(Node): + """A function declaration or expression. + + See Also: + FunctionDeclaration + FunctionExpression + FunctionBody + """ + + def __init__( + self, + node_type: str, + loc: Optional[SourceLocation], + function_id: Optional[Identifier], + params: List[Pattern], + body: FunctionBody, + ): + super().__init__(node_type, loc) + self.id = function_id + self.params = params + self.body = body diff --git a/jasminesnake/ast/nodes/identifiers.py b/jasminesnake/ast/nodes/identifiers.py new file mode 100644 index 0000000..dd03a02 --- /dev/null +++ b/jasminesnake/ast/nodes/identifiers.py @@ -0,0 +1,13 @@ +"""The module of AST nodes for identifiers.""" + +from . import * +from .patterns import Pattern +from .expressions import Expression + + +class Identifier(Expression, Pattern): + """An identifier. Note that an identifier may be an expression or a destructuring pattern.""" + + def __init__(self, loc: Optional[SourceLocation], name: str): + super(Identifier, self).__init__("Identifier", loc) + self.name = name diff --git a/jasminesnake/ast/nodes/literals.py b/jasminesnake/ast/nodes/literals.py new file mode 100644 index 0000000..4a15075 --- /dev/null +++ b/jasminesnake/ast/nodes/literals.py @@ -0,0 +1,14 @@ +"""The module of AST nodes for literals.""" + +from . import * +from .expressions import Expression + + +class Literal(Expression): + """A literal token. Note that a literal can be an expression.""" + + def __init__( + self, loc: Optional[SourceLocation], value: Union[str, bool, number, None] + ): + super().__init__("Literal", loc) + self.value = value diff --git a/jasminesnake/ast/nodes/operator_enums.py b/jasminesnake/ast/nodes/operator_enums.py new file mode 100644 index 0000000..8c299cb --- /dev/null +++ b/jasminesnake/ast/nodes/operator_enums.py @@ -0,0 +1,72 @@ +"""The module representing enums for JavaScript operators.""" + +from enum import Enum + + +class UnaryOperator(Enum): + """A unary operator token.""" + + MINUS = "-" + PLUS = "+" + NOT_LOGIC = "!" + NOT_BIT = "~" + TYPEOF = "typeof" + VOID = "void" + DELETE = "delete" + + +class UpdateOperator(Enum): + """An update (increment or decrement) operator token.""" + + INCREMENT = "++" + DECREMENT = "--" + + +class BinaryOperator(Enum): + """A binary operator token.""" + + EQ = "==" + NEQ = "!=" + EQ_IDENTITY = "===" + NEQ_IDENTITY = "!==" + LT = "<" + LTE = "<=" + GT = ">" + GTE = ">=" + SHL = "<<" + SHR = ">>" + SHR_LOGIC = ">>>" + ADD = "+" + SUB = "-" + MUL = "*" + DIV = "/" + MOD = "%" + OR = "|" + XOR = "^" + AND = "&" + IN = "in" + INSTANCEOF = "instanceof" + + +class AssignmentOperator(Enum): + """An assignment operator token.""" + + ASSIGN = "=" + ADD = "+=" + SUB = "-=" + MUL = "*=" + DIV = "/=" + MOD = "%=" + SHL = "<<=" + SHR = ">>=" + SHR_LOGIC = ">>>=" + OR = "|=" + XOR = "^=" + AND = "&=" + + +class LogicalOperator(Enum): + """A logical operator token.""" + + OR = "||" + AND = "&&" diff --git a/jasminesnake/ast/nodes/patterns.py b/jasminesnake/ast/nodes/patterns.py new file mode 100644 index 0000000..51c9357 --- /dev/null +++ b/jasminesnake/ast/nodes/patterns.py @@ -0,0 +1,39 @@ +"""The module of AST nodes for patterns. + +Destructuring binding and assignment are not part of ES5, but all binding positions accept `Pattern` to allow for +destructuring in ES6. Nevertheless, for ES5, the only `Pattern` subtype is `Identifier`. + +See Also: + Identifier +""" + +from typing import TypedDict, Union, List +from . import * +from .literals import Literal +from .identifiers import Identifier + + +class Pattern(Node): + """A pattern.""" + + def __init__(self, node_type: str, loc: Optional[SourceLocation]): + super().__init__(node_type, loc) + + +class ObjectKeyValue(TypedDict): + key: Union[Literal, Identifier] + value: Pattern + + +class ObjectPattern(Pattern): + def __init__(self, loc: Optional[SourceLocation], properties: List[ObjectKeyValue]): + super().__init__("ObjectPattern", loc) + self.properties = properties + + +class ArrayPattern(Pattern): + def __init__( + self, loc: Optional[SourceLocation], elements: List[Optional[Pattern]] + ): + super().__init__("ArrayPattern", loc) + self.elements = elements diff --git a/jasminesnake/ast/nodes/programs.py b/jasminesnake/ast/nodes/programs.py new file mode 100644 index 0000000..7577cc9 --- /dev/null +++ b/jasminesnake/ast/nodes/programs.py @@ -0,0 +1,15 @@ +"""The module of Program AST node.""" + +from typing import List +from . import * +from .statements import Statement, Directive + + +class Program(Node): + """A complete program source tree.""" + + def __init__( + self, loc: Optional[SourceLocation], body: List[Union[Directive, Statement]] + ): + super().__init__("Program", loc) + self.body = body diff --git a/jasminesnake/ast/nodes/statements.py b/jasminesnake/ast/nodes/statements.py new file mode 100644 index 0000000..25e6b64 --- /dev/null +++ b/jasminesnake/ast/nodes/statements.py @@ -0,0 +1,158 @@ +"""The module of AST nodes for statements.""" + +from typing import List + +from . import * +from .expressions import Expression +from .literals import Literal +from .identifiers import Identifier +from .declarations import VariableDeclaration +from .patterns import Pattern + + +class Statement(Node): + """Any statement.""" + + def __init__(self, node_type: str, loc: Optional[SourceLocation]): + super().__init__(node_type, loc) + + +class EmptyStatement(Statement): + """An empty statement, i.e., a solitary semicolon.""" + + def __init__(self, loc: Optional[SourceLocation]): + super().__init__("EmptyStatement", loc) + + +class BlockStatement(Statement): + """A block statement, i.e., a sequence of statements surrounded by braces.""" + + def __init__(self, loc: Optional[SourceLocation], body: List[Statement]): + super().__init__("BlockStatement", loc) + self.body = body + + +class ExpressionStatement(Statement): + """An expression statement, i.e., a statement consisting of a single expression.""" + + def __init__(self, loc: Optional[SourceLocation], expression: Expression): + super().__init__("ExpressionStatement", loc) + self.expression = expression + + +class Directive(Node): + """A directive from the directive prologue of a script or function. The `directive` property is the raw string + source of the directive without quotes. + """ + + def __init__( + self, loc: Optional[SourceLocation], expression: Literal, directive: str + ): + super().__init__("Directive", loc) + self.expression = expression + self.directive = directive + + +class FunctionBody(BlockStatement): + """The body of a function, which is a block statement that may begin with directives.""" + + def __init__( + self, loc: Optional[SourceLocation], body: List[Union[Directive, Statement]] + ): + super().__init__(loc, body) + + +class ReturnStatement(Statement): + """A `return` statement.""" + + def __init__(self, loc: Optional[SourceLocation], argument: Optional[Expression]): + super().__init__("ReturnStatement", loc) + self.argument = argument + + +class BreakStatement(Statement): + """A `break` statement.""" + + def __init__(self, loc: Optional[SourceLocation], label: Optional[Identifier]): + super().__init__("BreakStatement", loc) + self.label = label + + +class ContinueStatement(Statement): + """A `continue` statement.""" + + def __init__(self, loc: Optional[SourceLocation], label: Optional[Identifier]): + super().__init__("ContinueStatement", loc) + self.label = label + + +class IfStatement(Statement): + """An `if` statement.""" + + def __init__( + self, + loc: Optional[SourceLocation], + test: Expression, + consequent: Statement, + alternate: Optional[Statement], + ): + super().__init__("IfStatement", loc) + self.test = test + self.consequent = consequent + self.alternate = alternate + + +class WhileStatement(Statement): + """A `while` statement.""" + + def __init__( + self, loc: Optional[SourceLocation], test: Expression, body: Statement + ): + super().__init__("WhileStatement", loc) + self.test = test + self.body = body + + +class DoWhileStatement(Statement): + """A `do`/`while` statement.""" + + def __init__( + self, loc: Optional[SourceLocation], body: Statement, test: Expression + ): + super().__init__("DoWhileStatement", loc) + self.body = body + self.test = test + + +class ForStatement(Statement): + """A `for` statement.""" + + def __init__( + self, + loc: Optional[SourceLocation], + init: Union[VariableDeclaration, Expression, None], + test: Optional[Expression], + update: Optional[Expression], + body: Statement, + ): + super().__init__("ForStatement", loc) + self.init = init + self.test = test + self.update = update + self.body = body + + +class ForInStatement(Statement): + """A `for`/`in` statement.""" + + def __init__( + self, + loc: Optional[SourceLocation], + left: Union[VariableDeclaration, Pattern], + right: Expression, + body: Statement, + ): + super().__init__("ForInStatement", loc) + self.left = left + self.right = right + self.body = body diff --git a/jasminesnake/ast/visitors.py b/jasminesnake/ast/visitors.py new file mode 100644 index 0000000..07bb9b6 --- /dev/null +++ b/jasminesnake/ast/visitors.py @@ -0,0 +1,12 @@ +from lex import JavaScriptParserVisitor, JavaScriptParser +import ast.nodes + +JSBaseVisitor = JavaScriptParserVisitor.JavaScriptParserVisitor +JSParser = JavaScriptParser.JavaScriptParser + + +class JSASTVisitor(JSBaseVisitor): + def visitVariableDeclarationList( + self, ctx: JSParser.VariableDeclarationListContext + ): + pass