编写一个非常简单的解析器

时间:2012-11-09 15:33:39

标签: c# parsing compiler-construction scripting

我正在编写一个非常基本的Web服务器,它必须支持极其有限的特殊服务器端脚本语言。基本上我需要支持的是“回声”,只有2个操作数的加法/减法/乘法(无除法),一个简单的“date()”函数输出日期并使用“&”运算符以连接字符串。

一个例子可能是:

echo "Here is the date: " & date();
echo "9 x 15 = : & 9*15;

我已经完成并创建了生成令牌所需的代码,但我不确定我是否使用了正确的令牌。

我为以下内容创建了令牌:

ECHO - The echo command
WHITESPACE - Any whitespace
STRING - A string inside quotations
DATE - The date() function
CONCAT - the & operator for concatenation
MATH - Any instance of binary operation (5+4, 9*2, 8-2, etc)
TERM - The terminal character (;)

我特别不确定的MATH。通常我看到人们专门为整数创建一个令牌,然后为每个运算符创建一个令牌,但由于我只想允许二进制操作,我认为将它组合成一个令牌是有意义的。如果我要分开做所有事情,我将不得不做一些额外的工作,以确保我从未接受过“5 + 4 + 1”。

所以问题1是我在正确的轨道上使用哪些令牌?

我的下一个问题是我如何使用这些令牌来确保正确的语法?我想到的方法基本上是说:“好吧,我知道我有这个令牌,这里有一个令牌列表,根据当前令牌允许接下来。列表中的下一个令牌是什么?”

基于此,我列出了所有令牌,以及直接在它们后面显示哪些令牌有效(为简单起见,不包括空格)。

ECHO        ->      STRING|MATH|DATE
STRING      ->      TERM|CONCAT
MATH        ->      TERM|CONCAT
DATE        ->      TERM|CONCAT
CONCAT      ->      STRING|MATH|DATE

问题是我根本不确定如何最好地实现这一点。我真的需要跟踪空白以确保令牌之间有空格。但这意味着我必须一次向前看两个令牌,这更令人生畏。我也不确定如何管理“有效的下一个令牌”的东西,而不仅仅是一些令人作呕的if块。我应该在尝试实际执行脚本之前检查有效语法,还是应该立即执行所有操作并在遇到意外令牌时抛出错误?在这个简单的例子中,一切都将从左到右完美地解析,没有真正的优先规则(除了MATH之外,但这也是为什么我将它组合成一个令牌,即使它感觉不对。)即使这样,我也不会不介意设计更具可扩展性和优雅的解决方案。

在我关于编写解析器的研究中,我看到很多关于创建“accept()”和“expect()”函数的引用,但我找不到任何关于它们应该做什么或它们是什么的清楚描述应该工作。

我想我只是不确定如何实现这一点,然后如何在一天结束时实际提出结果字符串。

我是否朝着正确的方向前进,是否有人知道可以帮助我了解如何最好地实现这样简单的事情的资源?我需要手动完成,不能使用像ANTLR这样的工具。

提前感谢您的帮助。

2 个答案:

答案 0 :(得分:2)

您需要做的第一件事就是丢弃所有的空格(除了字符串中的空格)。这样,当您将令牌添加到令牌列表时,您确定该列表仅包含有效令牌。例如,请考虑以下声明:

echo "Here is the date: " & date();

我将开始标记并首先根据空白区域分离 echo (是的,此处需要使用空格来分隔它,但之后无效)。然后,tokenizer会遇到双引号并继续阅读所有内容,直到找到结束双引号。同样,我为& 日期()创建了单独的令牌。

我的令牌列表现在包含以下令牌:

  

<强>回声
  “这是日期:”
  的&安培;
  的日期
  的()

现在,在解析阶段,我们阅读了这些令牌。解析器循环遍历令牌列表中的每个令牌。它读取 echo 并检查它是否有效(基于您对该语言的规则/功能)。它前进到下一个标记,并查看它是日期字符串还是数学。同样,它会检查其余的令牌。如果在任何时候,不应该存在令牌,则可以抛出指示语法错误的错误。

对于数学语句标记化,仅分别包含括号中的表达式和其余的操作数和运算符。例如:9/3 +(7-3 + 1)将具有令牌9,/,3,+和(7-3 + 1)。由于每个令牌都有自己的优先级(您在令牌结构中定义),因此您可以从最高优先级令牌开始评估到最低令牌优先级。这样您就可以使用优先级表达式。如果你仍然有困惑,请告诉我。我会给你写一些示例代码。

答案 1 :(得分:1)

expect是您的解析器为获取下一个令牌而执行的操作,如果令牌不是正确的后续令牌,则会失败。首先,您的解析器expects ECHO WHITESPACE 。这些是唯一有效的起始条款。看过“ECHO”,您的解析器expects WHITESPACE | STRING | MATH | DATE 之一;其他任何事都是错误的。等等。

accept是您的解析器看到完整的“语句” - ECHO ,然后是有效的令牌序列,然后是 TERM 。您的解析器现在有足够的信息来处理您的 ECHO 命令。

哦,手写的解析器(特别是简单的解析器)经常令人厌恶的if块集合(或道德等同物,如switch语句):)更优雅的线条会更好是某种状态机,而且还有一个语法生成器,比如yacc或GOLD Parser Generator(它反过来为你生成丑陋的ifswitch和状态机。)

编辑以提供更多详情。

为了帮助理清责任,创建一个“词法分析器”,其作用是读取输入并生成标记。这涉及决定令牌的外观。一个简单的标记就是“回声”这个词。一个不太容易的标记是数学运算;令牌将由一个或多个数字,一个运算符和一个或多个数字组成,两者之间没有空格。词法分析器将负责跳过空格,以及理解带引号的字符串和形成date()函数的字符。词法分析器将返回两个东西 - 令牌读取的类型和令牌的值(例如,“MATH”和“9 * 15”)。

使用词法分析器来读取您的输入,解析器会使用令牌并确保它们处于正确的顺序。首先,您必须看到 ECHO 令牌。如果没有,则失败并显示错误消息。之后,您必须看到 STRING DATE MATH 。如果没有,则失败并显示错误消息。之后,您循环,观察 TERM ,或者 CONCAT ,然后是另一个 STRING DATE ,或者 MATH 。如果您看到 TERM ,请中断循环。如果您既未看到 TERM 也未看到 CONCAT ,则会显示错误消息。

您可以在解析时处理ECHO命令,因为它是一个简单的语法。每次找到 STRING DATE MATH 时,请对其进行评估并将其连接到您已有的内容。当您找到 TERM 时,退出该函数并返回组合字符串。

有问题吗?评论?煎蛋? :)