def parse_map(fields, text, index, *cwd): # #MAPvalue(default[,k1:v1,k2:v2...]) try: args_index, value = parse_ints(text, index, 1, fields=fields) except MissingParameterError: raise MacroParsingError("No valid expression found: '#MAP{}'".format( text[index:])) try: end, args = parse_strings(text, args_index) except NoParametersError: raise NoParametersError("No mappings provided: {}".format( text[index:args_index])) map_id = text[args_index:end] if map_id in _map_cache: return end, _map_cache[map_id][value] default = args.pop(0) m = defaultdict(lambda: default) if args: for pair in args: if ':' in pair: k, v = pair.split(':', 1) else: k = v = pair try: m[evaluate(k)] = v except ValueError: raise MacroParsingError("Invalid key ({}): {}".format( k, text[args_index:end])) _map_cache[map_id] = m return end, m[value]
def parse_call(writer, text, index, *cwd): # #CALL:methodName(args) macro = '#CALL' if index >= len(text): raise MacroParsingError("No parameters") if text[index] != ':': raise MacroParsingError("Malformed macro: {}{}...".format( macro, text[index])) end = index + 1 match = RE_METHOD_NAME.match(text, end) if match: method_name = match.group() end += len(method_name) else: raise MacroParsingError("No method name") end, m_args = parse_brackets(text, end) if not hasattr(writer, method_name): writer.warn("Unknown method name in {} macro: {}".format( macro, method_name)) return end, '' method = getattr(writer, method_name) if not inspect.ismethod(method): raise MacroParsingError( "Uncallable method name: {}".format(method_name)) if m_args is None: raise MacroParsingError("No argument list specified: {}{}".format( macro, text[index:end])) args = list(cwd) kwargs = {} if m_args: for arg in _format_params(writer.expand(m_args), m_args, **writer.fields).split(','): a1, sep, a2 = arg.partition('=') if sep: v = a2 else: v = a1 try: value = evaluate(v) except ValueError: if v: value = v else: value = None if sep: kwargs[a1] = value else: args.append(value) try: retval = method(*args, **kwargs) except Exception as e: raise MacroParsingError("Method call {} failed: {}".format( text[index + 1:end], e)) if retval is None: retval = '' return end, retval
def parse_foreach(entry_holder, text, index, *cwd): # #FOREACH([v1,v2,...])(var,string[,sep,fsep]) try: end, values = parse_strings(text, index) except NoParametersError: raise NoParametersError("No values") try: end, (var, s, sep, fsep) = parse_strings(text, end, 4, ('', None)) except (NoParametersError, MissingParameterError) as e: raise MacroParsingError("No variable name: {}".format(text[index:e.args[1]])) if len(values) == 1: value = values[0] if value.startswith(('EREF', 'REF')): addr_str, getter, desc = { 'E': (value[4:], entry_holder.get_instruction, 'instruction'), 'R': (value[3:], entry_holder.get_entry, 'entry') }[value[0]] try: address = evaluate(addr_str) entity = getter(address) if not entity: raise MacroParsingError('No {} at {}: {}'.format(desc, address, value)) values = [str(r.address) for r in sorted(entity.referrers, key=lambda e: e.address)] except ValueError: pass elif value.startswith('ENTRY'): types = value[5:] values = [str(e.address) for e in entry_holder.memory_map if e.ctl != 'i' and (not types or e.ctl in types)] if not values: return end, '' if fsep is None: fsep = sep if len(values) == 1: return end, s.replace(var, values[0]) return end, fsep.join((sep.join([s.replace(var, v) for v in values[:-1]]), s.replace(var, values[-1])))
def parse_def(writer, text, index, *cwd): # #DEF(#MACRO[(ia[=i0],ib[=i1]...)[(sa[=s0],sb[=s1]...)]] body) end, definition = parse_strings(text, index, 1) definition = definition.strip() match = RE_MACRO.match(definition, 0) if match: name = match.group() dindex = match.end() else: raise MacroParsingError("Invalid macro name: #DEF{}".format( text[index:])) dindex, iparams = parse_brackets(definition, dindex) if iparams is None: sparams = None else: dindex, sparams = parse_brackets(definition, dindex) body = definition[dindex:].strip() inames, idefaults = [], [] if iparams: for ispec in _format_params(iparams, iparams, **writer.fields).split(','): iname, sep, ival = [s.strip() for s in ispec.partition('=')] match = re.match(PARAM_NAME, iname) if match and iname == match.group(): inames.append(iname) else: raise MacroParsingError( f'Invalid macro argument name: {iname}') if sep: try: idefaults.append(evaluate(ival)) except ValueError: raise InvalidParameterError( f"Cannot parse integer argument value: '{ispec}'") elif idefaults: idefaults.append(0) snames, sdefaults = [], [] if sparams: for sspec in _split_unbracketed(sparams): sname, sep, sval = [s.strip() for s in sspec.partition('=')] snames.append(sname) if sep: sdefaults.append(Template(sval)) elif sdefaults: sdefaults.append(Template('')) writer.macros[name] = partial(_expand_def_macro, writer, inames, idefaults, snames, sdefaults, Template(body)) return end, ''
def get_params(param_string, num=0, defaults=(), names=(), safe=True): params = [] named_params = {} index = 0 has_named_param = False if names: num = len(names) if param_string: for p in param_string.split(','): if index < len(names): name = names[index] else: name = index if p and names: match = RE_PARAM_NAME.match(p) if match: name = match.group()[:-1].strip() if name in names: value = p[match.end():] index = names.index(name) has_named_param = True else: raise MacroParsingError( "Unknown keyword argument: '{}'".format(p)) else: if has_named_param: raise MacroParsingError( "Non-keyword argument after keyword argument: '{}'" .format(p)) value = p else: value = p if value: try: param = evaluate(value, safe) except ValueError: raise InvalidParameterError( "Cannot parse integer '{}' in parameter string: '{}'". format(value, param_string)) if names and name: named_params[name] = param else: params.append(param) elif not names: params.append(None) index += 1 req = num - len(defaults) if named_params: for i in range(req): req_name = names[i] if req_name not in named_params: raise MissingParameterError( "Missing required argument '{}': '{}'".format( req_name, param_string)) elif index < req: if params: raise MissingParameterError( "Not enough parameters (expected {}): '{}'".format( req, param_string)) raise MissingParameterError("No parameters (expected {})".format(req)) elif None in params: missing_index = params.index(None) if missing_index < req: raise MissingParameterError( "Missing required parameter in position {}/{}: '{}'".format( missing_index + 1, req, param_string)) if index > num > 0: raise TooManyParametersError( "Too many parameters (expected {}): '{}'".format( num, param_string), param_string) if names: for i in range(req, num): name = names[i] if name not in named_params: named_params[name] = defaults[i - req] return [named_params[name] for name in names] params += [None] * (num - len(params)) for i in range(req, num): if params[i] is None: params[i] = defaults[i - req] return params