написание парсера простого языка

Я пытаюсь разработать простой язык, похожий на губы, схему. Я написал свой лексер (токенизатор). Я могу разделить на операторы, идентификаторы и т. д. Но я сейчас пытаюсь написать парсер. Для этого мне достаточно одного примера. Может кто-нибудь привести пример в java-коде просто для примера? Кроме того, все упоминают об использовании antlr. Может ли кто-нибудь, кто пытается использовать antlr, показать пример, используя мой пример в этом вопросе?

  • EXP -> EXPI | EXPB
  • EXPI -> (+ EXPI EXPI) | (- EXPI EXPI) | (* EXPI EXPI) | (/ EXPI EXPI) | Id | IntegerValue | (Id EXPLIST)
  • EXPB -> (and EXPB EXPB)

если мой ввод (+ 1 2), я надеюсь получить в качестве вывода

 - START-> INPUT
 - -> EXP
 - -> EXPI
 - -> (+ EXPI EXPI)
 - -> (+ EXPI Id)
 - -> (+ Id Id)

Речь идет о LR (разбор сдвига-уменьшения). У меня есть простой пример, и я не знаю, могу ли я изменить код для синтаксического анализа. Но, что привлекает мое внимание, использование стека подходит для алгоритма LR. Не так ли?

import java.util.Stack;

public class SmallLisp {

    Stack<String> stack;

    public SmallLisp(){
        String[] tokens = new String[]{"(","+","2","(","+","3","2",")",")",};
        stack = new Stack<String>();
        for (int i=0;i<tokens.length;i++){
            stack.push(tokens[i]);
            if(tokens[i].equals(")")) Interprete();
        }
    }

    public void Interprete(){
        String tok;
        Stack<String> callStack = new Stack<String>();
        tok = stack.pop(); /* This is the ) character */
        while(!(tok=stack.pop()).equals("(")){
            callStack.push(tok);
        }
        Call(callStack);
    }

    public void Call(Stack<String> callStack){
        String func = callStack.pop(); /* This is the operator or function */
        if(func.equals("+")) {
            double result = Plus(callStack);
            stack.push(String.valueOf(result));
        }
        //if(func.equals("-")) Minus(callStack);
    }

    public double Plus(Stack<String> callStack){
        double a = Double.parseDouble(callStack.pop());
        double b = Double.parseDouble(callStack.pop());
        System.out.println("Answer is "+(a+b));
        return(a+b);
    }

    public static void main(String[] args) {
        new SmallLisp();
    }
}

person askque    schedule 23.12.2015    source источник
comment
Ответ, который вы ищете, сложно объяснить. Вот основные принципы. Во-первых, вам нужно записать (перечислить) все состояния, с которыми столкнется синтаксический анализатор. Затем вам нужно написать таблицу переходов, которая позволяет синтаксическому анализатору переходить из одного состояния в другое по мере того, как он сталкивается с токенами и анализирует правила. Вы на правильном пути в использовании стека: каждый раз, когда вы выполняете сдвиг, вы помещаете состояние в этот стек. Когда вы уменьшаете, вы выталкиваете несколько состояний из стека. (В вашем коде отсутствуют два важных массива, которые заставят ваш синтаксический анализатор работать.)   -  person scooter me fecit    schedule 23.12.2015
comment
Вы также можете использовать оператор switch вместо таблицы перехода (массива). Но вам все равно нужно знать все состояния парсера и способы перехода между ними (подсказка: вершина стека — это текущее состояние парсера, которое вы switch используете.)   -  person scooter me fecit    schedule 23.12.2015
comment
простой язык похож на губы, схема. Судя по тегам, вы имеете в виду Lisp и Scheme?   -  person Joshua Taylor    schedule 23.12.2015
comment
Вам будет легче анализировать, если вы не будете специально регистрировать арифметические операторы. Просто пусть ваша грамматика будет: exp ::= id | number | (exp exp exp). Затем, когда вы оцениваете выражения (после того, как вы их проанализировали), вы можете жаловаться на семантические ошибки, такие как число в позиции функции.   -  person Joshua Taylor    schedule 23.12.2015
comment
@JoshuaTaylor: Разве это не должно быть (oper exp exp) с правилом oper oper -> '+' | '-' | '*' | '/'?   -  person scooter me fecit    schedule 23.12.2015
comment
@ СкоттМ Наверное, нет. Если это действительно предполагается, что это Lisp/Scheme-ish, тогда вы хотели бы иметь возможность делать ((lambda (x) ...) ...); вам понадобятся выражения, чтобы иметь возможность отображаться в качестве первого элемента списка. Как я уже сказал, это, вероятно, будет проще реализовать в интерпретаторе/оценщике. Там проще определить модель оценки как ту, которая проверяет, является ли первый элемент +, -, * или /, и выдает ошибку, если это не так. Вопрос, как написано, касается синтаксического анализа Scheme/Lisp, а парсер Scheme/Lisp разрешает другие вещи в первой позиции.   -  person Joshua Taylor    schedule 23.12.2015
comment
@JoshuaTaylor: Сорта немного похожа на ч/б присваивание в базовом синтаксическом анализе, основанном на языке Lisp/Scheme-ish. Следовательно, ограничение области действия выглядит как назначение. OTOH, я мог быть слишком циничным.   -  person scooter me fecit    schedule 23.12.2015


Ответы (1)


Если вы хотите избежать написания собственного парсера, вы можете использовать ANTLR.

Это генератор синтаксического анализатора, который сделает всю работу за вас, если вы поддержите его корректной грамматикой.

Если вы все еще хотите написать это самостоятельно, вы все равно можете использовать ANTLR, чтобы увидеть, как они создают свои парсеры/лексеры.

person ChristophE    schedule 23.12.2015
comment
можете ли вы показать пример по моему вопросу, используя antlr? - person askque; 23.12.2015