Создание редактора простых формул в JavaScript

Я работаю над созданием прототипа базового игрового движка RPG с использованием JavaScript и Canvas. Я все еще работаю над некоторыми техническими требованиями к дизайну на бумаге, и у меня возникла небольшая проблема, которую я не совсем понимаю, как решить.

У меня будет объект Character, который будет иметь массив объектов Attribute. Атрибуты будут выглядеть примерно так:

function(name, value){
    this.name = name;
    this.value = value;
    ...
}

У персонажа также будут «навыки», рассчитываемые на основе атрибутов. Значение навыков также можно определить по формуле, введенной пользователем. Правильная формула выглядела бы примерно так:

((@attribute1Name + (@attribute2Name / 2) * 5)

где любой текст, следующий за знаком @, представляет имя атрибута, принадлежащего этому символу. Формула будет введена в текстовое поле в виде строки.

У меня проблема с пониманием того, как правильно анализировать и оценивать эту формулу. Изначально мой план состоял в том, чтобы просто заменить имена атрибутов и оценить выражение (если оно недействительно, то eval завершится ошибкой). Однако это представляет проблему, поскольку позволяет внедрять JavaScript в поле. Я предполагаю, что мне понадобится какой-то автомат, похожий на инфиксный калькулятор, чтобы решить эту проблему, но я немного заржавел в своей теории вычислений (спасибо корпоративному миру!). Я действительно не прошу кого-то просто передать мне код настолько, насколько мне хотелось бы узнать ваше мнение о том, как лучше всего решить эту проблему?

РЕДАКТИРОВАТЬ: Спасибо за ответы. К сожалению, жизнь заставила меня заняться, и я еще не пробовал решения. Будет обновлено, когда я получу результат (хороший или плохой).


person framauro13    schedule 16.07.2010    source источник


Ответы (6)


Другая идея, поэтому отдельное предложение:

eval() работает нормально, и нет необходимости заново изобретать колесо.

Предполагая, что на вашем языке формул есть только небольшое и фиксированное количество переменных, будет достаточно просмотреть выражение и убедиться, что все, с чем вы сталкиваетесь, является либо круглой скобкой, либо оператором, либо одним из имен ваших переменных. Я не думаю, что есть способ собрать эти части в кусок кода, который может иметь вредоносные побочные эффекты для eval.

So:

  • Просканируйте выражение, чтобы убедиться, что оно взято из очень ограниченного словаря.
  • Позвольте eval () решить это.

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

person Carl Smotricz    schedule 16.07.2010
comment
Это была моя первоначальная мысль. Используя регулярное выражение для ограничения ввода, подсчитайте скобки, проверьте имена переменных, затем eval. Коллега сказал мне, что FSM будет самым простым и безопасным подходом. Я согласен с безопасностью, но это спорно :) - person framauro13; 16.07.2010

Я думаю, что вместо того, чтобы позволить им вводить всю формулу, вы могли бы иметь теги select, которые имеют операции и значения, и позволить им выбирать.

т.е. набор тегов с атрибутом-номер-операции:

<select>         <select> <input type="text">
@attribute1Name1  +        (check if input is number)
@attribute1Name2  -
@attribute1Name3  *
@attribute1Name4  /

и Т. Д.

person tcooc    schedule 16.07.2010
comment
Спасибо за идею. Это вариант. Если я зациклюсь на создании поля формулы произвольной формы, это может быть жизнеспособной альтернативой, пока я не получу более надежное решение. Также можно утверждать, что это будет проще для пользователей, поскольку им не придется так сильно беспокоиться о синтаксисе формул. Придется попробовать оба варианта и посмотреть, какие отзывы я получу. Спасибо! - person framauro13; 16.07.2010
comment
Попробуйте это на минуту, и вы увидите, что это слишком утомительно и медленно. - person Aaron Digulla; 19.07.2010

Существует действительно простое решение: просто введите обычную формулу JavaScript (т.е. как если бы вы писали метод для своего объекта) и используйте this для ссылки на объект, над которым вы работаете.

Чтобы изменить this при оценке метода, используйте apply() или call() (см. Это ответ).

person Aaron Digulla    schedule 16.07.2010
comment
Это было то, что я планировал сделать. После проверки замените @ на "this". и eval. Больше всего меня беспокоило то, что пользователь вводил JavaScript в поле до вызова eval. Я обязательно это проверю ... как говорится, часто самый простой ответ оказывается правильным. Я хочу, чтобы это всегда относилось к программному обеспечению. - person framauro13; 16.07.2010
comment
Если ваш игровой движок должен стать чем-то вроде Facebook (где любой может редактировать формулы), любой способ редактирования формул слишком опасен, потому что это нарушит баланс игры. Я предполагаю, что вы предлагаете это игровым дизайнерам, и в этом случае вы можете и должны им доверять. - person Aaron Digulla; 19.07.2010

Я недавно написал подобное приложение. Я, вероятно, вложил слишком много работы, но я прошел целых 9 ярдов и написал и сканер, и синтаксический анализатор.

Сканер преобразовал текст в серию токенов; токены - это простые объекты, состоящие из типа и значения токена. Для знаков препинания значение = символ, для чисел значения будут целыми числами, соответствующими числовому значению числа, а для переменных это будет (ссылка на) объект переменной, где эта переменная будет находиться в списке объекты, имеющие имя. Тот же объект переменной = та же переменная, natch.

Синтаксический анализатор был простым синтаксическим анализатором рекурсивного спуска методом перебора. Вот код.

Мой синтаксический анализатор выполняет логические выражения с И / ИЛИ вместо +/-, но я думаю, вы понимаете идею. Существует несколько уровней выражений, и каждый пытается собрать как можно больше самого себя и обращается к более низким уровням для анализа вложенных конструкций. По завершении мой синтаксический анализатор сгенерировал единственный узел, содержащий древовидную структуру, представляющую выражение.

В своей программе, я думаю, вы могли бы просто сохранить этот узел, поскольку его структура, по сути, будет представлять формулу для его оценки.

Однако, учитывая всю эту работу, я бы понял, насколько заманчиво было бы просто прогнуться и использовать eval!

person Carl Smotricz    schedule 16.07.2010
comment
FSM кажется правильным решением, но для того, чего я пытаюсь достичь, я все еще не уверен, что это слишком много. Коллега сказал мне, что FSM будет самым простым и безопасным подходом. Самый безопасный, конечно. Я еще не совсем уверен, что это проще всего. Спасибо за код парсера, позже еще разберусь в нем. - person framauro13; 16.07.2010
comment
Я подумал о том, чтобы написать конечный автомат для моей задачи, но когда я позволил Bison уехать в город, оказалось, что для этого простого выражения требуется 12 или 18 состояний! Автомат быстр и относительно прост, но я чувствую, что было бы # ck написать его для оценки выражений. Тем не менее, я использовал автомат для своего сканера, и это было очень легко. - person Carl Smotricz; 16.07.2010

Меня очаровывает задача сделать это самыми простыми из возможных средств.

Вот еще один подход:

  1. преобразовать инфикс в постфикс;
  2. используйте очень простой калькулятор на основе стека, чтобы оценить полученное выражение.

Обоснование здесь в том, что как только вы избавитесь от усложнения "* перед +" и круглых скобок, оставшиеся вычисления будут очень простыми.

person Carl Smotricz    schedule 16.07.2010
comment
Вместо того, чтобы добавлять еще один ответ, я предлагаю отредактировать ваш первый. - person Aaron Digulla; 19.07.2010
comment
Я подумал об этом, но это семантика для изменения ответа, а не для представления совершенно другого. Однако я отредактирую извинения; это ничего не дает. - person Carl Smotricz; 19.07.2010

Вы можете посмотреть, как запустить пользовательский код в песочнице для предотвращения атак:
Можно ли изолировать JavaScript, выполняющийся в браузере?

person Tobias Cohen    schedule 19.07.2010