1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
|
from .shared import InterpreterError, string
import operator
def infixExpectationError(operator, expected):
return InterpreterError('infix: {} expects {} {} {}'.
format(operator, expected, operator, expected))
class Interpreter:
def __init__(self, context):
self.context = context
def visit(self, node):
method_name = 'visit_' + type(node).__name__
visitor = getattr(self, method_name)
return visitor(node)
def visit_ASTNode(self, node):
if node.token.kind == "number":
v = node.token.value
return float(v) if '.' in v else int(v)
elif node.token.kind == "null":
return None
elif node.token.kind == "string":
return node.token.value[1:-1]
elif node.token.kind == "true":
return True
elif node.token.kind == "false":
return False
elif node.token.kind == "identifier":
return node.token.value
def visit_UnaryOp(self, node):
value = self.visit(node.expr)
if node.token.kind == "+":
if not is_number(value):
raise InterpreterError('{} expects {}'.format('unary +', 'number'))
return value
elif node.token.kind == "-":
if not is_number(value):
raise InterpreterError('{} expects {}'.format('unary -', 'number'))
return -value
elif node.token.kind == "!":
return not self.visit(node.expr)
def visit_BinOp(self, node):
left = self.visit(node.left)
if node.token.kind == "||":
return bool(left or self.visit(node.right))
elif node.token.kind == "&&":
return bool(left and self.visit(node.right))
else:
right = self.visit(node.right)
if node.token.kind == "+":
if not isinstance(left, (string, int, float)) or isinstance(left, bool):
raise infixExpectationError('+', 'numbers/strings')
if not isinstance(right, (string, int, float)) or isinstance(right, bool):
raise infixExpectationError('+', 'numbers/strings')
if type(right) != type(left) and \
(isinstance(left, string) or isinstance(right, string)):
raise infixExpectationError('+', 'numbers/strings')
return left + right
elif node.token.kind == "-":
test_math_operands("-", left, right)
return left - right
elif node.token.kind == "/":
test_math_operands("/", left, right)
return operator.truediv(left, right)
elif node.token.kind == "*":
test_math_operands("*", left, right)
return left * right
elif node.token.kind == ">":
test_comparison_operands(">", left, right)
return left > right
elif node.token.kind == "<":
test_comparison_operands("<", left, right)
return left < right
elif node.token.kind == ">=":
test_comparison_operands(">=", left, right)
return left >= right
elif node.token.kind == "<=":
test_comparison_operands("<=", left, right)
return left <= right
elif node.token.kind == "!=":
return left != right
elif node.token.kind == "==":
return left == right
elif node.token.kind == "**":
test_math_operands("**", left, right)
return right ** left
elif node.token.value == "in":
if isinstance(right, dict):
if not isinstance(left, string):
raise infixExpectationError('in-object', 'string on left side')
elif isinstance(right, string):
if not isinstance(left, string):
raise infixExpectationError('in-string', 'string on left side')
elif not isinstance(right, list):
raise infixExpectationError(
'in', 'Array, string, or object on right side')
try:
return left in right
except TypeError:
raise infixExpectationError('in', 'scalar value, collection')
elif node.token.kind == ".":
if not isinstance(left, dict):
raise InterpreterError('infix: {} expects {}'.format(".", 'objects'))
try:
return left[right]
except KeyError:
raise InterpreterError(
'object has no property "{}"'.format(right))
def visit_List(self, node):
list = []
if node.list[0] is not None:
for item in node.list:
list.append(self.visit(item))
return list
def visit_ValueAccess(self, node):
value = self.visit(node.arr)
left = 0
right = None
if node.left:
left = self.visit(node.left)
if node.right:
right = self.visit(node.right)
if isinstance(value, (list, string)):
if node.isInterval:
if right is None:
right = len(value)
try:
return value[left:right]
except TypeError:
raise InterpreterError('cannot perform interval access with non-integers')
else:
try:
return value[left]
except IndexError:
raise InterpreterError('index out of bounds')
except TypeError:
raise InterpreterError('should only use integers to access arrays or strings')
if not isinstance(value, dict):
raise InterpreterError('infix: {} expects {}'.format('"[..]"', 'object, array, or string'))
if not isinstance(left, string):
raise InterpreterError('object keys must be strings')
try:
return value[left]
except KeyError:
return None
def visit_ContextValue(self, node):
try:
contextValue = self.context[node.token.value]
except KeyError:
raise InterpreterError(
'unknown context value {}'.format(node.token.value))
return contextValue
def visit_FunctionCall(self, node):
args = []
func_name = self.visit(node.name)
if callable(func_name):
if node.args is not None:
for item in node.args:
args.append(self.visit(item))
if hasattr(func_name, "_jsone_builtin"):
return func_name(self.context, *args)
else:
return func_name(*args)
else:
raise InterpreterError(
'{} is not callable'.format(func_name))
def visit_Object(self, node):
obj = {}
for key in node.obj:
obj[key] = self.visit(node.obj[key])
return obj
def interpret(self, tree):
return self.visit(tree)
def test_math_operands(op, left, right):
if not is_number(left):
raise infixExpectationError(op, 'number')
if not is_number(right):
raise infixExpectationError(op, 'number')
return
def test_comparison_operands(op, left, right):
if type(left) != type(right) or \
not (isinstance(left, (int, float, string)) and not isinstance(left, bool)):
raise infixExpectationError(op, 'numbers/strings')
return
def is_number(v):
return isinstance(v, (int, float)) and not isinstance(v, bool)
|