예제 #1
0
def parse_asm_block_directive(directive, stack):
    prefix = directive[:4]
    infix = directive[len(prefix):len(prefix) + 1]
    suffix = directive[len(prefix) + len(infix):].rstrip()
    if prefix in ('ofix', 'bfix', 'rfix', 'isub', 'ssub', 'rsub') and infix in '+-' and suffix in ('begin', 'else', 'end'):
        if stack:
            cur_op = stack[-1]
        else:
            cur_op = (None, None)
        if suffix == 'begin':
            if prefix == cur_op[0]:
                raise SkoolParsingError('{0} inside {1}{2} block'.format(directive, cur_op[0], cur_op[1]))
            stack.append((prefix, infix))
        elif suffix == 'else':
            if cur_op[0] is None:
                raise SkoolParsingError('{0} not inside block'.format(directive))
            if prefix != cur_op[0] or infix == cur_op[1]:
                raise SkoolParsingError('{0} inside {1}{2} block'.format(directive, cur_op[0], cur_op[1]))
            stack[-1] = (prefix, infix)
        elif suffix == 'end':
            if cur_op[0] is None:
                raise SkoolParsingError('{0} has no matching start directive'.format(directive))
            if (prefix, infix) != cur_op:
                raise SkoolParsingError('{0} cannot end {1}{2} block'.format(directive, cur_op[0], cur_op[1]))
            stack.pop()
        return True
    return False
예제 #2
0
def expand_macros(writer, text, *cwd):
    global _writer, _cwd
    _writer = writer
    _cwd = cwd

    if text.find('#') < 0:
        return text

    while 1:
        search = RE_MACRO.search(text)
        if not search:
            break
        marker = search.group()
        if marker not in writer.macros:
            raise SkoolParsingError('Found unknown macro: {}'.format(marker))
        start, index = search.span()

        while RE_EXPAND.match(text, index):
            end, expr = parse_strings(text, index + 1, 1)
            text = text[:index] + expand_macros(writer, expr, *cwd) + text[end:]

        repf = writer.macros[marker]
        try:
            end, rep = repf(text, index, *cwd)
        except UnsupportedMacroError:
            raise SkoolParsingError('Found unsupported macro: {}'.format(marker))
        except MacroParsingError as e:
            raise SkoolParsingError('Error while parsing {} macro: {}'.format(marker, e.args[0]))
        text = text[:start] + rep + text[end:]

    return text
예제 #3
0
    def parse_list(self, writer, list_def, *cwd):
        text = list_def
        for ws_char in '\n\r\t':
            text = text.replace(ws_char, ' ')

        try:
            index, params = parse_brackets(text, default='')
        except ClosingBracketError:
            raise SkoolParsingError("Cannot find closing ')' in parameter list:\n{}".format(list_def))
        css_class = writer.expand(params, *cwd).strip()
        list_obj = List(css_class)

        text = writer.expand(text[index:], *cwd)
        index = 0
        while text.find('{', index) >= 0:
            item_start = text.find('{ ', index)
            if item_start < 0:
                raise SkoolParsingError("Cannot find opening '{{ ' in list item:\n{0}".format(list_def))
            item_end = text.find(' }', item_start)
            if item_end < 0:
                raise SkoolParsingError("Cannot find closing ' }}' in list item:\n{0}".format(list_def))
            list_obj.add_item(text[item_start + 1:item_end].strip())
            index = item_end + 2

        return list_obj
예제 #4
0
 def _parse_instruction(self, address, line, removed):
     try:
         skool_address = get_int_param(line[1:6])
     except ValueError:
         raise SkoolParsingError("Invalid address ({}):\n{}".format(
             line[1:6], line.rstrip()))
     if address is None:
         address = skool_address
     original_op = partition_unquoted(line[6:], ';')[0].strip()
     subbed = max(self.subs)
     if subbed:
         operations = self.subs[subbed]
     else:
         operations = [original_op]
     self.subs = defaultdict(list, {0: []})
     parsed = [parse_asm_sub_fix_directive(v)[::2] for v in operations]
     before = [i[1] for i in parsed if i[0].prepend and i[1]]
     for operation in before:
         address = self._assemble(operation, address)
     after = [(i[0].overwrite, i[1], i[0].append) for i in parsed
              if not i[0].prepend]
     if not after or after[0][2]:
         after.insert(0, (False, original_op, False))
     overwrite, operation = after.pop(0)[:2]
     operation = operation or original_op
     if operation and skool_address not in removed:
         address = self._assemble(operation, address, overwrite, removed)
     for overwrite, operation, append in after:
         if operation:
             address = self._assemble(operation, address, overwrite,
                                      removed)
     return address
예제 #5
0
 def parse_text(self, writer, text, index, *cwd):
     try:
         end = text.index(TABLE_END_MARKER, index) + len(TABLE_END_MARKER)
     except ValueError:
         marker = text[text.rindex('#', 0, index):index]
         raise SkoolParsingError("Missing table end marker: {}{}...".format(marker, text[index:index + 15]))
     return end, self.parse_table(writer, text[index:end], *cwd)
예제 #6
0
    def apply_asm_attributes(self, instruction):
        instruction.keep = self.keep

        if self.asm_labels and self.label:
            if self.label in self.labels:
                raise SkoolParsingError('Duplicate label {0} at {1}'.format(self.label, instruction.address))
            self.labels.append(self.label)
            instruction.asm_label = self.label

        if not self.html:
            sub = self.isub
            if self.rfix is not None and self.do_rfixes:
                sub = self.rfix
            elif self.bfix is not None and self.do_bfixes:
                sub = self.bfix
            elif self.ofix is not None and self.do_ofixes:
                sub = self.ofix
            elif self.rsub is not None and self.do_rsubs:
                sub = self.rsub
            elif self.ssub is not None and self.do_ssubs:
                sub = self.ssub
            if sub is not None:
                _, sub = self.apply_case('', sub)
                _, sub = self.apply_base('', sub)
                instruction.apply_sub(sub)

            instruction.warn = not self.nowarn
            instruction.nolabel = self.nolabel
            instruction.ignoreua = self.ignoreua
            instruction.ignoremrcua = self.ignoremrcua
            instruction.org = self.org

        self.reset()
예제 #7
0
 def _replace(self, text):
     for pattern, rep in self._replacements:
         try:
             text = re.sub(pattern, rep, text)
         except Exception as e:
             raise SkoolParsingError("Failed to replace '{}' with '{}': {}".format(pattern, rep, e.args[0]))
     return text
예제 #8
0
 def _get_size(self,
               operation,
               address,
               marker,
               overwrite=False,
               removed=None,
               offset=0,
               sub=True,
               skool_address=None):
     if operation.upper().startswith(('DJNZ ', 'JR ')):
         size = 2
     else:
         size = self.assembler.get_size(operation, address)
     if size:
         if overwrite:
             removed.update(range(address + offset,
                                  address + offset + size))
             marker = '|'
         if self.start <= address < self.end:
             self.instructions.append(
                 Instruction(skool_address, address, operation, sub,
                             self.keep, self.nowarn, self.data, marker))
         return size
     raise SkoolParsingError("Failed to assemble:\n {} {}".format(
         address, operation))
예제 #9
0
 def _parse_instruction(self, line, removed):
     try:
         address = get_int_param(line[1:6])
     except ValueError:
         raise SkoolParsingError("Invalid address ({}):\n{}".format(
             line[1:6], line.rstrip()))
     original_op = partition_unquoted(line[6:], ';')[0].strip()
     subbed = max(self.subs)
     if subbed:
         operations = [
             partition_unquoted(s, ';') for s in self.subs[subbed]
         ]
     else:
         operations = [(original_op, '', '')]
     self.subs = defaultdict(list, {0: []})
     for op, sep, comment in operations:
         operation = op.strip() or original_op
         if operation:
             data = assemble(operation, address)
             if data:
                 end_address = address + len(data)
                 if address not in removed:
                     self.snapshot[address:end_address] = data
                     self.base_address = min(self.base_address, address)
                     self.end_address = max(self.end_address, end_address)
                 if subbed:
                     removed.update(range(address, end_address))
                 address = end_address
             else:
                 warn("Failed to assemble:\n {} {}".format(
                     address, operation))
                 break
         original_op = None
예제 #10
0
파일: skoolctl.py 프로젝트: dpt/skoolkit
    def _get_defs_length(self, operation):
        if self.preserve_base:
            fmt = FORMAT_PRESERVE_BASE
        else:
            fmt = FORMAT_NO_BASE
        items = self.op_evaluator.split_operands(operation[5:])[:2]

        try:
            size = self.op_evaluator.eval_int(items[0])
        except ValueError:
            raise SkoolParsingError("Invalid integer '{}': {}".format(
                items[0], operation))
        size_base = _get_base(items[0], self.preserve_base)
        try:
            get_int_param(items[0])
            size_fmt = fmt[size_base].format(items[0])
        except ValueError:
            size_fmt = fmt[size_base].format(size)

        if len(items) == 1:
            return size, size_fmt

        value_base = _get_base(items[1], self.preserve_base)
        if value_base in 'dh' and not self.preserve_base:
            value_base = 'n'
        return size, '{}:{}'.format(size_fmt, value_base)
예제 #11
0
 def _ignore_block(self, text, index, marker, end_marker, rep=None):
     try:
         end = text.index(end_marker, index) + len(end_marker)
     except ValueError:
         raise SkoolParsingError("Missing end marker: {}...".format(text[index - len(marker):index + 15]))
     if rep is None:
         rep = BLOCK_SEP + text[index - len(marker):end] + BLOCK_SEP
     return -end, rep
예제 #12
0
 def _parse_instruction(self, line):
     ctl, addr_str, operation, comment = parse_instruction(line)
     try:
         address = get_int_param(addr_str)
     except ValueError:
         raise SkoolParsingError("Invalid address ({}):\n{}".format(addr_str, line.rstrip()))
     instruction = Instruction(ctl, address, operation, self.preserve_base)
     self.mode.apply_asm_directives(instruction)
     return instruction, comment
예제 #13
0
 def find_markers(self, block_indexes, text, marker, end_marker):
     index = 0
     while text.find(marker, index) >= 0:
         block_index = text.index(marker, index)
         try:
             block_end_index = text.index(end_marker, block_index) + len(end_marker)
         except ValueError:
             raise SkoolParsingError("Missing end marker: {}...".format(text[block_index:block_index + len(marker) + 15]))
         block_indexes.append(block_index)
         block_indexes.append(block_end_index)
         index = block_end_index
예제 #14
0
 def _assemble(self, operation, address, overwrite=False, removed=None):
     data = self.assembler.assemble(operation, address)
     if data:
         end_address = address + len(data)
         if removed is None or address not in removed:
             self.snapshot[address:end_address] = data
             self.base_address = min(self.base_address, address)
             self.end_address = max(self.end_address, end_address)
         if overwrite:
             removed.update(range(address, end_address))
         return end_address
     raise SkoolParsingError("Failed to assemble:\n {} {}".format(
         address, operation))
예제 #15
0
 def _parse_instruction(self, line):
     ctl = line[0]
     try:
         address = get_int_param(line[1:6])
     except ValueError:
         raise SkoolParsingError("Invalid address ({}):\n{}".format(
             line[1:6], line.rstrip()))
     addr_str = self.address_fmt.format(address)
     comment_index = find_unquoted(line, ';', 6, neg=True)
     if comment_index > 0:
         end = comment_index
     else:
         end = len(line)
     operation = line[7:end].strip()
     comment = line[end + 1:].strip()
     return ControlLine(ctl, address, addr_str, operation, comment_index,
                        comment, self.preserve_base)
예제 #16
0
 def _parse_asm_directive(self, address, directive, removed):
     if directive.startswith(
         ('isub=', 'ssub=', 'rsub=', 'ofix=', 'bfix=', 'rfix=')):
         weight = self.weights[directive[:4]]
         if weight > (0, 0):
             value = directive[5:].rstrip()
             if value.startswith('!'):
                 removed.update(parse_address_range(value[1:]))
             else:
                 self.subs[weight].append(value)
     elif directive.startswith('if('):
         try:
             address = self._parse_asm_directive(
                 address,
                 parse_if(self.fields, directive, 2)[1], removed)
         except MacroParsingError:
             pass
     elif directive.startswith('org'):
         org = directive.rstrip().partition('=')[2]
         if org:
             try:
                 address = get_int_param(org)
             except ValueError:
                 raise SkoolParsingError(
                     "Invalid org address: {}".format(org))
         else:
             address = None
     elif directive.startswith('keep'):
         self.keep = parse_asm_keep_directive(directive)
     elif directive.startswith('nowarn'):
         self.nowarn = parse_asm_nowarn_directive(directive)
     elif self.data is not None and directive.startswith(
         ('defb=', 'defs=', 'defw=')):
         self.data.append(directive)
     elif directive.startswith('remote='):
         addrs = [
             parse_int(a)
             for a in directive[7:].partition(':')[-1].split(',')
         ]
         if addrs[0] is not None:
             self.remote_entries.append(
                 Entry(None,
                       [Instruction(a) for a in addrs if a is not None]))
     return address
예제 #17
0
 def _parse_instruction(self, address, line, removed):
     if self.entry_ctl is None:
         self.entry_ctl = line[0]
     try:
         skool_address = get_int_param(line[1:6])
     except ValueError:
         if address is None or line[1:6].strip():
             raise SkoolParsingError("Invalid address ({}):\n{}".format(
                 line[1:6], line.rstrip()))
         skool_address = None
     if address is None:
         address = skool_address
     if skool_address not in removed:
         original_op = partition_unquoted(line[6:], ';')[0].strip()
         address = self._add_instructions(address, skool_address,
                                          self.subs[max(self.subs)],
                                          original_op, removed)
     self._reset(self.data is not None)
     return address
예제 #18
0
def get_defs_length(operation, preserve_base):
    if preserve_base:
        fmt = FORMAT_PRESERVE_BASE
    else:
        fmt = FORMAT_NO_BASE
    values = []
    for item in split_operation(operation)[1:3]:
        base = _get_base(item, preserve_base)
        try:
            value = get_int_param(item)
        except ValueError:
            try:
                item = value = parse_word(item)
            except ValueError:
                raise SkoolParsingError("Invalid integer '{}': {}".format(item, operation))
            if base == 'c':
                base = 'd'
        values.append((value, fmt[base].format(item)))
    return values[0][0], ':'.join([v[1] for v in values])
예제 #19
0
 def _parse_instruction(self, line):
     try:
         address = get_int_param(line[1:6])
     except ValueError:
         raise SkoolParsingError("Invalid address ({}):\n{}".format(line[1:6], line.rstrip()))
     for sub in self.subs:
         if sub is not None:
             operation = sub
             self.subs = [None] * 4
             break
     else:
         comment_index = find_unquoted(line, ';', 6)
         operation = line[7:comment_index].strip()
     data = assemble(operation, address)
     if data:
         end_address = address + len(data)
         self.snapshot[address:end_address] = data
         self.base_address = min(self.base_address, address)
         self.end_address = max(self.end_address, end_address)
     else:
         warn("Failed to assemble:\n {} {}".format(address, operation))
예제 #20
0
 def parse_text(self, writer, text, index, *cwd):
     try:
         end = text.index(LIST_END_MARKER, index) + len(LIST_END_MARKER)
     except ValueError:
         raise SkoolParsingError("No end marker: #LIST{}...".format(text[index:index + 15]))
     return end, self.parse_list(writer, text[index:end], *cwd)
예제 #21
0
    def _parse_skool(self, skoolfile, min_address, max_address):
        map_entry = None
        instruction = None
        address_comments = []
        for line in skoolfile:
            if line.startswith(';'):
                if self.mode.started and self.mode.include:
                    self.comments.append(line[2:].rstrip())
                    self.mode.ignoreua = False
                instruction = None
                address_comments.append((None, None))
                continue

            if line.startswith('@'):
                self._parse_asm_directive(line[1:].rstrip())
                continue

            if not self.mode.include:
                continue

            s_line = line.strip()
            if not s_line:
                instruction = None
                address_comments.append((None, None))
                if self.comments:
                    if map_entry:
                        self._add_end_comment(map_entry)
                    else:
                        self.header += self.comments
                self.comments[:] = []
                self.ignores[:] = []
                map_entry = None
                continue

            if s_line[0] == ';' and map_entry and instruction:
                # This is an instruction comment continuation line
                address_comments[-1][1] = '{0} {1}'.format(address_comments[-1][1], s_line[1:].lstrip())
                continue

            # This line contains an instruction
            instruction, address_comment = self._parse_instruction(line)
            address = instruction.address
            addr_str = instruction.addr_str
            ctl = instruction.ctl
            if ctl in DIRECTIVES:
                if address is None:
                    raise SkoolParsingError("Invalid address: '{}'".format(addr_str))
                start_comment, desc, details, registers = parse_comment_block(self.comments, self.ignores, self.mode)
                map_entry = SkoolEntry(address, addr_str, ctl, desc, details, registers)
                instruction.mid_block_comment = start_comment
                map_entry.ignoreua.update(self.mode.entry_ignoreua)
                self.mode.reset_entry_ignoreua()
                self._entries[address] = map_entry
                self.memory_map.append(map_entry)
                self.comments[:] = []
                self.base_address = min((address, self.base_address))
            elif ctl == 'd':
                # This is a data definition entry
                map_entry = None
            elif ctl == 'r':
                # This is a remote entry
                map_entry = RemoteEntry(instruction.operation, address)

            if map_entry:
                address_comments.append([instruction, address_comment])
                if address is not None:
                    self._instructions.setdefault(address, []).append(instruction)
                map_entry.add_instruction(instruction)
                if self.comments:
                    instruction.mid_block_comment = join_comments(self.comments, split=True, html=self.mode.html)
                    self.comments[:] = []
                    self.mode.ignoremrcua = 0 in self.ignores

            self.mode.apply_asm_attributes(instruction)
            self.ignores[:] = []

            # Set bytes in the snapshot if the instruction is DEF{B,M,S,W}
            if address is not None:
                operation = instruction.operation
                if self.mode.assemble or operation.upper().startswith(('DEFB ', 'DEFM ', 'DEFS ', 'DEFW ')):
                    set_bytes(self.snapshot, address, operation)
        if self.comments and map_entry:
            self._add_end_comment(map_entry)

        if min_address > 0 or max_address < 65536:
            self.memory_map = [e for e in self.memory_map if min_address <= e.address < max_address]
            self._entries = {k: v for k, v in self._entries.items() if min_address <= k < max_address}
            if self._entries:
                self.base_address = min(self._entries)
                last_entry = self._entries[max(self._entries)]
                last_entry.instructions = [i for i in last_entry.instructions if i.address is None or i.address < max_address]
            else:
                self.base_address = max_address
            self._instructions = {k: v for k, v in self._instructions.items() if self.base_address <= k < max_address}
            address_comments = [c for c in address_comments if c[0] is None or c[0].address is None or self.base_address <= c[0].address < max_address]

        if self.memory_map:
            end_address = max([i.address for e in self.memory_map for i in e.instructions if i.address is not None])
            last_instruction = self.get_instruction(end_address)
            self.end_address = end_address + (get_size(last_instruction.operation, end_address) or 1)

        # Do some post-processing
        parse_address_comments(address_comments, self.mode.html)
        self.make_replacements(self)
        self._calculate_references()
        if self.mode.asm_labels:
            self._generate_labels()
        if self.mode.html:
            self._calculate_entry_sizes()
            self._escape_instructions()
        else:
            self._substitute_labels()
예제 #22
0
    def parse_table(self, writer, table_def, *cwd):
        text = table_def
        for ws_char in '\n\r\t':
            text = text.replace(ws_char, ' ')

        try:
            index, params = parse_brackets(text, default='')
        except ClosingBracketError:
            raise SkoolParsingError("Cannot find closing ')' in table CSS class list:\n{}".format(table_def))
        classes = [c.strip() for c in writer.expand(params, *cwd).split(',')]
        table_class = classes[0]
        column_classes = classes[1:]
        wrap_columns = []
        for i, column_class in enumerate(column_classes):
            if column_class.endswith(COLUMN_WRAP_MARKER):
                column_classes[i] = column_class[:-len(COLUMN_WRAP_MARKER)]
                wrap_columns.append(i)
        table = Table(table_class, wrap_columns)

        text = writer.expand(text[index:], *cwd)
        index = 0
        prev_spans = {}
        while text.find('{', index) >= 0:
            row = []
            row_start = text.find('{ ', index)
            if row_start < 0:
                raise SkoolParsingError("Cannot find opening '{{ ' in table row:\n{0}".format(table_def))
            row_end = text.find(' }', row_start)
            if row_end < 0:
                raise SkoolParsingError("Cannot find closing ' }}' in table row:\n{0}".format(table_def))
            col_index = 0
            for cell in text[row_start + 1:row_end + 1].split(' | '):
                prev_rowspan, prev_colspan = prev_spans.get(col_index, (1, 1))
                while prev_rowspan > 1:
                    prev_spans[col_index] = (prev_rowspan - 1, prev_colspan)
                    col_index += prev_colspan
                    prev_rowspan, prev_colspan = prev_spans.get(col_index, (1, 1))
                header, transparent = False, False
                rowspan, colspan = 1, 1
                if len(column_classes) > col_index:
                    cell_class = column_classes[col_index]
                else:
                    cell_class = ''
                cell = cell.strip()
                if cell.startswith('='):
                    end = cell.find(' ')
                    if end < 0:
                        end = len(cell)
                    for span in cell[1:end].split(','):
                        if span[0] == 'c':
                            try:
                                colspan = int(span[1:])
                            except ValueError:
                                raise SkoolParsingError("Invalid colspan indicator: '{}'".format(span))
                        elif span[0] == 'r':
                            try:
                                rowspan = int(span[1:])
                            except ValueError:
                                raise SkoolParsingError("Invalid rowspan indicator: '{}'".format(span))
                        elif span[0] == 'h':
                            header = True
                        elif span[0] == 't':
                            transparent = True
                    cell = cell[end:].lstrip()
                row.append(Cell(cell, transparent, colspan, rowspan, header, cell_class))
                prev_spans[col_index] = (rowspan, colspan)
                col_index += colspan

            # Deal with the case where the previous row contains one or more
            # cells at the end with rowspan > 1
            while col_index in prev_spans:
                prev_rowspan, prev_colspan = prev_spans[col_index]
                if prev_rowspan > 1:
                    prev_spans[col_index] = (prev_rowspan - 1, prev_colspan)
                col_index += prev_colspan

            table.add_row(row)
            index = row_end + 1

        return table