Научитесь писать парсер рекурсивного спуска. Как только вы поймете концепции, вы сможете сделать это на любом языке: Java, C ++, JavaScript, SystemVerilog, ... что угодно. Если вы можете обрабатывать строки, то можете анализировать.
Анализ с рекурсивным спуском - это базовый метод анализа, который может быть легко закодирован вручную. Это полезно, если у вас нет доступа (или вы не хотите дурачиться) с генератором парсера.
В синтаксическом анализаторе с рекурсивным спуском каждое правило в вашей грамматике транслируется в процедуру, которая анализирует правило. Если вам нужно обратиться к другим правилам, то вы можете сделать это, вызвав их - это всего лишь процедуры.
Простой пример: выражения, включающие числа, сложение и умножение (это иллюстрирует приоритет оператора). Во-первых, грамматика:
expr ::= term
| expr "+" term
term ::= factor
| term "*" factor
factor ::= /[0-9/+ (I'm using a regexp here)
Теперь, чтобы написать синтаксический анализатор (который включает лексер; с рекурсивным спуском вы можете соединить их вместе). Я никогда не использовал JavaScript, поэтому давайте попробуем это в (мой ржавый) Java:
class Parser {
string str;
int idx; // index into string
Node parseExpr() throws ParseException
{
Node op1 = parseTerm();
Node op2;
while (idx < str.size() && str.charAt(idx) == '+') {
idx++;
op2 = parseTerm();
op1 = new AddNode(op1, op2);
}
return op1;
}
Node parseTerm() throws ParseException
{
Node op1 = parseFactor();
Node op2;
while (idx < str.size() && str.charAt(idx) == '*') {
idx++;
op2 = parseFactor();
op1 = new MultNode(op1, op2);
}
return op1;
}
Node parseFactor() throws ParseException
{
StringBuffer sb = new StringBuffer();
int old_idx = idx;
while (idx < str.size() && str.charAt(idx) >= '0' && str.charAt(idx) <= '9') {
sb.append(str.charAt(idx));
idx++;
}
if (idx == old_idx) {
throw new ParseException();
}
return new NumberNode(sb.toString());
}
}
Вы можете видеть, как каждое правило грамматики переводится в процедуру. Я не проверял это; это упражнение для читателя.
Вам также нужно беспокоиться об обнаружении ошибок. Реальный компилятор должен восстановиться после ошибок разбора, чтобы попытаться проанализировать оставшуюся часть его входных данных. Синтаксический анализатор выражений, состоящий из одной строки, подобный этому, вообще не должен пытаться выполнить восстановление, но он должен определить, существует ли ошибка синтаксического анализа, и пометить ее. Самый простой способ сделать это, если ваш язык позволяет это, - вызвать исключение и перехватить его в точке входа в анализатор. Я не обнаружил все возможные ошибки разбора в моем примере выше.
Для получения дополнительной информации посмотрите "Парсер LL" и "Парсер рекурсивного спуска" в Википедии. Как я сказал в начале, если вы можете понять концепции (и они просты по сравнению с концепциями, лежащими в основе замыканий конфигурации конечного автомата LALR (1)), то вы можете написать анализатор для небольших задач на любом языке, если как у вас есть некоторые элементарные возможности строки. Наслаждайтесь силой.