Извините за расплывчатое название, но я действительно не знаю, как кратко описать эту проблему.
Я создал (более или менее) простой язык для предметной области, который я использовать, чтобы указать, какие правила проверки должны применяться к различным объектам (обычно формы, отправленные с веб-страницы). Внизу этого поста я включил образец того, как выглядит язык.
Моя проблема в том, что я понятия не имею, как начать синтаксический анализ этого языка в форме, которую я могу использовать (я буду использовать Python для синтаксического анализа). Моя цель — получить список правил/фильтров (в виде строк, включая аргументы, например, 'cocoa(99)'
), которые следует применять (по порядку) к каждому объекту/сущности (также к строке, например, 'chocolate'
, 'chocolate.lindt'
и т. д.).
Я не уверен, какую технику использовать для начала, или даже какие методы существуют для таких проблем. Как вы думаете, что является лучшим способом сделать это? Я не ищу полного решения, просто общий толчок в правильном направлении.
Спасибо.
Пример файла языка:
# Comments start with the '#' character and last until the end of the line
# Indentation is significant (as in Python)
constant NINETY_NINE = 99 # Defines the constant `NINETY_NINE` to have the value `99`
*: # Applies to all data
isYummy # Everything must be yummy
chocolate: # To validate, say `validate("chocolate", object)`
sweet # chocolate must be sweet (but not necessarily chocolate.*)
lindt: # To validate, say `validate("chocolate.lindt", object)`
tasty # Applies only to chocolate.lindt (and not to chocolate.lindt.dark, for e.g.)
*: # Applies to all data under chocolate.lindt
smooth # Could also be written smooth()
creamy(1) # Level 1 creamy
dark: # dark has no special validation rules
extraDark:
melt # Filter that modifies the object being examined
c:bitter # Must be bitter, but only validated on client
s:cocoa(NINETY_NINE) # Must contain 99% cocoa, but only validated on server. Note constant
milk:
creamy(2) # Level 2 creamy, overrides creamy(1) of chocolate.lindt.* for chocolate.lindt.milk
creamy(3) # Overrides creamy(2) of previous line (all but the last specification of a given rule are ignored)
ruleset food: # To define a chunk of validation rules that can be expanded from the placeholder `food` (think macro)
caloriesWithin(10, 2000) # Unlimited parameters allowed
edible
leftovers: # Nested rules allowed in rulesets
stale
# Rulesets may be nested and/or include other rulesets in their definition
chocolate: # Previously defined groups can be re-opened and expanded later
ferrero:
hasHazelnut
cake:
tasty # Same rule used for different data (see chocolate.lindt)
isLie
ruleset food # Substitutes with rules defined for food; cake.leftovers must now be stale
pasta:
ruleset food # pasta.leftovers must also be stale
# Sample use (in JavaScript):
# var choc = {
# lindt: {
# cocoa: {
# percent: 67,
# mass: '27g'
# }
# }
# // Objects/groups that are ommitted (e.g. ferrro in this example) are not validated and raise no errors
# // Objects that are not defined in the validation rules do not raise any errors (e.g. cocoa in this example)
# };
# validate('chocolate', choc);
# `validate` called isYummy(choc), sweet(choc), isYummy(choc.lindt), smooth(choc.lindt), creamy(choc.lindt, 1), and tasty(choc.lindt) in that order
# `validate` returned an array of any validation errors that were found
# Order of rule validation for objects:
# The current object is initially the object passed in to the validation function (second argument).
# The entry point in the rule group hierarchy is given by the first argument to the validation function.
# 1. First all rules that apply to all objects (defined using '*') are applied to the current object,
# starting with the most global rules and ending with the most local ones.
# 2. Then all specific rules for the current object are applied.
# 3. Then a depth-first traversal of the current object is done, repeating steps 1 and 2 with each object found as the current object
# When two rules have equal priority, they are applied in the order they were defined in the file.
# No need to end on blank line