当前位置: 首页 > 知识库问答 >
问题:

用XText逐行匹配“文本”文件

鄢博简
2023-03-14

我尝试为配置文件编写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终端?

我试过了

>

  • 终端文本:任意其他 这会引发警告

    以下标记定义永远无法匹配,因为之前的标记匹配相同的输入:RULE_INT、RULE_STRING、RULE_ML_COMMENT、RULE_ANY_OTHER

    (我觉得无所谓)。

    解析失败

    必需循环(…)输入“a”中的任何内容都不匹配

    终端文本:!(“\r”|“\n”|“#”) 这会引发警告

    以下标记定义永远无法匹配,因为之前的标记匹配相同的输入:RULE_INT

    (我觉得无所谓)。

    解析失败

    [第1节]的EOF缺失

    终端文本:(“!”|“$”..“~”)(除了#”)在生成lexer/parser期间没有警告。但是,在

    不匹配输入Easy123期待RULE_TEXT

    需要规则文本的无关输入“This”

    必需循环(…)与“is”中的任何内容都不匹配

    谢谢你的帮助(我希望这个语法也能对其他人有用)


  • 共有3个答案

    酆鸿彩
    2023-03-14

    作为一种解决办法,我已经改变了

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

    当然,现在=< /code>不再冲突,但这肯定不是一个好的解决方案,因为属性通常可以用name=value定义

    编辑:实际上,我的输入是一个特定的属性文件,属性是预先知道的。

    我的代码现在看起来像

    Section:
        '[' name=ID ']'
        (NEWLINE (properties+=AbstractProperty)?)+;
    
    AbstractProperty:
        ADef
            | BDef
    
    ADef:
        'A' (':'|'=') ID;
    
    BDef:
        'B' (':'|'=') Float;
    

    还有一个额外的好处,即属性名被称为关键字,并因此被着色。但是,自动补全仅建议“[”:(

    锺离德运
    2023-03-14

    解析这种格式的问题(或者至少是一个问题)是,由于文本部分可能包含=< /code>字符,所以像foo=bar这样的行将被解释为单个TEXT令牌,而不是ID,后面跟着“=”,后面跟着TEXT。我看不出有什么方法可以避免这种情况,而不禁止(或要求转义)文本部分中的=< /code>字符。

    如果这不是一个选项,我认为,唯一的解决方案是制作一个与整行匹配的令牌类型LINE,然后自己将其拆开。您可以通过从语法中删除文本ID,并将其替换为标记类型,该类型匹配所有内容,直到下一个换行符或注释符号,并且必须以有效的ID开头。类似于这样:

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

    这个令牌基本上会取代你的属性规则。

    当然,这是一个相当不令人满意的解决方案,因为它会把整行作为一个字符串,你仍然必须自己把它分开来将标识和文本部分分开。它还阻止你突出显示标识部分或=符号,因为整行是一个令牌,你不能突出显示令牌的一部分(据我所知)。总的来说,这并没有给你带来太多好处,因为你根本不使用XText,但是我没有看到更好的方法。

    葛玉堂
    2023-03-14

    这个语法起到了作用:

    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)
        }
    
    }
    
     类似资料:
    • 问题内容: 我刚刚开始学习Swift。我有要从文本文件读取的代码,应用程序显示了整个文本文件的内容。如何显示一行一行并多次调用该行? 包含以下内容: 以下是目前的情况。 如果还有另一种方法,请告诉我。将不胜感激。 问题答案: 斯威夫特3.0 该变量应该是数据的每一行。 使用的代码来自: 在用Obj-C编写的iOSSDK中逐行读取文件并使用NSString 查看旧版Swift的编辑历史记录。

    • 我刚刚开始学习 Swift。我已经从文本文件中读取了我的代码,并且应用程序显示整个文本文件的内容。如何逐行显示并多次调用该行? 包含以下内容: 以下是目前的… 如果有别的方法,请告诉我。非常感谢。

    • 问题内容: 我需要使用Java逐行读取大约5-6 GB的大型文本文件。 我如何快速做到这一点? 问题答案: 常见的模式是使用 如果你假设没有字符编码,则可以更快地读取数据。例如ASCII-7,但差别不大。你处理数据的时间很可能会花费更长的时间。 一种不太常用的模式,可以避免line泄漏的范围。 在Java 8中,你可以执行

    • dir=“某物”\temp。 我是新来的,任何帮助都很感激。我认为这是字符转义…但我不确定,我想使用正则表达式,但我想我会遇到同样的问题。 预期 C:\\users\\admin\\appdata\\local\\ dir=c:\\users\\admin\\appdata\\local\\temp

    • 类似定位器参数,文本模式是另一种常用的 Selenium 命令参数。需要使用文本模式的命令,例如:verifyTextPresent, verifyTitle, verifyAlert, assertConfirmation, verifyText, verifyPrompt。上面已经提到,LinkText 定位器可使用文本模式。文本模式使用特殊字符来模糊匹配预期的文本,而不必准确的描述该文本。

    • 问题内容: 如何在不使用?的情况下逐行读取文本文件的内容? 例如,我有一个文本文件,里面看起来像这样: 我想创建两个,然后使用类似这样的东西 这样,它分配的价值,以及价值。 问题答案: 您应该使用。 然后,您可以从下一个索引中访问列表的一个特定元素: 这将给您第一行(即:Purlplemonkeys)