Правило условной грамматики в PEGjs

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

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

Я хочу, чтобы условные выражения выглядели так condition?value, где, если condition истинно, термин равен value. Переменные по обе стороны от ? также могут быть точечными свойствами объекта, подобного этому object.property1?object.property2.

Итак, если парсеру передается такой объект:

context = {
  depth: 100,
  material: {
    thickness: 20
    include: true
  }
  edge: {
    face: 4.5
  }
}

Выражение:

500 + depth + material.include?edge.face + material.thickness следует приравнивать к 624.5.

Я использую онлайн-редактор PEGjs. Я пробовал много разных подходов, но, похоже, не могу понять условность. Все остальное работает. Вот соответствующие правила:

Variable "variable"
  = variable:identifier accessor:("." identifier)* {
      var result = context[variable], i

      for (i = 0; i < accessor.length; i++) {
        result = result[accessor[i][1]]
      }

      return result
    }

identifier
  = identifier:$([0-9a-zA-Z_\$]+)

Conditional
  = condition:Variable "?" value:Variable {
    return condition ? value : 0
  }

Я просмотрел пример грамматики для javascript в репозитории PEGjs на github, и условное правило очень похоже на то, что у меня здесь, но я все еще не могу заставить его работать.

Каким будет правильный способ реализации условного оператора, подобного тому, который я описал в правиле PEGjs?


person oorst    schedule 20.12.2016    source источник
comment
Я думаю, ваша проблема может быть больше, чем просто синтаксический анализ ваших условных выражений. Быстрый рефакторинг того, что я написал для другого ответа, и я получаю ожидаемый результат. Вы найдете мою грамматику в этой сути и честном предупреждении, здесь используется lodash для извлечения значений из объекта контекста, но вы можете легко заменить его. Если у вас сработает, добавлю ответ.   -  person Rob Raisch    schedule 27.01.2017
comment
На самом деле я выбрал другой синтаксический анализатор выражений. Я использовал jsep. Он возвращает AST, который затем можно оценить любым способом. Я рекомендую jsep для простых вещей.   -  person oorst    schedule 28.01.2017


Ответы (1)


Я знаю, что это немного поздно, но проблема в том, что ваш variable - это строка, оценивающая "material.include".

Взгляните на этот код:

var result = context[variable], i

Вы пытаетесь получить доступ к свойству с именем «material.include» из вашего объекта контекста, которое будет выглядеть следующим образом:

{
    "material.include": true
}

Вместо того, чтобы пытаться получить доступ к объекту, на который ссылается свойство "material", а затем к свойству "include" полученного объекта, которое будет выглядеть следующим образом:

{
    "material": {
        "include": true
    }
}

Решением было бы разделить строку переменной на "." символов, а затем рекурсивно найти ваше свойство:

Variable "variable"
  = variable:identifier accessor:("." identifier)* {
      var path = variable.split(".");
      var result = path.reduce( (nextObject, propName) => nextObject[propName], context );

      for (var i = 0; i < accessor.length; i++) {
        result = result[accessor[i][1]]
      }

      return result
    }

Обратите внимание, что это решение не является полным, поскольку оно вызовет ошибку, если вы попытаетесь получить доступ к material.include, где material никогда не определяется в вашем контексте. Возможно, вы захотите добавить дополнительную обработку ошибок, но в данном примере это работает.

person JDB still remembers Monica    schedule 21.02.2020