Функция «замена» не работает при переборе массива строк - PullRequest
0 голосов
/ 06 февраля 2019

Я пытаюсь разобрать массив строк с помощью split, replace и join.

Моя конечная цель - взять данный фрагмент кода (в Солидности для всего, что имеет значение)и добавьте подчеркивание к имени каждого аргумента функции.

Вот сокращенная версия моего кода (только то, что необходимо для рассматриваемого вопроса):

def checkLine(line):
    return any([line.startswith('    '+word) for word in ['function','constructor','event']])

def checkWord(word):
    return any([word.startswith(x) for x in 'abcdefghijklmnopqrstuvwxyz']) and any([word.endswith(x) for x in ',);'])

def parseFile(fileName):
    fileDesc = open(fileName, 'r')
    lines = fileDesc.read().split('\n')
    fileDesc.close()

    for n in range(len(lines)):
        if checkLine(lines[n]):
            for word in lines[n].split(' '):
                if checkWord(word):
                    lines[n] = lines[n].replace(word,'_'+word)
                    if lines[n].endswith('{'):
                        m = n+1
                        while not lines[m].endswith('}'):
                            lines[m] = lines[m].replace(word,'_'+word)
                            m += 1

    fileDesc = open(fileName, 'w')
    fileDesc.write('\n'.join(lines))
    fileDesc.close()

И вот проблемая часами бьюсь головой:

Линия lines[n] = lines[n].replace(word,'_'+word) отлично справляется с работой.

Но линия lines[m] = lines[m].replace(word,'_'+word) не имеет никакого эффекта.

Так что я могу изменить имена аргументов в заголовке функции, но не в теле функции.

Может кто-нибудь просветить меня об этом?

Вот примервходной файл:

pragma solidity 0.4.25;

import "./interfaces/IMyContract.sol";

/**
 * @title My Contract.
 */
contract MyContract is IMyContract {
    string public constant version = "1.0.0";

    mapping(bytes32 => address) private something;

    event Event(bytes32 indexed var1, address indexed var2);

    /**
     * @dev ...
     * @param var3 ...
     * @param var4 ...
     */
    constructor(bytes32[] memory var3, address[] var4) public {
        uint256 length = var3.length;
        require(length == var4.length);
        for (uint256 i = 0; i < length; i++) {
            require(uint256(something[var3[i]]) == 0);
            something[var3[i]] = var4[i];
            emit Event(var3[i], var4[i]);
        }
    }

    /**
     * @dev ...
     * @param var5 ...
     */
    function get(bytes32 var5) external view returns (address) {
        return something[var5];
    }
}

Большое спасибо !!!

Ответы [ 2 ]

0 голосов
/ 06 февраля 2019

Чтобы прочитать файл, лучше использовать with, чтобы избежать явного вызова open и close:

with open(myfile) as fh:
    lines = fh.readlines()

Вы можете не указывать флаг 'r',поскольку файлы будут автоматически открываться в режиме чтения.

Здесь вы можете увидеть, находится ли один из ваших типов событий в строке, используя цикл for, например:

f_types = ['Event', 'constructor', 'function']

for line in lines:
    for f in f_types:
        if f in line:
            break
    else:
         continue

else здесь проверяет, что итерация по f_types завершена (break не произошло), поэтому мы пропустим строки, у которых нет заметных событий (f_types)

Далее, мы можем использовать следующий шаблон регулярных выражений, чтобы найти все наши аргументы:

res = re.findall('\((.*?)\)', line)
# for the line constructor(bytes32[] memory var3, address[] var4)
# this returns ['bytes32[] memory var3, address[] var4']

# Now we want to split the line on commas to give each declaration and
# grab the last statement because we don't care about anything but the name
args = [x.split()[-1] for x in res[0].split(',')]

Теперь, чтобы сохранить все это, весь скрипт становится следующим:

f_types = ['Event', 'constructor', 'function']
allargs = [] # to store all of our replace calls at the end

with open(myfile) as fh:
    lines = fh.readlines()

for line in lines:
    for f in f_types:
        if f in line:
            break
    else:
         continue

    in_parens = re.findall('\w+\((.*?)\)', line)[0]
    args = [x.split()[-1] for x in in_parens.split(',')

    allargs.extend(args)

ПослеВыполнение этого, allargs будет выглядеть так:

# ['var1', 'var2', 'var3', 'var4', 'var3[i]', 'var4[i]', 'var5']

Теперь, заимствуя из ответа @ Джина выше, я не слишком беспокоюсь о дублированных var3 и var4, потому что мы установили теграницы строки:

whole_file = '\n'.join(lines)

for arg in allargs:
    whole_file = re.sub(r"\b(%s)\b" % arg, r'_\1', whole_file)

А теперь ваш файл выглядит как

pragma solidity 0.4.25;

import "./interfaces/IMyContract.sol";

/**
 * @title My Contract.
 */
contract MyContract is IMyContract {
    string public constant version = "1.0.0";

    mapping(bytes32 => address) private something;

    event Event(bytes32 indexed _var1, address indexed _var2);

    /**
     * @dev ...
     * @param _var3 ...
     * @param _var4 ...
     */
    constructor(bytes32[] memory _var3, address[] _var4) public {
        uint256 length = _var3.length;
        require(length == _var4.length);
        for (uint256 i = 0; i < length; i++) {
            require(uint256(something[_var3[i]]) == 0);
            something[_var3[i]] = _var4[i];
            emit Event(_var3[i], _var4[i]);
        }
    }

    /**
     * @dev ...
     * @param _var5 ...
     */
    function get(bytes32 _var5) external view returns (address) {
        return something[_var5];
    }
}
0 голосов
/ 06 февраля 2019

Проблема в том, что после теста checkWord вам нужно убрать завершающие скобки, двоеточие и т. Д. ... или замена не работает для остальных токенов

Использовать rstrip чтобы удалить эти символы, тогда замена работает.

    for word in lines[n].split(' '):
        if checkWord(word):
            word = word.rstrip(',);')

пример вывода:

constructor(bytes32[] memory _var3, address[] _var4) public {
    uint256 length = _var3.length;
    require(length == _var4.length);
    for (uint256 i = 0; i < length; i++) {
        require(uint256(something[_var3[i]]) == 0);

Также измените lines[m].replace(word,"_"+word) с помощью более надежного метода замены слова, такого как замена границы словаregex, потому что если другие переменные начнут с той же строки, они также будут заменены.

>>> s = "hello var4 this is var41"
>>> s.replace("var4",'_var4')
'hello _var4 this is _var41'   # wrong!
import re
>>> re.sub(r"\b({})\b".format("var4"),r"_\1",s)
'hello _var4 this is var41'  # correct

Ваш код работает с сгенерированным кодом, но в значительной степени зависит от того факта, что параметрысразу следуют некоторые символы, а не пробелы.Ручное редактирование файла может нарушить ваш анализ.

...