简单(大部分)变量解析器

发布于 2024-10-29 00:22:32 字数 2113 浏览 3 评论 0原文

在我的一个项目中,我需要能够提供一个非常简单的变量查找和替换解析器(主要用于路径中)。变量主要在启动期间使用,偶尔用于访问文件(不是程序的主要功能,只是加载资源),因此解析器不需要高性能。然而,我非常希望它是线程安全的。

解析器需要能够存储一组变量(目前map)并能够用字符串中相应的值替换标记。变量值可能包含其他变量,这些变量将在使用变量时解析(添加变量时不会解析,因为变量可能会随着时间的推移而添加)。

当前的变量语法看起来像这样:

$basepath$/resources/file.txt
/$drive$/$folder$/path/file

我当前的解析器使用一对 stringstream(“output”和“varname”),写入“output”流,直到找到第一个 $,即“ varname" 流直到第二个 $,然后查找变量(使用 varname.str() 的内容)。它非常简单并且工作得很好,即使在递归变量值时也是如此。

String Parse(String input)
{
    stringstream output, varname;
    bool dest = false;
    size_t total = input.length();
    size_t pos = 0;
    while ( pos < total )
    {
        char inchar = input[pos];
        if ( inchar != '$' )
        {
            if ( dest ) output << inchar;
            else varname << inchar;
        } else {
            // Is a varname start/end
            if ( !dest )
            {
                varname.clear();
                dest = true;
            } else {
                // Is an end
                Variable = mVariables.find(varname.str());
                output << Parse(Variable.value());
                dest = false;
            }
        }

        ++pos;
    }

    return output.str();
}

(错误检查等已删除)

但是,当我尝试将其应用于我想要的语法时,该方法失败了。我想要类似于 Visual Studio 用于项目变量的东西:

$(basepath)/resources/file.txt
/$(drive)/$(folder)/path/file

我还希望能够做到:

$(base$(path))/subdir/file

变量名称中的递归使我陷入困境,并且我不确定继续进行的最佳方法。

目前,我有两个可能的概念:

迭代输入字符串,直到找到 $,查找 ( 作为下一个字符,然后找到匹配的 ) (计算进出级别,直到达到正确的关闭 paran) 。将该位发送出去进行解析,然后使用返回的值作为变量名。然而,这看起来会很混乱并导致大量复制。

第二个概念是使用 char *char * &,并向前移动直到到达终止 null。解析器函数可以在解析变量名称时在对其自身的递归调用中使用指针。我不确定如何最好地实现这项技术,除了让每个调用跟踪它解析出的名称,并附加它所做的任何调用的返回值之外。

该项目只需要在 VS2010 中编译,因此 STL 流和字符串、C++0x 支持的位以及 Microsoft 特定的功能都是公平的游戏(如果这些要求发生变化,最好使用通用解决方案,但此时没有必要)观点)。不过,使用其他库也不好,尤其是 Boost。

我的两个想法似乎都比需要的更复杂和混乱,所以我正在寻找一种干净的方法来处理这个问题。讨论如何最好地做到这一点的代码、想法或文档都非常受欢迎。

In one of my projects, I need to be able to provide a very simple variable find-and-replace parser (mostly for use in paths). Variables are used primarily during startup and occasionally to access files (not the program's primary function, just loading resources), so the parser need not be high-performance. I would greatly prefer it to be thread-safe, however.

The parser needs to be able to store a set of variables (map<string, string> at the moment) and be able to replace tokens with the corresponding value in strings. Variable values may contain other variables, which will be resolved when the variable is used (not when it is added, as variables may be added over time).

The current variable grammar looks something like:

$basepath$/resources/file.txt
/$drive$/$folder$/path/file

My current parser uses a pair of stringstreams ("output" and "varname"), writes to the "output" stream until it finds the first $, the "varname" stream until the second $, then looks up the variable (using the contents of varname.str()). It's very simple and works nicely, even when recursing over variable values.

String Parse(String input)
{
    stringstream output, varname;
    bool dest = false;
    size_t total = input.length();
    size_t pos = 0;
    while ( pos < total )
    {
        char inchar = input[pos];
        if ( inchar != '

(error checking and such removed)

However, that method fails me when I try to apply it to my desired grammar. I would like something similar to what Visual Studio uses for project variables:

$(basepath)/resources/file.txt
/$(drive)/$(folder)/path/file

I would also like to be able to do:

$(base$(path))/subdir/file

Recursing in the variable name has run me into a wall, and I'm not sure the best way to proceed.

I have, at the moment, two possible concepts:

Iterate over the input string until I find a $, look for a ( as the next character, then find the matching ) (counting levels in and out until the proper close paran is reached). Send that bit off to be parsed, then use the returned value as the variable name. This seems like it will be messy and cause a lot of copying, however.

The second concept is to use a char *, or perhaps char * &, and move that forward until I reach a terminating null. The parser function can use the pointer in recursive calls to itself while parsing variable names. I'm not sure how best to implement this technique, besides having each call keep track of the name it's parsed out, and append the returned value of any calls it makes.

The project need only compile in VS2010, so STL streams and strings, the supported bits of C++0x, and Microsoft-specific features are all fair game (a generic solution is preferable in case those reqs change, but it's not necessary at this point). Using other libraries is no good, though, especially not Boost.

Both my ideas seem like they're more complicated and messier than is needed, so I'm looking for a nice clean way of handling this. Code, ideas or documents discussing how best to do it are all very much welcome.

) { if ( dest ) output << inchar; else varname << inchar; } else { // Is a varname start/end if ( !dest ) { varname.clear(); dest = true; } else { // Is an end Variable = mVariables.find(varname.str()); output << Parse(Variable.value()); dest = false; } } ++pos; } return output.str(); }

(error checking and such removed)

However, that method fails me when I try to apply it to my desired grammar. I would like something similar to what Visual Studio uses for project variables:

I would also like to be able to do:

Recursing in the variable name has run me into a wall, and I'm not sure the best way to proceed.

I have, at the moment, two possible concepts:

Iterate over the input string until I find a $, look for a ( as the next character, then find the matching ) (counting levels in and out until the proper close paran is reached). Send that bit off to be parsed, then use the returned value as the variable name. This seems like it will be messy and cause a lot of copying, however.

The second concept is to use a char *, or perhaps char * &, and move that forward until I reach a terminating null. The parser function can use the pointer in recursive calls to itself while parsing variable names. I'm not sure how best to implement this technique, besides having each call keep track of the name it's parsed out, and append the returned value of any calls it makes.

The project need only compile in VS2010, so STL streams and strings, the supported bits of C++0x, and Microsoft-specific features are all fair game (a generic solution is preferable in case those reqs change, but it's not necessary at this point). Using other libraries is no good, though, especially not Boost.

Both my ideas seem like they're more complicated and messier than is needed, so I'm looking for a nice clean way of handling this. Code, ideas or documents discussing how best to do it are all very much welcome.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

内心荒芜 2024-11-05 00:22:32

简单的解决方案是搜索字符串中的第一个 ')',然后向后查看是否有“$(”前面的标识符。如果有,则替换它并重新开始扫描。如果没有找到“$(” " 标识符,然后找到下一个 ')' - 如果没有,您就完成了。

解释一下:通过搜索 ) 您可以确定您正在为您的替换找到一个完整的标识符,然后它有机会为后续替换中使用的其他标识符做出贡献。

例子

Had a great time on $($(day)$(month)), did you?

Dictionary: "day" -> "1", "month" -> "April", "1April" -> "April Fools Day"

Had a great time on $($(day)$(month)), did you?
                           ^ find this
Had a great time on $($(day)$(month)), did you?
                      ^^^^^^ back up to match this complete substitution
Had a great time on $(1$(month)), did you?
                      ^ substitution made, restart entire process...
Had a great time on $(1$(month)), did you?
                              ^ find this
etc.

Simple solution is to search for the first ')' in the string, then move backwards to see if there's an identifier preceeded by "$(". If so, replace it and restart your scanning. If you don't find "$(" identifier, then find the next ')' - when there isn't one you're finished.

To explain: by searching for a ) you can be sure that you're finding a complete identifier for your substitution, which then has the chance to contribute to some other identifier used in a subsequent substitution.

EXAMPLE

Had a great time on $($(day)$(month)), did you?

Dictionary: "day" -> "1", "month" -> "April", "1April" -> "April Fools Day"

Had a great time on $($(day)$(month)), did you?
                           ^ find this
Had a great time on $($(day)$(month)), did you?
                      ^^^^^^ back up to match this complete substitution
Had a great time on $(1$(month)), did you?
                      ^ substitution made, restart entire process...
Had a great time on $(1$(month)), did you?
                              ^ find this
etc.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文