我已经详细说明了问题的规格,原因在我提问后最终会变得清晰。我正在构建的程序是一个Java语言解析器,用于具有以下语法的语言(尽管这与问题不太相关):
<expr> ::= [<op> <expr> <expr>] | <symbol> | <value>
<symbol> ::= [a-zA-Z]+
<value> ::= [0-9]+
<op> ::= '+' | '*' | '==' | ‘<’
<assignment> ::= [= <symbol> <expr>]
<prog> ::= <assignment> |
[; <prog> <prog>] |
[if <expr> <prog> <prog>] |
[for <assignment> <expr> <assignment> <prog>] |
[assert <expr>] |
[return <expr>]`
这是所述语言的代码示例:
[; [= x 0] [; [if [== x 5] [= x 7] [= x [+ x 1]]] [return x]]]
相当于:
x = 0;
if (x == 5)
x = 7;
else
x = x + 1;
return x;`
保证代码的语法正确;只有具有以下内容才能定义给定代码的不正确性:
a)先前未声明的已使用变量(符号)(通过声明的含义分配给它),即使该变量用于在程序执行中从未到达的if或其他某个地方的分支中;
b)在程序可以采用的每条路径上都有一个“返回”指令,这意味着程序无法在不返回任何执行路径的情况下结束。
要求是程序应该解析代码。
我的解析器必须:
a)检查所述正确性;
b)解析代码并计算返回值是什么。
我对此的看法是:
1)将给出的代码解析为指令和表达式树;
2)通过遍历树并在使用之前查看变量是否在上部范围内声明来检查是否正确;
3)通过遍历树并查看任何执行分支是否在“返回”指令中结束来检查是否正确;
4)如果所有先前的条件都成立,则通过遍历树并记住HashMap或其他存储中的所有变量的值来评估代码的返回值。
现在,我的问题是我必须使用访问者和观察者设计模式来实现解析器。这是该项目的关键要求。我对设计模式很新,只对这两种模式有基本的把握。
我的问题是:我应该在哪里/可以在解析器中使用Observer设计模式?
使用访问者访问树的节点以获取步骤2,3和4是有意义的。但我无法弄清楚我必须使用Observer模式的位置。
我可以在实施中使用它吗?根据我的理解,Observer模式负责处理许多“观察者”可以读取和修改的数据,其核心思想是修改数据的对象将宣告可能受修改影响的其他对象。
在我的程序中修改的主要数据是树和 HashMap ,其中我存储了变量的值。两者都以线性方式访问,只有一件事。树一次构建一个节点,就此而言,没有其他节点或对象关心添加或修改节点。在评估阶段,访问每个节点并在哈希表中添加或修改变量,但是除当前节点的当前访问者之外的任何对象都不关心这一点。我想我可以让每个节点成为一个观察者,观察者在观察到变化时什么都不做,或类似的东西,强制观察者模式,但这并不是真的有用。
那么,有一个明显的答案我完全不知道了吗?是否有一个不那么明显的,但仍然提供有用的Observer实现?我可以在我的算法中的某处使用一个有用的稍微强制的Observer模式,或者是完全强制的,完全无用的方式来实现它的唯一方法吗?有没有完全不同的方法来解决问题,这将允许我使用访问者,更重要的是,使用观察者模式?
备注:
我还没有与访客进行树的评估(步骤2,3和4);我只考虑过应该怎么做。我明天会实现它,看看是否有办法在某个地方使用Observer,但考虑过如何使用它几个小时,我仍然不知道。但是,我希望有一种方法,我无法发现但在写完该部分后会变得清晰。
我为写这么多而道歉。我无法更好地总结它,仍然可以更好地提供有关情况的详细信息
另外,如果我不清楚解释,我道歉。现在已经很晚了,我已经有几个小时了,累了,我不能说我对这些概念有了完美的把握。如果有什么不清楚或想要了解某些事项的更多细节,请不要犹豫。此外,在我对这个问题的判断中,不要犹豫突出任何错误或错误的路径。
答案 0 :(得分:0)
以下是一些有关如何使用众所周知的模式和概念为您的语言构建解释器的想法:
开始处理输入流,将其拆分为令牌([;
,=
,x
,0
,]
等。 )。第一个组件(a.k.a.词法分析器,扫描程序,标记器)删除不相关的细节,如空格并生成标记。
您可以将其实现为一个简单的状态机,一次只能输入一个字符。因此,它可以实现为输入字符序列的观察者。
在下一个阶段(a.k.a.解析)中,您可以从生成的标记构建一个抽象语法树(AST)。
解析器是标记生成器的观察者,即它会收到标记生成器生成的任何新标记的通知。 (*) 它是一次喂一个令牌。在相当简单的语法的情况下,扫描仪本身也可以是一些基于堆栈的状态机。 (例如,如果它需要匹配开始和结束括号,它需要能够记住它在括号外的内容/状态,因此它可能会使用某种堆栈进行上下文管理。)< / p>
解析器构建AST后,可能几乎所有后续步骤都可以使用访问者模式来实现。每个遍历AST,定位特定节点或子树,或转换(部分)AST的算法都可以建模为访问者。一些访问者将仅模拟不返回值的操作,其他访问者为访问节点返回新的AST节点,以便可以将新的转换AST组合在一起。例如:
(*) 将解析器称为扫描器的观察者可能有点不准确。 This Software Engineering SE post对密切相关的设计模式有一个很好的总结。可能是扫描程序实现了用于处理令牌的策略。