def _TwoArgs(w_parser): """Returns an expression tree to be evaluated.""" w0 = w_parser.Read() w1 = w_parser.Read() if w0.s == '!': return bool_expr.LogicalNot(bool_expr.WordTest(w1)) unary_id = TEST_UNARY_LOOKUP.get(w0.s) if unary_id is None: # TODO: # - separate lookup by unary p_die('Expected unary operator, got %r (2 args)', w0.s, word=w0) return bool_expr.Unary(unary_id, w1)
def _ParseFormatStr(self): # type: () -> printf_part_t self._Next(lex_mode_e.PrintfPercent) # move past % part = printf_part.Percent() while self.token_type in (Id.Format_Flag, Id.Format_Zero): # space and + could be implemented flag = self.cur_token.val if flag in '# +': p_die("osh printf doesn't support the %r flag", flag, token=self.cur_token) part.flags.append(self.cur_token) self._Next(lex_mode_e.PrintfPercent) if self.token_type in (Id.Format_Num, Id.Format_Star): part.width = self.cur_token self._Next(lex_mode_e.PrintfPercent) if self.token_type == Id.Format_Dot: part.precision = self.cur_token self._Next(lex_mode_e.PrintfPercent) # past dot if self.token_type in (Id.Format_Num, Id.Format_Star, Id.Format_Zero): part.precision = self.cur_token self._Next(lex_mode_e.PrintfPercent) if self.token_type in (Id.Format_Type, Id.Format_Time): part.type = self.cur_token # ADDITIONAL VALIDATION outside the "grammar". if part.type.val in 'eEfFgG': p_die("osh printf doesn't support floating point", token=part.type) # These two could be implemented. %c needs utf-8 decoding. if part.type.val == 'c': p_die("osh printf doesn't support single characters (bytes)", token=part.type) else: if self.cur_token.val: msg = 'Invalid printf format character' else: # for printf '%' msg = 'Expected a printf format character' p_die(msg, token=self.cur_token) # Do this check AFTER the floating point checks if part.precision and part.type.val[-1] not in 'fsT': p_die("precision can't be specified when here", token=part.precision) return part
def ParseForBuiltin(self): # type: () -> bool_expr_t """For test builtin.""" self._Next() node = self.ParseExpr() if self.op_id != Id.Eof_Real: p_die('Unexpected trailing word %s', word.Pretty(self.cur_word), word=self.cur_word) return node
def ParseOilArgList(self, lexer, out): # type: (Lexer, arg_list) -> token if self.parsing_expr: p_die("TODO: can't be nested") pnode, last_token = self._ParseOil(lexer, grammar_nt.oil_arglist) if 0: self.p_printer.Print(pnode) self.tr.ArgList(pnode, out) return last_token
def _ReadExtGlobPart(self): # type: () -> word_part__ExtGlobPart """ Grammar: Item = CompoundWord | EPSILON # important: @(foo|) is allowed LEFT = '@(' | '*(' | '+(' | '?(' | '!(' RIGHT = ')' ExtGlob = LEFT (Item '|')* Item RIGHT # ITEM may be empty CompoundWord includes ExtGlobPart """ left_token = self.cur_token arms = [] # type: List[word_t] spids = [] spids.append(left_token.span_id) self.lexer.PushHint(Id.Op_RParen, Id.Right_ExtGlob) self._Next(lex_mode_e.ExtGlob) # advance past LEFT read_word = False # did we just a read a word? To handle @(||). while True: self._Peek() if self.token_type == Id.Right_ExtGlob: if not read_word: arms.append(osh_word.CompoundWord()) spids.append(self.cur_token.span_id) break elif self.token_type == Id.Op_Pipe: if not read_word: arms.append(osh_word.CompoundWord()) read_word = False self._Next(lex_mode_e.ExtGlob) # lex mode EXTGLOB should only produce these 4 kinds of tokens elif self.token_kind in (Kind.Lit, Kind.Left, Kind.VSub, Kind.ExtGlob): w = self._ReadCompoundWord(lex_mode=lex_mode_e.ExtGlob) arms.append(w) read_word = True elif self.token_kind == Kind.Eof: p_die('Unexpected EOF reading extended glob that began here', token=left_token) else: raise AssertionError('Unexpected token %r' % self.cur_token) part = word_part.ExtGlobPart(left_token, arms) part.spids.extend(spids) return part
def _TwoArgs(argv): """Returns an expression tree to be evaluated.""" a0, a1 = argv if a0 == '!': return ast.LogicalNot(_StringWordTest(a1)) unary_id = _UNARY_LOOKUP.get(a0) if unary_id is None: # TODO: # - syntax error # - separate lookup by unary util.p_die('Expected unary operator, got %r (2 args)', a0) child = ast.StringWord(Id.Word_Compound, a1) return ast.BoolUnary(unary_id, child)
def _TwoArgs(w_parser): # type: (_StringWordEmitter) -> bool_expr_t """Returns an expression tree to be evaluated.""" w0 = w_parser.Read() w1 = w_parser.Read() if w0.s == '!': return bool_expr.LogicalNot(bool_expr.WordTest(w1)) unary_id = match.BracketUnary(w0.s) if unary_id == -1: # TODO: # - separate lookup by unary p_die('Expected unary operator, got %r (2 args)', w0.s, word=w0) return bool_expr.Unary(unary_id, w1)
def ParseProc(self, node): # type: (command__Proc) -> None # proc name-with-hyphens() must be accepted self._Next(lex_mode_e.ShCommand) self._Peek() # example: 'proc f[' gets you Lit_ArrayLhsOpen if self.token_type != Id.Lit_Chars: p_die('Invalid proc name %r', self.cur_token.val, token=self.cur_token) node.name = self.cur_token last_token = self.parse_ctx.ParseProc(self.lexer, node) if last_token.id == Id.Op_LBrace: # Translate to what CommandParser wants last_token.id = Id.Lit_LBrace self.buffered_word = word.Token(last_token) self._Next(lex_mode_e.ShCommand) # required
def _ReadLikeDQ(self, left_dq_token, out_parts): # type: (Optional[Token], List[word_part_t]) -> None """ Args: left_dq_token: A token if we are reading a double quoted part, or None if we're reading a here doc. out_parts: list of word_part to append to """ done = False while not done: self._Next(lex_mode_e.DQ) self._Peek() if self.token_kind == Kind.Lit: if self.token_type == Id.Lit_EscapedChar: part = word_part.EscapedLiteral(self.cur_token) # type: word_part_t else: part = self.cur_token out_parts.append(part) elif self.token_kind == Kind.Left: part = self._ReadDoubleQuotedLeftParts() out_parts.append(part) elif self.token_kind == Kind.VSub: part = simple_var_sub(self.cur_token) out_parts.append(part) # NOTE: parsing "$f(x)" would BREAK CODE. Could add a more for it # later. elif self.token_kind == Kind.Right: assert self.token_type == Id.Right_DoubleQuote, self.token_type if left_dq_token: done = True else: # In a here doc, the right quote is literal! out_parts.append(self.cur_token) elif self.token_kind == Kind.Eof: if left_dq_token: p_die('Unexpected EOF reading double-quoted string that began here', token=left_dq_token) else: # here docs will have an EOF in their token stream done = True else: raise AssertionError(self.cur_token)
def _NameInClass(self, negated_tok, tok): # type: (token, token) -> class_literal_term_t """ Like the above, but 'dot' doesn't mean anything. """ if negated_tok: # For error messages negated_speck = speck(negated_tok.id, negated_tok.span_id) else: negated_speck = None val = tok.val if val in self.POSIX_CLASSES: return posix_class(negated_speck, val) perl = self.PERL_CLASSES.get(val) if perl: return perl_class(negated_speck, perl) p_die("%r isn't a character class", val, token=tok)
def _ReadArithSub2Part(self): # type: () -> word_part__ArithSubPart """Non-standard arith sub $[a + 1].""" left_span_id = self.cur_token.span_id self._Next(lex_mode_e.Arith) anode = self._ReadArithExpr() if self.token_type != Id.Arith_RBracket: p_die('Expected ], got %r', self.cur_token.val, token=self.cur_token) right_span_id = self.cur_token.span_id node = word_part.ArithSubPart(anode) node.spids.append(left_span_id) node.spids.append(right_span_id) return node
def _ReadArithSubPart(self): # type: () -> word_part__ArithSubPart """ Read an arith substitution, which contains an arith expression, e.g. $((a + 1)). """ left_span_id = self.cur_token.span_id # The second one needs to be disambiguated in stuff like stuff like: # $(echo $(( 1+2 )) ) self.lexer.PushHint(Id.Op_RParen, Id.Right_ArithSub) # NOTE: To disambiguate $(( as arith sub vs. command sub and subshell, we # could save the lexer/reader state here, and retry if the arithmetic parse # fails. But we can almost always catch this at parse time. There could # be some exceptions like: # $((echo * foo)) # looks like multiplication # $((echo / foo)) # looks like division self._Next(lex_mode_e.Arith) anode = self._ReadArithExpr() if self.token_type != Id.Arith_RParen: p_die('Expected first ) to end arith sub, got %r', self.cur_token.val, token=self.cur_token) self._Next(lex_mode_e.Outer) # TODO: This could be DQ or ARITH too # PROBLEM: $(echo $(( 1 + 2 )) ) # Two right parens break the Id.Eof_RParen scheme self._Peek() if self.token_type != Id.Right_ArithSub: p_die('Expected second ) to end arith sub, got %r', self.cur_token.val, token=self.cur_token) right_span_id = self.cur_token.span_id node = word_part.ArithSubPart(anode) node.spids.append(left_span_id) node.spids.append(right_span_id) return node
def ParseVarDecl(self, kw_token, lexer): # type: (Token, Lexer) -> Tuple[command__VarDecl, Token] """ var mylist = [1, 2, 3] """ # TODO: We do need re-entrancy for var x = @[ (1+2) ] and such if self.parsing_expr: p_die("ShAssignment expression can't be nested like this", token=kw_token) self.parsing_expr = True try: pnode, last_token = self.e_parser.Parse(lexer, grammar_nt.oil_var_decl) finally: self.parsing_expr = False if 0: self.p_printer.Print(pnode) ast_node = self.tr.MakeVarDecl(pnode) ast_node.keyword = kw_token # VarDecl didn't fill this in return ast_node, last_token
def _Classify(gr, tok): # type: (Grammar, token) -> int # We have to match up what ParserGenerator.make_grammar() did when # calling make_label() and make_first(). See classify() in # opy/pgen2/driver.py. # 'x' and 'for' are both tokenized as Expr_Name. This handles the 'for' # case. if tok.id == Id.Expr_Name: if tok.val in gr.keywords: return gr.keywords[tok.val] # This handles 'x'. typ = tok.id.enum_id if typ in gr.tokens: return gr.tokens[typ] type_str = '' if tok.id == Id.Unknown_Tok else (' (%s)' % tok.id.name) p_die('Unexpected token in expression mode%s', type_str, token=tok)
def _Tuple(self, children): # type: (List[PNode]) -> expr_t n = len(children) # (x) -- not a tuple if n == 1: return self.Expr(children[0]) # x, and (x,) aren't allowed if n == 2: p_die('Write singleton tuples with tup(), not a trailing comma', token=children[1].tok) elts = [] # type: List[expr_t] for i in xrange(0, n, 2): # skip commas p_node = children[i] elts.append(self.Expr(p_node)) return expr.Tuple(elts, expr_context_e.Store) # unused expr_context_e
def _ReadArithWord(self): # type: () -> Tuple[word_t, bool] """Helper function for ReadArithWord.""" self._Peek() if self.token_kind == Kind.Unknown: p_die('Unexpected token in arithmetic context', token=self.cur_token) elif self.token_kind == Kind.Eof: # Just return EOF token w = osh_word.TokenWord(self.cur_token) # type: word_t return w, False elif self.token_kind == Kind.Ignored: # Space should be ignored. TODO: change this to SPACE_SPACE and # SPACE_NEWLINE? or SPACE_TOK. self._Next(lex_mode_e.Arith) return None, True # Tell wrapper to try again elif self.token_kind in (Kind.Arith, Kind.Right): # Id.Right_ArithSub IS just a normal token, handled by ArithParser self._Next(lex_mode_e.Arith) w = osh_word.TokenWord(self.cur_token) return w, False elif self.token_kind in (Kind.Lit, Kind.Left): w = self._ReadCompoundWord(lex_mode=lex_mode_e.Arith) return w, False elif self.token_kind == Kind.VSub: part = word_part.SimpleVarSub(self.cur_token) self._Next(lex_mode_e.Arith) w = osh_word.CompoundWord([part]) return w, False else: assert False, ("Unexpected token parsing arith sub: %s" % self.cur_token) raise AssertionError("Shouldn't get here")
def _ReadSliceVarOp(self): # type: () -> suffix_op__Slice """ VarOf ':' ArithExpr (':' ArithExpr )? """ self._Next(lex_mode_e.Arith) self._Peek() if self.token_type == Id.Arith_Colon: # A pun for Id.VOp2_Colon begin = None # no beginning specified else: begin = self._ReadArithExpr() if self.token_type == Id.Arith_RBrace: return suffix_op.Slice(begin, None) # No length specified # Id.Arith_Colon is a pun for Id.VOp2_Colon if self.token_type == Id.Arith_Colon: self._Next(lex_mode_e.Arith) length = self._ReadArithExpr() return suffix_op.Slice(begin, length) p_die("Unexpected token in slice: %r", self.cur_token.val, token=self.cur_token)
def _Classify(gr, tok): # type: (Grammar, token) -> int # We have to match up what ParserGenerator.make_grammar() did when # calling make_label() and make_first(). See classify() in # opy/pgen2/driver.py. # 'x' and 'for' are both tokenized as Expr_Name. This handles the 'for' # case. if tok.id == Id.Expr_Name: ilabel = gr.keywords.get(tok.val) if ilabel is not None: return ilabel # This handles 'x'. typ = tok.id.enum_id ilabel = gr.tokens.get(typ) if ilabel is not None: return ilabel p_die('Invalid token %s', tok, token=tok)
def _PlaceList(self, p_node): # type: (PNode) -> List[place_expr_t] """ place_list: expr (',' expr)* """ assert p_node.typ == grammar_nt.place_list places = [] # type: List[place_expr_t] for p in p_node.children[::2]: e = self.Expr(p) if e.tag == expr_e.Var: # COMPATIBILITY hack assert isinstance(e, expr__Var) places.append(place_expr.Var(e.name)) elif e.tag in (place_expr_e.Var, place_expr_e.Subscript, place_expr_e.Attribute): places.append(cast(place_expr_t, e)) else: # This blame mechanism seems to work. Otherwise we don't have a method # to blame an arbitrary expr_t. p_die("Can't assign to this expression", token=p.tok if p.tok else None) return places
def _NameInRegex(self, negated_tok, tok): # type: (token, token) -> re_t if negated_tok: # For error messages negated_speck = speck(negated_tok.id, negated_tok.span_id) else: negated_speck = None val = tok.val if val == 'dot': if negated_tok: p_die("Can't negate this symbol", token=tok) return tok if val in self.POSIX_CLASSES: return posix_class(negated_speck, val) perl = self.PERL_CLASSES.get(val) if perl: return perl_class(negated_speck, perl) p_die("%r isn't a character class", val, token=tok)
def _ReadSliceVarOp(self): # type: () -> suffix_op__Slice """ VarOf ':' ArithExpr (':' ArithExpr )? """ self._Next(lex_mode_e.Arith) self._Peek() if self.token_type == Id.Arith_Colon: # A pun for Id.VOp2_Colon # no beginning specified begin = None # type: Optional[arith_expr_t] else: begin = self._ReadArithExpr() if self.token_type == Id.Arith_RBrace: no_length = None # type: Optional[arith_expr_t] # No length specified return suffix_op.Slice(begin, no_length) # Id.Arith_Colon is a pun for Id.VOp2_Colon if self.token_type == Id.Arith_Colon: self._Next(lex_mode_e.Arith) length = self._ReadArithExpr() return suffix_op.Slice(begin, length) p_die("Expected : or } in slice", token=self.cur_token)
def _ReadPatSubVarOp(self, lex_mode): # type: (lex_mode_t) -> suffix_op__PatSub """ Match = ('/' | '#' | '%') WORD VarSub = ... | VarOf '/' Match '/' WORD """ pat = self._ReadVarOpArg(lex_mode, eof_type=Id.Lit_Slash, empty_ok=False) assert isinstance(pat, word__CompoundWord) # Because empty_ok=False if len(pat.parts) == 1: ok, s, quoted = word.StaticEval(pat) if ok and s == '/' and not quoted: # Looks like ${a////c}, read again self._Next(lex_mode) self._Peek() p = word_part.LiteralPart(self.cur_token) pat.parts.append(p) if len(pat.parts) == 0: p_die('Pattern in ${x/pat/replace} must not be empty', token=self.cur_token) replace_mode = Id.Undefined_Tok # Check for / # % modifier on pattern. first_part = pat.parts[0] if isinstance(first_part, word_part__LiteralPart): lit_id = first_part.token.id if lit_id in (Id.Lit_Slash, Id.Lit_Pound, Id.Lit_Percent): pat.parts.pop(0) replace_mode = lit_id # NOTE: If there is a modifier, the pattern can be empty, e.g. # ${s/#/foo} and ${a/%/foo}. if self.token_type == Id.Right_VarSub: # e.g. ${v/a} is the same as ${v/a/} -- empty replacement string return suffix_op.PatSub(pat, None, replace_mode) if self.token_type == Id.Lit_Slash: replace = self._ReadVarOpArg(lex_mode) # do not stop at / self._Peek() if self.token_type != Id.Right_VarSub: # NOTE: I think this never happens. # We're either in the VS_ARG_UNQ or VS_ARG_DQ lex state, and everything # there is Lit_ or Left_, except for }. p_die("Expected } after replacement string, got %s", self.cur_token, token=self.cur_token) return suffix_op.PatSub(pat, replace, replace_mode) # Happens with ${x//} and ${x///foo}, see test/parse-errors.sh p_die("Expected } after pat sub, got %r", self.cur_token.val, token=self.cur_token)
def ReadForExpression(self): # type: () -> command__ForExpr """Read ((i=0; i<5; ++i)) -- part of command context.""" self._NextNonSpace() # skip over (( self._Peek() if self.token_type == Id.Arith_Semi: # for (( ; i < 10; i++ )) init_node = None # type: Optional[arith_expr_t] else: init_node = self._ReadArithExpr() self._NextNonSpace() self._Peek() if self.token_type == Id.Arith_Semi: # for (( ; ; i++ )) cond_node = None # type: Optional[arith_expr_t] else: cond_node = self._ReadArithExpr() self._NextNonSpace() self._Peek() if self.token_type == Id.Arith_RParen: # for (( ; ; )) update_node = None # type: Optional[arith_expr_t] else: update_node = self._ReadArithExpr() self._NextNonSpace() self._Peek() if self.token_type != Id.Arith_RParen: p_die('Expected ) to end for loop expression', token=self.cur_token) self._Next(lex_mode_e.ShCommand) node = command.ForExpr() node.init = init_node node.cond = cond_node node.update = update_node return node
def _PlaceList(self, p_node): # type: (PNode) -> List[place_expr_t] """ place_list: expr (',' expr)* """ assert p_node.typ == grammar_nt.place_list places = [] # type: List[place_expr_t] n = len(p_node.children) for i in xrange(0, n, 2): # was children[::2] p = p_node.children[i] e = self.Expr(p) UP_e = e tag = e.tag_() if tag == expr_e.Var: # COMPATIBILITY hack e = cast(expr__Var, UP_e) places.append(place_expr.Var(e.name)) elif tag in ( place_expr_e.Var, place_expr_e.Subscript, place_expr_e.Attribute): places.append(cast(place_expr_t, UP_e)) else: # This blame mechanism seems to work. Otherwise we don't have a method # to blame an arbitrary expr_t. p_die("Can't assign to this expression", token=p.tok if p.tok else None) return places
def Parse(self, lexer, start_symbol): # type: (Lexer, int) -> Tuple[PNode, Token] # Reuse the parser self.push_parser.setup(start_symbol) try: last_token = _PushOilTokens(self.parse_ctx, self.gr, self.push_parser, lexer) except parse.ParseError as e: #log('ERROR %s', e) # TODO: # - Describe what lexer mode we're in (Invalid syntax in regex) # - Maybe say where the mode started # - Id.Unknown_Tok could say "This character is invalid" # ParseError has a "too much input" case but I haven't been able to # tickle it. Mabye it's because of the Eof tokens? p_die('Syntax error in expression (near %s)', ui.PrettyId(e.tok.id), token=e.tok) #raise error.Parse('Syntax error in expression', token=e.tok) return self.push_parser.rootnode, last_token
def Parse(self): self._Next(lex_mode_e.PrintfOuter) parts = [] while True: if (self.token_kind == Kind.Char or self.token_type == Id.Format_EscapedPercent): # TODO: Could handle Char_BadBackslash. # Maybe make that a different kind? parts.append(printf_part.Literal(self.cur_token)) elif self.token_type == Id.Format_Percent: parts.append(self._ParseFormatStr()) elif self.token_type == Id.Eof_Real: break else: p_die('Invalid token %r', token=self.cur_token) self._Next(lex_mode_e.PrintfOuter) return parts
def _ReadPatSubVarOp(self): # type: () -> suffix_op__PatSub """ Match = ('/' | '#' | '%') WORD VarSub = ... | VarOf '/' Match '/' WORD """ # Exception: VSub_ArgUnquoted even if it's quoted # stop at eof_type=Lit_Slash, empty_ok=False UP_pat = self._ReadVarOpArg3(lex_mode_e.VSub_ArgUnquoted, Id.Lit_Slash, False) assert UP_pat.tag_() == word_e.Compound, UP_pat # Because empty_ok=False pat = cast(compound_word, UP_pat) if len(pat.parts) == 1: ok, s, quoted = word_.StaticEval(pat) if ok and s == '/' and not quoted: # Looks like ${a////c}, read again self._Next(lex_mode_e.VSub_ArgUnquoted) self._Peek() pat.parts.append(self.cur_token) if len(pat.parts) == 0: p_die('Pattern in ${x/pat/replace} must not be empty', token=self.cur_token) replace_mode = Id.Undefined_Tok # Check for / # % modifier on pattern. UP_first_part = pat.parts[0] if UP_first_part.tag_() == word_part_e.Literal: lit_id = cast(Token, UP_first_part).id if lit_id in (Id.Lit_Slash, Id.Lit_Pound, Id.Lit_Percent): pat.parts.pop(0) replace_mode = lit_id # NOTE: If there is a modifier, the pattern can be empty, e.g. # ${s/#/foo} and ${a/%/foo}. if self.token_type == Id.Right_DollarBrace: # e.g. ${v/a} is the same as ${v/a/} -- empty replacement string return suffix_op.PatSub(pat, None, replace_mode) if self.token_type == Id.Lit_Slash: replace = self._ReadVarOpArg(lex_mode_e.VSub_ArgUnquoted) # do not stop at / self._Peek() if self.token_type != Id.Right_DollarBrace: # NOTE: I think this never happens. # We're either in the VS_ARG_UNQ or VS_ARG_DQ lex state, and everything # there is Lit_ or Left_, except for }. p_die("Expected } after replacement string, got %s", ui.PrettyId(self.token_type), token=self.cur_token) return suffix_op.PatSub(pat, replace, replace_mode) # Happens with ${x//} and ${x///foo}, see test/parse-errors.sh p_die('Expected } or / to close pattern', token=self.cur_token)
def _RangeChar(self, p_node): # type: (PNode) -> str """Evaluate a range endpoints. - the 'a' in 'a'-'z' - the \x00 in \x00-\x01 etc. TODO: This function doesn't respect the LST invariant. """ assert p_node.typ == grammar_nt.range_char, p_node children = p_node.children typ = children[0].typ if ISNONTERMINAL(typ): # 'a' in 'a'-'b' if typ == grammar_nt.sq_string: sq_part = cast(single_quoted, children[0].children[1].tok) tokens = sq_part.tokens if len( tokens ) > 1: # Can happen with multiline single-quoted strings p_die(RANGE_POINT_TOO_LONG, part=sq_part) if len(tokens[0].val) > 1: p_die(RANGE_POINT_TOO_LONG, part=sq_part) s = tokens[0].val[0] return s if typ == grammar_nt.char_literal: raise AssertionError('TODO') # TODO: This brings in a lot of dependencies, and this type checking # errors. We want to respect the LST invariant anyway. #from osh import word_compile #tok = children[0].children[0].tok #s = word_compile.EvalCStringToken(tok.id, tok.val) #return s raise NotImplementedError() else: # Expr_Name or Expr_DecInt tok = p_node.tok if tok.id in (Id.Expr_Name, Id.Expr_DecInt): # For the a in a-z, 0 in 0-9 if len(tok.val) != 1: p_die(RANGE_POINT_TOO_LONG, token=tok) return tok.val[0] raise NotImplementedError()
def NullError(p, t, bp): # type: (TdopParser, word_t, int) -> arith_expr_t # TODO: I need position information p_die("Token can't be used in prefix position", word=t) return None # never reached
def LeftError(p, t, left, rbp): # type: (TdopParser, word_t, arith_expr_t, int) -> arith_expr_t # Hm is this not called because of binding power? p_die("Token can't be used in infix position", word=t) return None # never reached