Regex для соответствия шаблону MediaWiki и его параметрам - PullRequest
4 голосов
/ 30 июня 2011

Я пишу простой Javascript для добавления определенного параметра к определенному шаблону в статье, которая в данный момент редактируется.

Шаблоны Википедии структурированы в следующем формате:

 {{Template name|unnamed parameter|named parameter=some value|another parameter=[[target article|article name]]|parameter={{another template|another tamplate's parameter}}}}

Один шаблон также может быть больше строк, например:

{{Template 
|name=John
|surname=Smith
|pob=[[London|London, UK]]
}}

Для дальнейшего ознакомления, пожалуйста, посмотрите http://en.wikipedia.org/wiki/Help:Template

Итак, во-первых, я хотел бы сопоставить весь шаблон. Я пришел к частичному решению, то есть:

document.editform.wpTextbox1.value.match(/\{\{template name((.|\n)*?)\}\}$/gmis)

Однако проблема в том, что он сопоставляет только текст от начальных скобок до закрывающих скобок первого вложенного шаблона (первый пример).

Кроме того, я хотел бы получить его параметры в виде массива. Поэтому для результата я бы хотел получить массив с параметрами в определенном порядке. Массив (значение параметра paramter, значение имени параметра, значение фамилии параметра, значение параметра pod (в данном случае пусто, поскольку оно было не установлено))

Я бы использовал это для очистки нестандартного форматирования в некоторых статьях и добавления некоторых новых параметров.

Спасибо!

1 Ответ

7 голосов
/ 11 июля 2011

Напишите простой синтаксический анализатор.

Решение такого рода проблем с помощью регулярных выражений не является правильным.Это то же самое, что сопоставление скобок - это сложно сделать с помощью регулярных выражений.Регулярные выражения вообще не подходят для вложенных выражений.

Попробуйте что-то вроде этого:

var parts = src.split(/(\{\{|\}\})/);
for (var i in parts) {
  if (parts[i] == '{{') // starting new (sub) template
  else if (parts[i] == '}}') // ending (sub) template
  else // content (or outside)
}

Это просто псевдокод, так как я сейчас спешу, обновит этот код доработает ...

ОБНОВЛЕНИЕ (9 августа 2011 г.)

var NO_TPL = 0, // outside any tpl - ignoring...
    IN_TPL = 1, // inside tpl
    IN_LIST = 3; // inside list of arguments

function parseWiki(src) {
  var tokens = src.split(/(\{\{|\}\}|\||=|\[\[|\]\])/),
      i = -1, end = tokens.length - 1,
      token, next, state = NO_TPL,
      work = [], workChain = [], stateChain = [];

  function trim(value) {
    return value.replace(/^\s*/, '').replace(/\s*$/, '');
  }

  // get next non empty token
  function getNext(next) {
    while (!next && i < end) next = trim(tokens[++i]);
    return next;
  }

  // go into tpl / list of arguments
  function goDown(newState, newWork, newWorkKey) {
    stateChain.push(state);
    workChain.push(work);

    if (newWorkKey) {
      work[newWorkKey] = newWork;
    } else {
      work.push(newWork);
    }

    work = newWork;
    state = newState;
  }

  // jump up from tpl / list of arguments
  function goUp() {
    work = workChain.pop();
    state = stateChain.pop();
  }

  // state machine
  while ((token = getNext())) {
    switch(state) {

      case IN_TPL:
        switch(token) {
          case '}}': goUp(); break;
          case '|': break;
          default:
            next = getNext();
            if (next != '=') throw "invalid";
            next = getNext();
            if (next == '[[') {
              goDown(IN_LIST, [], token);
            } else if (next == '{{') {
              goDown(IN_TPL, {id: getNext()}, token);
            } else {
              work[token] = next;
            }
        }
        break;

      case IN_LIST:
        switch(token) {
          case ']]': goUp(); break;
          case '|': break;
          default: work.push(token);
        }
        break;

      case NO_TPL:
        if (token == '{{') {
          next = getNext();
          goDown(IN_TPL, {id: next});
        }
        break;
    }
  }

  return work;
}

ИСПЫТАНИЯ НА УСТРОЙСТВО

describe('wikiTpl', function() {
  it('should do empty tpl', function() {
    expect(parseWiki('{{name}}'))
      .toEqual([{id: 'name'}]);
  });

  it('should ignore text outside from tpl', function() {
    expect(parseWiki(' abc {{name}} x y'))
    .toEqual([{id: 'name'}]);
  });

  it('should do simple param', function() {
    expect(parseWiki('{{tpl | p1= 2}}'))
      .toEqual([{id: 'tpl', p1: '2'}]);
  });

  it('should do list of arguments', function() {
    expect(parseWiki('{{name | a= [[1|two]]}}'))
      .toEqual([{id: 'name', a: ['1', 'two']}]);
  });

  it('should do param after list', function() {
    expect(parseWiki('{{name | a= [[1|two|3]] | p2= true}}'))
      .toEqual([{id: 'name', a: ['1', 'two', '3'], p2: 'true'}]);
  });

  it('should do more tpls', function() {
    expect(parseWiki('{{first | a= [[1|two|3]] }} odd test {{second | b= 2}}'))
      .toEqual([{id: 'first', a: ['1', 'two', '3']}, {id: 'second', b: '2'}]);
  });

  it('should allow nested tpl', function() {
    expect(parseWiki('{{name | a= {{nested | p1= 1}} }}'))
      .toEqual([{id: 'name', a: {id: 'nested', p1: '1'}}]);
  });
});

ПримечаниеЯ использую синтаксис Жасмин для этих модульных тестов.Вы можете легко запустить его, используя AngularJS, который содержит всю среду тестирования - посмотрите на http://angularjs.org

...