def _split_on(span: Span, split: Set[str]) -> List[Span]: start = 0 result: List[Span] = [] for i, c in enumerate(span.text() + list(split)[0]): if c in split: if i > start: result.append(span[start:i]) start = i + 1 return result
def _character_literal(span: Span) -> int: text = span.text() if text.startswith('\\'): if len(text) != 2 or text[1] not in escapes: raise SingleParseError('Invalid escape sequence: "' + text + '"', span) return ord(escapes[text[1]]) else: if len(text) > 1 or ord(text) >= 256: raise SingleParseError('Invalid character literal: "' + text + '"', span) return ord(text)
def _line(span: Span, args: Args) -> List[Instruction]: span = span.strip() text = span.text() if not text: return [] code = _code(span) if args.assertions and text[0] in ('=', '$'): if code: raise SingleParseError('Brainfuck code in assertion line', span) if text[0] == '=': return [_tape_assertion(span)] elif text[0] == '$': return [_test_input(span)] else: assert False, 'unreachable' else: return code
def _matcher(span: Span) -> Matcher: text = span.text() if text.startswith('!'): return InverseMatcher(_matcher(span[1:])) if text == '*': return WildcardMatcher() if text.startswith('@'): return LiteralMatcher(text, _character_literal(span[1:])) number_matches = re.findall('^[0-9]+$', text) if number_matches: value = int(text) if value < 0 or value >= 256: raise SingleParseError( 'Invalid cell value ' + str(value) + ', must be in range 0-255', span) return LiteralMatcher(text, int(text)) ident_matches = re.findall('^[a-zA-Z_][a-zA-Z_0-9]*$', text) if ident_matches: return VariableMatcher(text) raise SingleParseError('Invalid assertion cell: "' + text + '"', span)
def _code(span: Span) -> List[Instruction]: code: List[Instruction] = [] for i, c in enumerate(span.text()): if c in op_set: code.append(Op(c, span[i:i + 1])) return code