c# - 已设置通用操作后如何更新它?
问题描述
所以我正在制作一种“玩具语言”并使用“玩具编译器”来执行代码。
基本上,我在 C# 中设计所有东西,并简单地说它是如何工作的,只是从源文件中制作标记,循环它们并使用 C# Action 列表设计指令。
我已经尝试向后“解析/编译”所有内容,然后在执行时反转操作列表,考虑到问题的结构,这是非常愚蠢的。
这是“玩具语言”的部分源代码
printl("Hello, What is your name?")
string Name = inline()
printl("Oh, hello there " + Name)
而我的 C#“玩具编译器”是如何通过添加操作来完成的
printl("Hello, what is your name?")
将函数内的字符串作为具有值的标记给出以下解析代码:
Actions.Add(new Action(() => Console.WriteLine(CurrentTok.Value)));
尽管在代码的最后一部分中具有多个值,但它只需要一个空对象并通过将值转换为字符串来将所有值添加到循环中,直到当前标记变为')' RightParen
标记。生成一个对象,其中包含使用该ToString()
函数打印的所有值。
对于我拥有该inline()
函数的那个,还请记住,我有一个Dictionary
类型<string, object>
来存储所有变量。
Actions.Add(new Action(() => Variables[Var_name] = Console.ReadLine()));
现在问题出现在解析应该写出该值的最后一行时,因为它已经被“编译”并且变量没有值。inline()
命令执行后。该变量不会更新它的值,因为它在一个列表中。
这是“编译器”代码源的简化版本,用于更好地解释问题,注意。Current = Tokens[Index]
While(Index < Tokens.Count - 1)
{ // Index a simple int
if(Ignore.Contains(CurrentTok.Type)) // Type = Type of Token
Index++ // if it's a { or a }.. and so on
if(CurrentTok.Type == TokenType.String) // TokenType = enum
{
if(Current.Value == "inline()")
{
Variables[Current.Symbol] = " "; // so it's not undefined
Actions.Add(new Action(() => Variables[Current.Symbol] = Console.ReadLine()
)); // Current.Symbol being the variable name
} else {
Variables[Current.Symbol] = Current.Value;
}
}
if(Current.Type == TokenType.Function) {
if(Current.Symbol == "printl") {
Index++;
if(Current.Type == TokenType.LParen) { // '('
Index++;
object ToPrint = " "; // init an object
While(Current.Type != TokenType.RParen) { // ')'
if(Current.Type == TokenType.Plus)
Index++;
if(Current.Type == TokenType.PrintString) {
// PrintString being a string inside a function
// that has not been declared as an variable.
object ToAdd = Current.Value;
ToPrint += Convert.ToString(ToAdd);
}
if(Current.Type == TokenType.String) {
object ToAdd = GetVar(Current.Symbol);
//GetVar = object that returns the value if the dictionary contains it
ToPrint += Convert.ToString(ToAdd);
}
Index++;
}
Actions.Add(new Action(() => Console.WriteLine(ToPrint)));
} else {
// errors...
}
}
}
index++;
}
从我上面列出的源代码中它可以正常工作,它打印文本Hello, What is your name
并使用 readline 打开输入流。但返回时Oh, heloo there
没有名字。
解决方案
问题是您ToPrint
在解析脚本时构造 value 并且当您调用时GetVar(Current.Symbol)
, value 还不存在,因为它将在执行期间被初始化。最简单的选择就是将评估推迟ToPrint
到执行。
有很多方法可以做到这一点,例如将其转换为Func
:
Func<String> ToPrint = ()=> " "; // // this is func, evaluation is deferred to invocation
While(Current.Type != TokenType.RParen) { // ')'
if(Current.Type == TokenType.Plus)
Index++;
if(Current.Type == TokenType.PrintString) {
var ToAdd = Current.Value;
var currentValue = ToPrint; // you cannot use ToPrint in the clausure :(
ToPrint = () => currentValue() + Convert.ToString(ToAdd);
}
if(Current.Type == TokenType.String) {
var ToAddSymbol = Current.Symbol;
//GetVar = object that returns the value if the dictionary contains it
var currentValue = ToPrint
ToPrint = () => currentValue() + Convert.ToString(GetVar(ToAddSymbol)); // GetVar will be called during execution
}
Index++;
}
Actions.Add(new Action(() => Console.WriteLine(ToPrint())));
从堆栈使用的角度(如果字符串中有很多标记怎么办?)和内存的角度(这里有多少个闭包?),这个解决方案都不好。您可能会玩弄实现,但主要思想是将GetVar
调用推迟到执行时间。
编写解析器并不是一项新任务,有很多框架可以做到这一点,您可以阅读此线程以获得广泛的概述。您的解析器解决了非常自定义的场景。添加新功能可能会很痛苦且有风险,例如,想一想添加嵌套表达式的成本是多少?我不怪你的实现,但是已经写了这么多,随意学习和使用:)
推荐阅读
- javascript - 未定义 JQuery UI 组合框
- laravel - 如何通过单击按钮同时更新和插入多行数据(axios + laravel + vuejs)
- python-3.x - 加入相似的 OrderID 并聚合购买项目 Pandas/DataFrame
- c - BUILD_BUG_ON_ZERO 在简单的用户空间应用程序中不起作用
- c# - ASP.Net C#在GridView中选择行而不更新整个页面
- python - 将 Csv models.FileField 输入到 pandas 对象中
- jquery - 为什么我通过索引而不是 .val 选择选项功能不起作用?
- java - 如何修复“JRException:编译报告java源文件时出错”错误?
- mysql - MYSQL 在一个圆圈位置上选择并且只有某些结果
- python - 无法使用 python 在 3D matplotlib 中绘制极点