4

構成ファイル用の Xtext BNF を書き込もうとしています (.ini拡張子で知られています) 。

たとえば、正常に解析したい

[Section1]
a = Easy123
b = This *is* valid too

[Section_2]
c = Voilà # inline comments are ignored

私の問題は、プロパティ値(「=」の右側にあるもの)を一致させることです。

IDプロパティが端末に一致する場合、現在の文法は機能します(例: a = Easy123)。

PropertyFile hidden(SL_COMMENT, WS):
    sections+=Section*;

Section:
    '[' name=ID ']'
    (NEWLINE properties+=Property)+
    NEWLINE+;

Property:
    name=ID (':' | '=') value=ID ';'?;

terminal WS:
    (' ' | '\t')+;

terminal NEWLINE:
// New line on DOS or Unix 
    '\r'? '\n';

terminal ID:
    ('A'..'Z' | 'a'..'z') ('A'..'Z' | 'a'..'z' | '_' | '-' | '0'..'9')*;

terminal SL_COMMENT:
// Single line comment
    '#' !('\n' | '\r')*;

文法を一般化して任意のテキストに一致させる方法がわかりません (例: c = Voilà)。

確かに、新しい端末プロパティを導入する必要があります: name=ID (':' | '=') value=TEXT ';'?;

質問:この端末をどのように定義すればよいですか?TEXT

私が試してみました

  • terminal TEXT: ANY_OTHER+; これにより警告が発生します

    前のトークンが同じ入力に一致するため、次のトークン定義は一致しません: RULE_INT、RULE_STRING、RULE_ML_COMMENT、RULE_ANY_OTHER

    (関係ないと思います)。

    解析が失敗する

    必要なループ (...)+ は、入力 'à' で何にも一致しませんでした

  • terminal TEXT: !('\r'|'\n'|'#')+; これにより警告が発生します

    前のトークンが同じ入力に一致するため、次のトークン定義は一致しません: RULE_INT

    (関係ないと思います)。

    解析が失敗する

    [Section1] に EOF がありません

  • terminal TEXT: ('!'|'$'..'~');#(これはとを除くほとんどの文字をカバーします") lexer/parser の生成中に警告はありません。ただし、解析は失敗します

    入力 'Easy123' が不一致で、RULE_TEXT が必要です

    無関係な入力 'This' は RULE_TEXT を予期しています

    必要なループ (...)+ は「is」で何にも一致しませんでした

ご協力ありがとうございます (この文法が他の人にも役立つことを願っています)

4

3 に答える 3

4

この文法はトリックを行います:

grammar org.xtext.example.mydsl.MyDsl hidden(SL_COMMENT, WS)

generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"
import "http://www.eclipse.org/emf/2002/Ecore"

PropertyFile:
    sections+=Section*;

Section:
    '[' name=ID ']' 
    (NEWLINE+ properties+=Property)+
    NEWLINE+;

Property:
    name=ID value=PROPERTY_VALUE;

terminal PROPERTY_VALUE: (':' | '=') !('\n' | '\r')*;

terminal WS:
    (' ' | '\t')+;

terminal NEWLINE:
// New line on DOS or Unix 
    '\r'? '\n';

terminal ID:
    ('A'..'Z' | 'a'..'z') ('A'..'Z' | 'a'..'z' | '_' | '-' | '0'..'9')*;

terminal SL_COMMENT:
// Single line comment
    '#' !('\n' | '\r')*;

重要なのは、文法だけで完全なセマンティクスをカバーしようとせず、他のサービスも考慮に入れることです。最終規則PROPERTY_VALUEは、先頭の割り当てとオプションの末尾のセミコロンを含む完全な値を消費します。

次に、その言語の値コンバーター サービスを登録し、入力の重要でない部分を処理します。

import org.eclipse.xtext.conversion.IValueConverter;
import org.eclipse.xtext.conversion.ValueConverter;
import org.eclipse.xtext.conversion.ValueConverterException;
import org.eclipse.xtext.conversion.impl.AbstractDeclarativeValueConverterService;
import org.eclipse.xtext.conversion.impl.AbstractIDValueConverter;
import org.eclipse.xtext.conversion.impl.AbstractLexerBasedConverter;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.util.Strings;

import com.google.inject.Inject;

public class PropertyConverters extends AbstractDeclarativeValueConverterService {
    @Inject
    private AbstractIDValueConverter idValueConverter;

    @ValueConverter(rule = "ID")
    public IValueConverter<String> ID() {
        return idValueConverter;
    }

    @Inject
    private PropertyValueConverter propertyValueConverter;

    @ValueConverter(rule = "PROPERTY_VALUE")
    public IValueConverter<String> PropertyValue() {
        return propertyValueConverter;
    }

    public static class PropertyValueConverter extends AbstractLexerBasedConverter<String> {

        @Override
        protected String toEscapedString(String value) {
            return " = " + Strings.convertToJavaString(value, false);
        }

        public String toValue(String string, INode node) {
            if (string == null)
                return null;
            try {
                String value = string.substring(1).trim();
                if (value.endsWith(";")) {
                    value = value.substring(0, value.length() - 1);
                }
                return value;
            } catch (IllegalArgumentException e) {
                throw new ValueConverterException(e.getMessage(), node, e);
            }
        }
    }
}

次のようにランタイム モジュールにサービスを登録すると、次のテスト ケースが成功します。

@Override
public Class<? extends IValueConverterService> bindIValueConverterService() {
    return PropertyConverters.class;
}

テストケース:

import org.junit.runner.RunWith
import org.eclipse.xtext.junit4.XtextRunner
import org.xtext.example.mydsl.MyDslInjectorProvider
import org.eclipse.xtext.junit4.InjectWith
import org.junit.Test
import org.eclipse.xtext.junit4.util.ParseHelper
import com.google.inject.Inject
import org.xtext.example.mydsl.myDsl.PropertyFile
import static org.junit.Assert.*

@RunWith(typeof(XtextRunner))
@InjectWith(typeof(MyDslInjectorProvider))
class ParserTest {

    @Inject
    ParseHelper<PropertyFile> helper

    @Test
    def void testSample() {
        val file = helper.parse('''
            [Section1]
            a = Easy123
            b : This *is* valid too;

            [Section_2]
            # comment
            c = Voilà # inline comments are ignored
        ''')
        assertEquals(2, file.sections.size)
        val section1 = file.sections.head
        assertEquals(2, section1.properties.size)
        assertEquals("a", section1.properties.head.name)
        assertEquals("Easy123", section1.properties.head.value)
        assertEquals("b", section1.properties.last.name)
        assertEquals("This *is* valid too", section1.properties.last.value)

        val section2 = file.sections.last
        assertEquals(1, section2.properties.size)
        assertEquals("Voilà # inline comments are ignored", section2.properties.head.value)
    }

}
于 2013-01-15T16:19:49.653 に答える
1

このような形式の解析に関する問題(またはとにかく1つの問題)は、テキスト部分に=文字が含まれている可能性があるため、のような行foo = barがIDではなく単一のTEXTトークンとして解釈され、その後に「=」が続き、その後に続くことです。TEXT。_ =テキスト部分の文字を禁止する(またはエスケープする必要がある)ことなしに、それを回避する方法はわかりません。

それが選択肢ではない場合、唯一の解決策はLINE、行全体に一致するトークンタイプを作成し、それを自分で分解することだと思います。これを行うには、文法を削除TEXTして、次の改行またはコメント記号までのすべてに一致し、有効なIDで始まる必要がIDあるトークンタイプに置き換えます。LINEだからこのようなもの:

LINE :
    ('A'..'Z' | 'a'..'z') ('A'..'Z' | 'a'..'z' | '_' | '-' | '0'..'9')*
    WS* '=' WS*
    !('\r' | '\n' | '#')+
;

このトークンは基本的にPropertyルールを置き換えます。

もちろん、これは行全体を文字列として表示し、IDをテキスト部分から分離するために自分で選択する必要があるため、かなり不十分な解決策です。また、行全体が1つのトークンであり、トークンの一部を強調表示できないため、ID部分または=記号を強調表示することもできません(私が知る限り)。全体として、これはまったく使用しないことであなたにそれほど多くを買うことはありませんXTextが、私はより良い方法を見ていません。

于 2012-12-21T16:29:55.330 に答える
0

回避策として、私は変更しました

Property:
    name=ID ':' value=ID ';'?;

もちろん、=これ以上競合することはありませんが、プロパティは通常、次のように定義できるため、これは確かに良い解決策ではありません。name=value

編集:実際、私の入力は特定のプロパティファイルであり、プロパティは事前にわかっています。

私のコードは次のようになります

Section:
    '[' name=ID ']'
    (NEWLINE (properties+=AbstractProperty)?)+;

AbstractProperty:
    ADef
        | BDef

ADef:
    'A' (':'|'=') ID;

BDef:
    'B' (':'|'=') Float;

追加の利点があります。プロパティ名はキーワードとして知られており、そのように色付けされています。ただし、オートコンプリートは'[' :(

于 2013-01-15T20:54:02.787 に答える