def feed_line(self, line):
        """Feeds one line of input into the reader machine.  This method is
        a generator that yields all top-level S-expressions that have been
        recognized on this line (including multi-line expressions whose last
        character is on this line).
        """
        self.line += 1
        pos = 0
        while pos < len(line):
            loc_start = TextLocationSingle(self.filename, self.line, pos + 1)
            if self.state is State.NORMAL:
                item_re = RE_TOKEN
                thing = 'token'
            elif self.state is State.STRING:
                item_re = RE_STRING_ITEM
                thing = 'escape sequence'
            elif self.state is State.BINARRAY:
                item_re = RE_BINARRAY_ITEM[self.binarray_base]
                thing = 'binarray item'
            else:
                assert 0
            match = item_re.match(line, pos)
            if not match:
                raise ReadError(f'{loc_start}: unknown {thing}')
            pos = match.end()
            loc_end = TextLocationSingle(self.filename, self.line, pos + 1)
            loc = loc_start - loc_end
            if match['ws_error'] is not None:
                raise ReadError(f'{loc_end}: no whitespace after token')
            if self.state is State.NORMAL:
                # Normal state -- read tokens.
                if match['lparen'] is not None:
                    self.stack.append(StackEntryList(loc_start, []))
                elif match['rparen'] is not None:
                    if not self.stack:
                        raise ReadError(f'{loc}: unmatched closing paren')
                    top = self.stack.pop()
                    if not isinstance(top, StackEntryList):
                        top.raise_unclosed_error()
                    yield from self._feed_node(top.items, top.start - loc_end)
                elif match['symbol'] is not None:
                    value = Symbol(match['symbol'])
                    yield from self._feed_node(value, loc)
                elif match['sexpr_comment'] is not None:
                    self.stack.append(StackEntryComment(loc))
                elif match['bool_value'] is not None:
                    value = match['bool_value'] == '@true'
                    yield from self._feed_node(value, loc)
                elif match['nil_value'] is not None:
                    yield from self._feed_node(None, loc)
                elif match['int_or_word'] is not None:
                    if match['number'] is not None:
                        value = int(match['number'], 0)
                    elif match['raw_char'] is not None:
                        value = ord(match['raw_char'])
                    elif match['simple_escape'] is not None:
                        value = ord(ESCAPE_TO_CHAR[match['simple_escape']])
                    elif match['hex_code'] is not None:
                        value = int(match['hex_code'], 16)
                        if value not in range(0x110000):
                            raise ReadError(
                                    f'{loc}: not a valid unicode codepoint')
                    else:
                        assert 0
                    if match['word_width'] is not None:
                        width = int(match['word_width'])
                        if value < 0:
                            value += 1 << width
                        if value not in range(1 << width):
                            raise ReadError(f'{loc}: word value out of range')
                        value = BinWord(width, value)
                    yield from self._feed_node(value, loc)
                elif match['array_width'] is not None:
                    self.binarray_base = {
                        '0b': 2,
                        '0o': 8,
                        None: 10,
                        '0x': 16,
                    }[match['array_base']]
                    self.binarray_data = []
                    self.binarray_width = int(match['array_width'])
                    self.token_start = loc_start
                    self.state = State.BINARRAY
                elif match['start_quote'] is not None:
                    self.state = State.STRING
                    self.token_start = loc_start
                    self.string_buffer = StringIO()
                    if match['string_width'] is not None:
                        self.binarray_width = int(match['string_width'])
                    else:
                        self.binarray_width = None
            elif self.state is State.STRING:
                # Inside a string.
                if match['end_quote'] is not None:
                    self.state = State.NORMAL
                    value = self.string_buffer.getvalue()
                    loc = self.token_start - loc_end
                    if self.binarray_width is not None:
                        vals = [ord(x) for x in value]
                        for x in vals:
                            if x not in range(1 << self.binarray_width):
                                raise ReadError(
                                        f'{loc}: character code out of range')
                        value = BinArray(vals, width=self.binarray_width)
                    yield from self._feed_node(value, loc)
                elif match['raw_chars'] is not None:
                    self.string_buffer.write(match['raw_chars'])
                elif match['simple_escape'] is not None:
                    c = ESCAPE_TO_CHAR[match['simple_escape']]
                    self.string_buffer.write(c)
                elif match['hex_code'] is not None:
                    code = int(match['hex_code'], 16)
                    if code not in range(0x110000):
                        raise ReadError(
                                f'{loc}: not a valid unicode codepoint')
                    self.string_buffer.write(chr(code))
                else:
                    assert 0
            elif self.state is State.BINARRAY:
                # In a BinArray.
                if match['rparen'] is not None:
                    self.state = State.NORMAL
                    value = BinArray(self.binarray_data,
                                     width=self.binarray_width)
                    loc = self.token_start - loc_end
                    yield from self._feed_node(value, loc)
                elif match['digits'] is not None:
                    value = int(match['digits'], self.binarray_base)
                    if value < 0:
                        value += 1 << self.binarray_width
                    if value not in range(1 << self.binarray_width):
                        raise ReadError(f'{loc}: word value out of range')
                    self.binarray_data.append(value)
            else:
                assert 0
Feeds one line of input into the reader machine.  This method is
        a generator that yields all top-level S-expressions that have been
        recognized on this line (including multi-line expressions whose last
        character is on this line).