Commit last changes

master
Yury Kurlykov 2020-06-26 16:24:06 +10:00
parent 44d7e2410f
commit ea923d2273
Signed by: t1meshift
GPG Key ID: B133F3167ABF94D8
5 changed files with 154 additions and 18 deletions

View File

@ -11,7 +11,7 @@ Another JavaScript interpreter written on Python 3.
To run tests: To run tests:
- pylint - pytest
- Tox - Tox
You can get ANTLR [here](https://www.antlr.org/), other dependencies could be installed with pip: You can get ANTLR [here](https://www.antlr.org/), other dependencies could be installed with pip:

View File

@ -3,7 +3,7 @@ So here it is.
""" """
import logging import logging
__version__ = "0.0.7" __version__ = "0.0.9"
__snake__ = r""" __snake__ = r"""
_________ _________ _________ _________
/ \ / \ / \ / \
@ -32,4 +32,5 @@ LOG_LEVELS = {
}, },
} }
# TODO: make it usable as a module too # TODO: make it usable as a module too

View File

@ -6,9 +6,11 @@ Todo:
* Fill `source` field in SourceLocation and pass it to each `_get_source_location()` call. * Fill `source` field in SourceLocation and pass it to each `_get_source_location()` call.
* Compare `SourceLocation` creation behavior with the one in Acorn/ESPrima * Compare `SourceLocation` creation behavior with the one in Acorn/ESPrima
""" """
import logging import logging
from typing import Optional, List, Union from typing import Optional, List, Union
import antlr4.ParserRuleContext import antlr4.ParserRuleContext
from antlr4 import ErrorNode, ParserRuleContext
from ..lex.JavaScriptParser import JavaScriptParser from ..lex.JavaScriptParser import JavaScriptParser
from ..lex.JavaScriptParserListener import JavaScriptParserListener as JSBaseListener from ..lex.JavaScriptParserListener import JavaScriptParserListener as JSBaseListener
@ -31,7 +33,78 @@ def _get_source_location(
return nodes.SourceLocation(source=source, start=start_pos, end=end_pos) return nodes.SourceLocation(source=source, start=start_pos, end=end_pos)
class AssignableListener(JSBaseListener): class NodeListener(JSBaseListener):
def visitErrorNode(self, node: ErrorNode):
pass
def enterEveryRule(self, ctx: ParserRuleContext):
pass
class LiteralListener(JSBaseListener):
_literal: nodes.Literal
@property
def literal(self):
return self._literal
def enterLiteral(self, ctx: JavaScriptParser.LiteralContext):
loc = _get_source_location(ctx, None)
if ctx.NullLiteral() is not None:
self._literal = nodes.NullLiteral(loc)
elif ctx.BooleanLiteral() is not None:
value = ctx.BooleanLiteral().getText() == "true"
self._literal = nodes.BooleanLiteral(loc, value)
elif ctx.StringLiteral() is not None:
self._literal = nodes.StringLiteral(loc, ctx.StringLiteral().getText())
else:
ctx.getChild(0).enterRule(self)
def enterNumericLiteral(self, ctx: JavaScriptParser.NumericLiteralContext):
# Thank you, PEP-515, very cool!
loc = _get_source_location(ctx, None)
value = float(ctx.DecimalLiteral().getText())
self._literal = nodes.NumericLiteral(loc, value)
def enterBigintLiteral(self, ctx: JavaScriptParser.BigintLiteralContext):
raise NotImplementedError("Bigint literals")
class ExpressionListener(JSBaseListener):
_expr: nodes.Expression
@property
def expression(self):
return self._expr
def enterExpressionStatement(
self, ctx: JavaScriptParser.ExpressionStatementContext
):
ctx.expressionSequence().enterRule(self)
def enterExpressionSequence(self, ctx: JavaScriptParser.ExpressionSequenceContext):
expressions: List[nodes.Expression] = []
loc = _get_source_location(ctx, None)
for expr in ctx.singleExpression():
expr_listener = ExpressionListener()
expr.enterRule(expr_listener)
expressions.append(expr_listener.expression)
self._expr = nodes.SequenceExpression(loc, expressions)
def enterParenthesizedExpression(
self, ctx: JavaScriptParser.ParenthesizedExpressionContext
):
ctx.expressionSequence().enterRule(self)
def enterLiteralExpression(self, ctx: JavaScriptParser.LiteralExpressionContext):
literal_listener = LiteralListener()
ctx.literal().enterRule(literal_listener)
self._expr = literal_listener.literal
# TODO single expression
class AssignableListener(NodeListener):
_result: Union[nodes.Identifier, nodes.ObjectPattern, nodes.ArrayPattern] _result: Union[nodes.Identifier, nodes.ObjectPattern, nodes.ArrayPattern]
@property @property
@ -70,11 +143,11 @@ class VariableDeclarationListener(JSBaseListener):
assign_listener = AssignableListener() assign_listener = AssignableListener()
ctx.assignable().enterRule(assign_listener) ctx.assignable().enterRule(assign_listener)
init = None # or value from ExpressionListener
if ctx.singleExpression() is not None: if ctx.singleExpression() is not None:
raise NotImplementedError("VariableDeclarator initialization") expression_listener = ExpressionListener()
ctx.singleExpression().enterRule(expression_listener)
# ctx.singleExpression().enterRule(expression_listener) # FIXME No ExpressionListener yet init = expression_listener.expression
init = None # value from ExpressionListener
self._var_decl = nodes.VariableDeclarator(loc, assign_listener.result, init) self._var_decl = nodes.VariableDeclarator(loc, assign_listener.result, init)
@ -88,6 +161,17 @@ class StatementListener(JSBaseListener):
return self._stmt return self._stmt
def __init__(self, in_loop: bool = False, in_func: bool = False):
"""
Statement listener. Generates a Statement.
Args:
in_loop (bool): allow `continue` and `break` statements
in_func (bool): allow `return` statement
"""
self._in_loop = in_loop
self._in_func = in_func
def enterStatement(self, ctx: JavaScriptParser.StatementContext): def enterStatement(self, ctx: JavaScriptParser.StatementContext):
"""Obtain an actual statement.""" """Obtain an actual statement."""
logging.debug("Entered section Statement") logging.debug("Entered section Statement")
@ -137,17 +221,17 @@ class StatementListener(JSBaseListener):
def enterExpressionStatement( def enterExpressionStatement(
self, ctx: JavaScriptParser.ExpressionStatementContext self, ctx: JavaScriptParser.ExpressionStatementContext
): ):
"""Listener for ExpressionStatement. """Listener for ExpressionStatement."""
TODO: check up expression containers.
"""
logging.debug("Entered section ExpressionStatement") logging.debug("Entered section ExpressionStatement")
raise NotImplementedError("ExpressionStatement") expr_listener = ExpressionListener()
ctx.expressionSequence().enterRule(expr_listener)
loc = _get_source_location(ctx, None)
self._stmt = nodes.ExpressionStatement(loc, expr_listener.expression)
def enterIfStatement(self, ctx: JavaScriptParser.IfStatementContext): def enterIfStatement(self, ctx: JavaScriptParser.IfStatementContext):
"""Listener for IfStatement.""" """Listener for IfStatement."""
logging.debug("Entered section IfStatement") logging.debug("Entered section IfStatement")
raise NotImplementedError("ExpressionStatement") # FIXME raise NotImplementedError("IfStatement")
pass
def enterFunctionDeclaration( def enterFunctionDeclaration(
self, ctx: JavaScriptParser.FunctionDeclarationContext self, ctx: JavaScriptParser.FunctionDeclarationContext
@ -156,7 +240,54 @@ class StatementListener(JSBaseListener):
logging.debug("Entered section FunctionDeclaration") logging.debug("Entered section FunctionDeclaration")
raise NotImplementedError("FunctionDeclaration") raise NotImplementedError("FunctionDeclaration")
# TODO: import/export, ClassDeclaration, iter statements, continue. break, return def enterDoStatement(self, ctx: JavaScriptParser.DoStatementContext):
"""Listener for DoStatement (do-while)."""
raise NotImplementedError("DoWhileStatement")
def enterWhileStatement(self, ctx: JavaScriptParser.WhileStatementContext):
"""Listener for WhileStatement."""
logging.debug("Entered section WhileStatement")
raise NotImplementedError("WhileStatement")
def enterForStatement(self, ctx: JavaScriptParser.ForStatementContext):
"""Listener for ForStatement."""
logging.debug("Entered section ForStatement")
raise NotImplementedError("ForStatement")
def enterForInStatement(self, ctx: JavaScriptParser.ForInStatementContext):
"""Listener for ForInStatement."""
logging.debug("Entered section ForInStatement")
raise NotImplementedError("ForInStatement")
def enterContinueStatement(self, ctx: JavaScriptParser.ContinueStatementContext):
logging.debug("Entered section ContinueStatement")
raise NotImplementedError("ContinueStatement")
def enterBreakStatement(self, ctx: JavaScriptParser.BreakStatementContext):
logging.debug("Entered BreakStatement")
raise NotImplementedError("BreakStatement")
def enterReturnStatement(self, ctx: JavaScriptParser.ReturnStatementContext):
logging.debug("Entered ReturnStatement")
raise NotImplementedError("ReturnStatement")
def enterImportStatement(self, ctx: JavaScriptParser.ImportStatementContext):
logging.debug("Entered ImportStatement")
raise NotImplementedError("ImportStatement")
def enterExportDeclaration(self, ctx: JavaScriptParser.ExportDeclarationContext):
logging.debug("Entered ExportDeclaration")
raise NotImplementedError("ExportDeclaration")
def enterExportDefaultDeclaration(
self, ctx: JavaScriptParser.ExportDefaultDeclarationContext
):
logging.debug("Entered ExportDefaultDeclaration")
raise NotImplementedError("ExportDefaultDeclaration")
def enterClassDeclaration(self, ctx: JavaScriptParser.ClassDeclarationContext):
logging.debug("Entered ClassDeclaration")
raise NotImplementedError("ClassDeclaration")
class SourceElementListener(JSBaseListener): class SourceElementListener(JSBaseListener):

View File

@ -1,15 +1,19 @@
from antlr4.error.ErrorListener import ErrorListener from antlr4.error.ErrorListener import ErrorListener
import logging import logging
from antlr4.error.Errors import ParseCancellationException
class LogErrorListener(ErrorListener): class LogErrorListener(ErrorListener):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e): def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e):
logging.debug( logging.critical("SyntaxError: %s", msg)
"{}\n{}\n{}\n{}\n{}".format(offendingSymbol, line, column, msg, e) raise ParseCancellationException("Syntax Error lol")
) # logging.debug(
# "{}\n{}\n{}\n{}\n{}".format(offendingSymbol, line, column, msg, e)
# )
def reportAmbiguity( def reportAmbiguity(
self, recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs self, recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs

View File

@ -1,5 +1,5 @@
[pytest] [pytest]
envlist = py38 envlist = py37,py38
[testenv] [testenv]
changedir = tests changedir = tests