Возвращаемые значения токена в ANTLR 3 C - PullRequest
5 голосов
/ 14 октября 2010

Я новичок в ANTLR, и я пытаюсь написать простой парсер, использующий язык C (antler3C). Грамматика достаточно проста, поэтому я бы хотел, чтобы каждое правило возвращало значение, например:

number returns [long value]
 :
 ( INT {$value = $INT.ivalue;}
 | HEX {$value = $HEX.hvalue;}
 ) 
 ; 

HEX returns [long hvalue] 
    : '0' 'x' ('0'..'9'|'a'..'f'|'A'..'F')+  {$hvalue = strtol((char*)$text->chars,NULL,16);}
    ;

INT returns [long ivalue] 
    : '0'..'9'+    {$ivalue = strtol((char*)$text->chars,NULL,10);}
    ;

Каждое правило собирает возвращаемое значение своих дочерних правил, пока самое верхнее правило не возвращает красивую структуру, полную моих данных.

Насколько я могу судить, ANTLR позволяет правилам лексера (токены, например, 'INT' и 'HEX') возвращать значения точно так же, как правила синтаксического анализатора (например, "число"). Однако сгенерированный код C не будет компилироваться:

error C2228: left of '.ivalue' must have class/struct/union
error C2228: left of '.hvalue' must have class/struct/union

Я немного поковырялся, и ошибки имеют смысл - токены заканчиваются как универсальная ANTLR3_COMMON_TOKEN_struct, которая не допускает возвращаемого значения. Так что, возможно, цель C просто не поддерживает эту функцию. Но, как я уже сказал, я новичок в этом, и прежде чем уйти, чтобы найти другой подход, я хочу подтвердить, что я не могу сделать это таким образом.

Таким образом, вопрос заключается в следующем: «Поддерживает ли antler3C возвращаемые значения для правил лексеров, и если да, то как правильно их использовать?»

Ответы [ 3 ]

8 голосов
/ 14 октября 2010

На самом деле нет никакой новой информации, просто некоторые подробности о том, что @bemace уже упоминало.

Нет, правила лексера не могут иметь возвращаемых значений.См. 4.3 Правила из Определенная ссылка ANTLR :


Аргументы правил и возвращаемые значения

Так же, как вызовы функций, ANTLRПравила синтаксического анализатора и синтаксического анализатора дерева могут иметь аргументы и возвращаемые значения.Правила лексера ANTLR не могут иметь возвращаемых значений [...]


Существует две опции:

Опция 1

Вы можете выполнить преобразование вlong в правиле парсера number:

number returns [long value]
  :  INT {$value = Long.parseLong($INT.text);}
  |  HEX {$value = Long.parseLong($HEX.text.substring(2), 16);}
  ;

Опция 2

Или создайте свой собственный токен, который имеет, скажем, метод toLong(): long:

import org.antlr.runtime.*;

public class YourToken extends CommonToken {

  public YourToken(CharStream input, int type, int channel, int start, int stop) {
    super(input, type, channel, start, stop);
  }

  // your custom method
  public long toLong() {
    String text = super.getText();
    int radix = text.startsWith("0x") ? 16 : 10;
    if(radix == 16) text = text.substring(2);
    return Long.parseLong(text, radix);
  }
}

и определите в заголовке options {...} в вашей грамматике использование этого токена и переопределите метод emit(): Token в своем классе лексеров:

grammar Foo;

options{
  TokenLabelType=YourToken;
}

@lexer::members {
  public Token emit() {
    YourToken t = new YourToken(input, state.type, state.channel, 
        state.tokenStartCharIndex, getCharIndex()-1);
    t.setLine(state.tokenStartLine);
    t.setText(state.text);
    t.setCharPositionInLine(state.tokenStartCharPositionInLine);
    emit(t);
    return t;
  }
}

parse
  :  number {System.out.println("parsed: "+$number.value);} EOF
  ;

number returns [long value]
  :  INT {$value = $INT.toLong();}
  |  HEX {$value = $HEX.toLong();}
  ;

HEX
  :  '0' 'x' ('0'..'9'|'a'..'f'|'A'..'F')+
  ;

INT
  :  '0'..'9'+
  ;

Когда вы генерируете парсер и лексер и запускаете этоткласс теста:

import org.antlr.runtime.*;
import java.io.*;

public class Main {
    public static void main(String[] args) throws Exception {
        ANTLRStringStream in = new ANTLRStringStream("0xCafE");
        FooLexer lexer = new FooLexer(in);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        FooParser parser = new FooParser(tokens);
        parser.parse();
    }
}

он выдаст следующий вывод:

parsed: 51966

Первые варианты кажутся более практичными в вашем случае.

Обратите внимание, что, как выМожно видеть, что примеры приведены на Java.Я понятия не имею, если вариант 2 поддерживается в цель C / время выполнения.Я решил все еще опубликовать его, чтобы иметь возможность использовать его в качестве будущей ссылки здесь на SO.

1 голос
/ 15 января 2012

Существует третий вариант: вы можете передать объект в качестве аргумента правилу лексера.Этот объект содержит член, который представляет возвращаемое значение лексера.В рамках правила лексера вы можете установить участника.Вне правила лексера, в тот момент, когда вы его называете, вы можете получить член и делать с этим «возвращаемым значением» все, что захотите.Этот способ передачи параметров соответствует параметрам 'var' в Pascal или параметрам 'out' в C ++ и других языках программирования.

1 голос
/ 14 октября 2010

Правила Lexer должны возвращать объекты Token, потому что именно с этим Parser ожидает работать. Может быть способ настроить тип используемого объекта токена, но проще просто преобразовать токены в значения в правилах синтаксического анализатора самого низкого уровня.

social_title returns [Name.Title title]
 : SIR { title = Name.Title.SIR; }
 | 'Dame' { title = Name.Title.DAME; }
 | MR { title = Name.Title.MR; }
 | MS { title = Name.Title.MS; }
 | 'Miss' { title = Name.Title.MISS; }
 | MRS { title = Name.Title.MRS; };
...