import io from typing import Optional, Tuple from tclint.parser import Parser from tclint.commands import CommandArgError from tclint.syntax_tree import ( BracedWord, BareWord, BracedExpression, List, QuotedWord, Expression, ) from tclint.lexer import TclSyntaxError, Lexer, TOK_EOF class CustomParser(Parser): def __init__(self, debug=False, command_plugins=None): super().__init__(debug, command_plugins) # Used to normalize newlines consistently with open()'s universal newlines mode. self._decoder = io.IncrementalNewlineDecoder(None, True) def parse(self, script: str, pos: Optional[Tuple[int, int]] = None): script = self._decoder.decode(script, True) lexer = Lexer(pos=pos) lexer.input(script) tree = self._parse_script(lexer, in_command_sub=False) assert lexer.type() == TOK_EOF, ( "Didn't reach EOF parsing script, please file a bug report." ) return tree def _parse_operator(self, ts): pos = ts.pos() # hacky logic to handle parsing legal operators if ts.value() in {"*", "&", "|"}: # one or two of these characters are legal operators operator = ts.value() ts.next() if ts.value() == operator: operator += ts.value() ts.next() elif ts.value() in {"<", ">"}: operator = ts.value() ts.next() if ts.value() in {operator, "="}: operator += ts.value() ts.next() elif ts.value() in {"=", "!"}: operator = ts.value() ts.next() if ts.value() != "=": raise TclSyntaxError( f"invalid operator in expression: {operator}", pos, ts.pos() ) operator += ts.value() ts.next() elif ts.value() in {"*", "/", "%", "+", "-", "^", "eq", "ne", "in", "ni"}: operator = ts.value() ts.next() else: message = "invalid operator in expression: " if ts.value() == "\\ ": message += ( "\\ (check for trailing whitespace if it's the end of the line)" ) else: message += ts.value() raise TclSyntaxError(message, pos, ts.pos()) return BareWord(operator, pos=pos, end_pos=ts.pos())