📄 narcissus.py
字号:
if tt != consts["END"] and \
tt != consts["NEWLINE"] and \
tt != consts["SEMICOLON"] and \
tt != consts["RIGHT_CURLY"]:
raise NarcissusError("Missing ; before statement", t)
t.match(consts["SEMICOLON"])
return n
def FunctionDefinition(t, x, requireName, functionForm):
# t => an instance of Tokenizer
f = Node(t)
if f.type != consts["FUNCTION"]:
f.type = (f.value == "get") and consts["GETTER"] or consts["SETTER"]
# print f.type
if t.match(consts["IDENTIFIER"]):
f.name = t.token().value
t.mustMatch(consts["LEFT_PAREN"])
f.params = []
while True:
tt = t.get()
print 'TT IZ',tt
if tt==consts["RIGHT_PAREN"]: break
if tt != consts["IDENTIFIER"]:
raise NarcissusError("Missing formal parameters", t)
f.params.append(t.token().value)
if t.peek() != consts["RIGHT_PAREN"]:
t.mustMatch(consts["COMMA"])
t.mustMatch(consts["LEFT_CURLY"])
x2 = CompilerContext(True)
f.body = Script(t, x2)
t.mustMatch(consts["RIGHT_CURLY"])
f.end = t.token().end
f.functionForm = functionForm
if functionForm == consts.get("DECLARED_FORM"):
# print 'okay'
x.funDecls.append(f)
return f
def Variables(t, x):
n = Node(t)
while True:
t.mustMatch(consts["IDENTIFIER"])
n2 = Node(t)
n2.name = n2.value
if t.match(consts["ASSIGN"]):
if t.token().assignOp:
raise NarcissusError("Invalid variable initialization", t)
# print 'Initializing var...'
n2.initializer = Expression(t, x, consts["COMMA"])
n2.readOnly = ( n.type == consts["CONST"])
# print 'vars=>',n2.type
# print 'Starting to push'
n.push(n2)
# print "Push ended"
x.varDecls.append(n2)
if not t.match(consts["COMMA"]): break
return n
def ParenExpression(t, x):
t.mustMatch(consts["LEFT_PAREN"])
n = Expression(t, x)
t.mustMatch(consts["RIGHT_PAREN"])
return n
def Expression(t, x, stop = None):
operators = List2()
operands = List2()
print 'In Expression',len(operands)
bl = x.bracketLevel
cl = x.curlyLevel
pl = x.parenLevel
hl = x.hookLevel
def Reduce(operators, operands, t):
# print 'Reduce called!'
#for item in operators:
# print 'operator=>',item
n = operators.pop()
print 'N=>',n.type
op = n.type
arity = opArity[op]
print 'Arity=>',arity
if arity == -2:
if len(operands) >= 2:
# Flatten left-associative trees
left = operands[len(operands) - 2]
print 'Left=>',left
if left.type == op:
print 'Dude!'
right = operands.pop()
left.append(right)
return (operators, operands, left)
arity = 2
# Always use push to add operands to n, to update start and end.
# print 'Before slicing =>',len(operands)
startidx, endidx = len(operands)-arity, 2*len(operands) - arity
a = operands[startidx:endidx]
operands = List2(operands[:startidx])
# print 'After slicing =>',len(operands)
# for x in operands:
# print "Optype=>",x.type
# print a
# print arity
for x in range(arity):
# print x
# print a[x]
# print 'Type=>',a[x].type
n.push(a[x])
# Include closing bracket or postfix operator in [start,end).
if n.end < t.token().end:
n.end = t.token().end
operands.append(n)
# print 'Operands length =>',len(operands)
return (operators, operands, n)
while True: # (t.token() and t.token().type != consts["END"]):
if (t.token() and t.token().type == consts["END"]): break
tt = t.get()
if tt == consts["END"]: break
print 'TT ==>',tt
# Stop only if tt matches the optional stop parameter, and that
# token is not quoted by some kind of bracket.
if (tt==stop) and \
(x.bracketLevel == bl) and \
(x.curlyLevel == cl) and \
(x.parenLevel == pl) and \
(x.hookLevel == hl):
break
if tt == consts["SEMICOLON"]:
# NB: cannot be empty, Statement handled that.
break
elif (tt==consts["ASSIGN"]) or \
(tt==consts["HOOK"]) or \
(tt==consts["COLON"]):
if t.scanOperand:
break
# print 'here....'
# print operators
# print operands
# print len(operands)
# Use >, not >=, for right-associative ASSIGN and HOOK/COLON.
while len(operators) > 0 and \
opPrecedence.get(operators.last().type) and \
(opPrecedence.get(operators.last().type) > opPrecedence.get(tt)):
operators, operands, ret = Reduce(operators, operands, t)
# print 'Operands length2 =>',len(operands)
if tt == consts["COLON"]:
n = operators.last()
if n.type != consts["HOOK"]:
raise NarcissusError("Invalid label", t)
n.type = consts["CONDITIONAL"]
x.hookLevel -= 1
else:
operators.append(Node(t))
if tt == consts["ASSIGN"]:
# print operands
operands.last().assignOp = t.token().assignOp
else:
x.hookLevel += 1 # tt == HOOK
t.scanOperand = True
# Treat comma as left-associative so reduce can fold left-heavy
# COMMA trees into a single array.
elif tt in (consts["COMMA"], consts["OR"], consts["AND"], consts["BITWISE_OR"],
consts["BITWISE_XOR"], consts["BITWISE_AND"], consts["EQ"],
consts["NE"], consts["STRICT_EQ"], consts["STRICT_NE"],
consts["LT"], consts["LE"], consts["GE"], consts["GT"],
consts["INSTANCEOF"], consts["LSH"], consts["RSH"],
consts["URSH"], consts["PLUS"], consts["MINUS"], consts["MUL"],
consts["DIV"], consts["MOD"], consts["DOT"], consts["IN"]):
# print 'here...'
# An in operator should not be parsed if we're parsing the head of
# a for (...) loop, unless it is in the then part of a conditional
# expression, or parenthesized somehow.
if (tt == consts["IN"]) and \
x.inForLoopInit and \
x.hookLevel == 0 and \
x.bracketLevel == 0 and \
x.curlyLevel == 0 and \
x.parenLevel == 0:
break
if t.scanOperand:
break
while (len(operators)) and \
(opPrecedence.get(operators.last().type)) and \
(opPrecedence.get(operators.last().type) >= opPrecedence.get(tt)):
operators, operands, ret = Reduce(operators, operands, t)
# print 'Operands length2 =>',len(operands)
if tt == consts["DOT"]:
t.mustMatch(consts["IDENTIFIER"])
node = Node(t, consts["DOT"])
node.push(operands.pop())
node.push(Node(t))
operands.append(node)
else:
operators.append(Node(t))
# print operators
t.scanOperand = True
elif tt in (consts["DELETE"], consts["VOID"], consts["TYPEOF"],
consts["NOT"], consts["BITWISE_NOT"], consts["UNARY_PLUS"],
consts["UNARY_MINUS"], consts["NEW"]):
if not t.scanOperand:
break
operators.append(Node(t))
elif tt in (consts["INCREMENT"], consts["DECREMENT"]):
if t.scanOperand:
operators.append(Node(t)) # prefix increment or decrement
else:
# Use >, not >=, so postfix has higher precedence than prefix.
while (len(operators)) and \
(opPrecedence.get(operators.last().type)) and \
(opPrecedence.get(operators.last().type) > opPrecedence.get(tt)):
operators, operands, ret = Reduce(operators, operands, t)
n = Node(t, tt)
n.push(operands.pop())
n.postfix = True
operands.append(n)
elif tt == consts["FUNCTION"]:
# print 'HERE'
if not t.scanOperand:
break
operands.append(FunctionDefinition(t, x, False, consts.get("EXPRESSED_FORM")))
t.scanOperand = False
elif tt in (consts["NULL"], consts["THIS"], consts["TRUE"], consts["FALSE"],
consts["IDENTIFIER"], consts["NUMBER"], consts["STRING"],
consts["REGEXP"]):
if not t.scanOperand:
break
# print 'HERE2'
operands.append(Node(t))
# print operands
t.scanOperand = False
elif tt == consts["LEFT_BRACKET"]:
if t.scanOperand:
# Array initialiser. Parse using recursive descent, as the
# sub-grammar here is not an operator grammar.
n = Node(t, consts["ARRAY_INIT"])
while True:
tt = t.peek()
if tt == consts["RIGHT_BRACKET"]: break
if tt == consts["COMMA"]:
print 'Tt Iz',t.get()
n.push(None)
# FIXME: What is this next ?
next
n.push(Expression(t, x, consts["COMMA"]))
if not t.match(consts["COMMA"]):
continue
t.mustMatch(consts["RIGHT_BRACKET"])
operands.append(n)
t.scanOperand = False
else:
# Property indexing operator.
operators.append(Node(t, consts["INDEX"]))
t.scanOperand = True
x.bracketLevel += 1
elif tt == consts["RIGHT_BRACKET"]:
if t.scanOperand or x.bracketLevel == bl:
break
while True:
operators, operands, ret = Reduce(operators, operands, t)
if ret.type == consts["INDEX"]: break
pass
x.bracketLevel -= 1
elif tt == consts["LEFT_CURLY"]:
if not t.scanOperand:
break
# Object initialiser. As for array initialisers (see above),
# parse using recursive descent.
x.curlyLevel += 1
n = Node(t, consts["OBJECT_INIT"])
if not t.match(consts["RIGHT_CURLY"]):
while True:
tt = t.get()
print 'tt IZ',tt
if (t.token().value == 'get') or (t.token().value=='set') and (t.peek() == consts["IDENTIFIER"]):
if x.ecmaStrictMode:
raise NarcissusError("Illegal property accessor", t)
n.push(FunctionDefinition(t, x, True, consts["EXPRESSED_FORM"]))
elif tt in (consts["IDENTIFIER"], consts["NUMBER"], consts["STRING"]):
id = Node(t)
elif tt == consts["RIGHT_CURLY"]:
if x.ecmaStrictMode:
raise NarcissusError("Illegal training", t)
else:
raise NarcissusError("Invalid property name", t)
t.mustMatch(consts["COLON"])
n2 = Node(t, consts["PROPERTY_INIT"])
n2.push(id)
n2.push(Expression(t, x, consts["COMMA"]))
n.push(n2)
if not t.match(consts["COMMA"]): break
t.mustMatch(consts["RIGHT_CURLY"])
operands.append(n)
t.scanOperand = False
x.curlyLevel -= 1
elif tt == consts["RIGHT_CURLY"]:
if not t.scanOperand and x.curlyLevel != cl:
raise NarcissusError("PANIC: right curly botch", t)
break
elif tt == consts["LEFT_PAREN"]:
if t.scanOperand:
operators.append(Node(t, consts["GROUP"]))
else:
print operators, type(operators)
while len(operators) and \
opPrecedence.get(operators.last().type) and \
opPrecedence.get(operators.last().type) > opPrecedence.get(consts["NEW"]):
operators, operands, ret = Reduce(operators, operands, t)
# Handle () now, to regularize the n-ary case for n > 0.
# We must set scanOperand in case there are arguments and
# the first one is a regexp or unary+/-.
n = operators.last()
# print n, n.type, consts["NEW"]
t.scanOperand = True
print 'I WENT HERE'
if t.match(consts["RIGHT_PAREN"]):
if n != None and n.type == consts["NEW"]:
print "INSIDE"
operators.pop()
n.push(operands.pop())
else:
print "OUTSIDE"
n = Node(t, consts["CALL"])
n.push(operands.pop())
n.push(Node(t, consts["LIST"]))
operands.append(n)
t.scanOperand = False
continue
if n != None and n.type == consts["NEW"]:
n.type = consts["NEW_WITH_ARGS"]
else:
operators.append(Node(t, consts["CALL"]))
x.parenLevel += 1
elif tt == consts["RIGHT_PAREN"]:
if t.scanOperand or x.parenLevel == pl:
break
while True:
print 'Operators =>',len(operators),len(operands)
operators, operands, tt = Reduce(operators, operands, t)
print 'TT=>',tt,tt.type
if (tt.type == consts["GROUP"]) or \
(tt.type == consts["CALL"]) or \
(tt.type == consts["NEW_WITH_ARGS"]):
print 'Breaking'
break
if tt != consts["GROUP"]:
n = operands.last()
if n[1].type != consts["COMMA"]:
n2 = n[1]
n[1] = Node(t, consts["LIST"])
n[1].push(n2)
else:
n[1].type = consts["LIST"]
x.parenLevel -= 1
else:
# Automatic semicolon insertion means we may scan across a newline
# and into the beginning of another statement. If so, break out of
# the while loop and let the t.scanOperand logic handle errors.
break
if x.hookLevel != hl:
raise NarcissusError("Missing : after ?", t)
if t.scanOperand:
raise NarcissusError("Missing operand", t)
# Resume default mode, scanning for operands, not operators.
t.scanOperand = True
print 'Ungetting...'
print t.unget()
# print 'HERE4'
while len(operators) > 0:
operators, operands, ret = Reduce(operators, operands, t)
# print 'Operands length2 =>',len(operands)
return operands.pop()
def parse(source, filename, line = 1):
t = Tokenizer(source, filename, line)
x = CompilerContext(False)
n = Script(t, x)
if not t.done:
raise NarcissusError("Syntax error", t)
return n
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -