def ParseUntil(self, rbp): """ Parse to the right, eating tokens until we encounter a token with binding power LESS THAN OR EQUAL TO rbp. """ # TODO: use Kind.Eof if self.op_id in (Id.Eof_Real, Id.Eof_RParen, Id.Eof_Backtick): raise TdopParseError('Unexpected end of input') t = self.cur_word self.Next() # skip over the token, e.g. ! ~ + - null_info = self.spec.LookupNud(word.ArithId(t)) node = null_info.nud(self, t, null_info.bp) while True: t = self.cur_word try: left_info = self._Led(word.ArithId(t)) except KeyError: raise TdopParseError('Invalid token %s' % t) # Examples: # If we see 1*2+ , rbp = 27 and lbp = 25, so stop. # If we see 1+2+ , rbp = 25 and lbp = 25, so stop. # If we see 1**2**, rbp = 26 and lbp = 27, so keep going. if rbp >= left_info.lbp: break self.Next() # skip over the token, e.g. / * node = left_info.led(self, t, node, left_info.rbp) return node
def LeftIncDec(p, w, left, rbp): """ For i++ and i-- """ if word.ArithId(w) == Id.Arith_DPlus: op_id = Id.Node_PostDPlus elif word.ArithId(w) == Id.Arith_DMinus: op_id = Id.Node_PostDMinus else: raise AssertionError child = tdop.ToLValue(left) return ast.UnaryAssign(op_id, child)
def LeftIncDec(p, w, left, rbp): """ For i++ and i-- """ if not tdop.IsLValue(left): raise tdop.ParseError("Can't assign to %r (%s)" % (left, left.token)) if word.ArithId(w) == Id.Arith_DPlus: op_id = Id.Node_PostDPlus elif word.ArithId(w) == Id.Arith_DMinus: op_id = Id.Node_PostDMinus else: raise AssertionError return ast.ArithUnary(op_id, left)
def LeftIndex(p, w, left, unused_bp): """Array indexing, in both LValue and RValue context. LValue: f[0] = 1 f[x+1] = 2 RValue: a = f[0] b = f[x+1] On RHS, you can have: 1. a = f[0] 2. a = f(x, y)[0] 3. a = f[0][0] # in theory, if we want character indexing? NOTE: a = f[0].charAt() is probably better On LHS, you can only have: 1. a[0] = 1 Nothing else is valid: 2. function calls return COPIES. They need a name, at least in osh. 3. strings don't have mutable characters. """ if not tdop.IsIndexable(left): p_die("%s can't be indexed", left, word=w) index = p.ParseUntil(0) p.Eat(Id.Arith_RBracket) return ast.ArithBinary(word.ArithId(w), left, index)
def LeftAssign(p, w, left, rbp): """ Normal binary operator like 1+2 or 2*3, etc. """ # x += 1, or a[i] += 1 lhs = ToLValue(left) if lhs is None: p_die("Can't assign to %r", lhs, word=w) return ast.BinaryAssign(word.ArithId(w), lhs, p.ParseUntil(rbp))
def NullIncDec(p, w, bp): """ ++x or ++x[1] """ right = p.ParseUntil(bp) child = tdop.ToLValue(right) if child is None: p_die("This value can't be assigned to", word=w) return ast.UnaryAssign(word.ArithId(w), child)
def Next(self): """Preferred over Eat()? """ self.cur_word = self.w_parser.ReadWord(lex_mode_e.ARITH) if self.cur_word is None: error_stack = self.w_parser.Error() self.error_stack.extend(error_stack) p_die('Error reading arith word in ArithParser') self.op_id = word.ArithId(self.cur_word) return True
def LeftIndex(p, w, left, unused_bp): """ index f[x+1] """ # f[x] or f[x][y] if not tdop.IsIndexable(left): raise tdop.ParseError("%s can't be indexed" % left) index = p.ParseUntil(0) p.Eat(Id.Arith_RBracket) return ast.ArithBinary(word.ArithId(w), left, index)
def NullPrefixOp(p, w, bp): """Prefix operator. Low precedence: return, raise, etc. return x+y is return (x+y), not (return x) + y High precedence: logical negation, bitwise complement, etc. !x && y is (!x) && y, not !(x && y) """ right = p.ParseUntil(bp) return ast.ArithUnary(word.ArithId(w), right)
def Next(self): """Preferred over Eat()? """ self.cur_word = self.w_parser.ReadWord(LexMode.ARITH) if self.cur_word is None: error_stack = self.w_parser.Error() self.error_stack.extend(error_stack) self.AddErrorContext('Error reading arith word in ArithParser', word=self.cur_word) #return False raise TdopParseError() # use exceptions for now self.op_id = word.ArithId(self.cur_word) return True
def LeftAssign(p, w, left, rbp): """ Normal binary operator like 1+2 or 2*3, etc. """ # x += 1, or a[i] += 1 if not IsLValue(left): raise TdopParseError("Can't assign to %r (%s)" % (left, IdName(left.id))) # HACK: NullConstant makes this of type RightVar? Change that to something # generic? if left.tag == arith_expr_e.RightVar: lhs = ast.LeftVar(left.name) elif left.tag == arith_expr_e.ArithBinary: assert left.op_id == Id.Arith_LBracket # change a[i] to LeftIndex(a, i) lhs = ast.LeftIndex(left.left, left.right) else: raise AssertionError return ast.ArithAssign(word.ArithId(w), lhs, p.ParseUntil(rbp))
def LeftBinaryOp(p, w, left, rbp): """ Normal binary operator like 1+2 or 2*3, etc. """ # TODO: w shoudl be a TokenWord, and we should extract the token from it. return ast.ArithBinary(word.ArithId(w), left, p.ParseUntil(rbp))
def LeftAssign(p, w, left, rbp): """ Normal binary operator like 1+2 or 2*3, etc. """ # x += 1, or a[i] += 1 lhs = ToLValue(left) return ast.BinaryAssign(word.ArithId(w), lhs, p.ParseUntil(rbp))
def NullIncDec(p, w, bp): """ ++x or ++x[1] """ right = p.ParseUntil(bp) if not tdop.IsLValue(right): raise tdop.ParseError("Can't assign to %r (%s)" % (right, right.token)) return ast.ArithUnary(word.ArithId(w), right)
def LeftBinaryOp(p, w, left, rbp): """ Normal binary operator like 1+2 or 2*3, etc. """ return ast.ArithBinary(word.ArithId(w), left, p.ParseUntil(rbp))
def NullIncDec(p, w, bp): """ ++x or ++x[1] """ right = p.ParseUntil(bp) child = tdop.ToLValue(right) return ast.UnaryAssign(word.ArithId(w), child)
def Next(self): """Preferred over Eat()? """ self.cur_word = self.w_parser.ReadWord(lex_mode_e.ARITH) assert self.cur_word is not None self.op_id = word.ArithId(self.cur_word) return True