解释器 运行 如何编码?
How does an interpreter run code?
阅读所有编译与解释的文章,似乎编译意味着机器将直接 运行 编译代码,而解释器将 运行 代码。但是,如果代码在机器上,解释器 运行 是如何处理的呢?难道它最终还是不得不将它正在解释的内容转换为机器代码并且仍然拥有机器 运行 它吗?归根结底,一切都必须以机器代码结束,这样机器才能 运行 对吗?似乎 interpreted 只是意味着它 运行 一次一行地通过语言,而 compiled 意味着一次通过它。之后就差不多了吧?
相关:
不,不需要将其转换为机器代码。这些指令仅向解释器本身提供指令,然后解释器自行执行。
考虑一个非常愚蠢的 "language",它包含以下指令:
add [number]
subtract [number]
divide [number]
multiply [number]
我们可以像这样实现一个 "interpreter"(用 C# 编写):
public static void ExecuteStatements(List<string> instructions)
{
int result = 0;
foreach (string instruction in instructions)
{
string[] action = instruction.Split(' ');
int number = int.Parse(action[1]);
switch (action[0].Trim().ToLower())
{
case "add":
result += number;
break;
case "subtract":
result -= number;
break;
case "divide":
result /= number;
break;
case "multiply":
result *= number;
break;
}
}
Console.WriteLine("Result: " + result);
}
ExecuteStatements
方法将被编译为机器代码。另外,我们有一个这样的文本文件:
add 1
subtract 1
add 10
multiply 50
divide 5
结果将是 100。这些字符串实际上从未被编译成任何东西——它们只是告诉解释器要采取什么行动。
显然,这个 "language" 甚至不是模糊的图灵完备,但关键是我们决不会以某种方式 "translating" 把它变成机器代码 - "interpreter" 只是执行指定的任何操作。
我实际上曾经写过一个解释器作为测试自动化框架的一部分。当有人对 API 进行调用时,我们将拦截调用,对其使用反射来确定调用是什么以及参数是什么,并将反射元数据序列化为 JSON。然后,我们稍后反序列化 JSON 并使用反射调用之前使用相同参数的任何方法 运行 。该引擎实际上并不需要机器代码 - 它只是使用反射来确定要执行的调用。
这是关键的见解:解释代码本身实际上什么都不做 - 所有它正在做的是向解释器提供它需要采取的行动。解释器已经 "knows" 如何执行您可以在解释语言中执行的所有操作,因此不需要额外的机器代码。
打个比方,将您解释的代码想象成菜谱,将解释器想象成厨师。食谱指定了 "add 1 cup of flour and mix." 之类的操作 厨师知道如何按照他在食谱中找到的任何指示进行操作,并且他自己会执行这些操作。严格来说,食谱实际上并没有做什么——它只是放在那里供厨师阅读,以便厨师知道要采取什么行动。食谱实际上不需要 成为 厨师才能完成食谱 - 它只需要知道如何遵循其指示的人。
TL;DR 你不需要 "translate" 它变成机器码 - 你只需要有足够的信息让你的解释器知道要采取什么行动.一个好的解释器已经 "knows" 如何执行该语言可以执行的任何操作,因此无需创建任何额外的机器代码。
阅读所有编译与解释的文章,似乎编译意味着机器将直接 运行 编译代码,而解释器将 运行 代码。但是,如果代码在机器上,解释器 运行 是如何处理的呢?难道它最终还是不得不将它正在解释的内容转换为机器代码并且仍然拥有机器 运行 它吗?归根结底,一切都必须以机器代码结束,这样机器才能 运行 对吗?似乎 interpreted 只是意味着它 运行 一次一行地通过语言,而 compiled 意味着一次通过它。之后就差不多了吧?
相关:
不,不需要将其转换为机器代码。这些指令仅向解释器本身提供指令,然后解释器自行执行。
考虑一个非常愚蠢的 "language",它包含以下指令:
add [number]
subtract [number]
divide [number]
multiply [number]
我们可以像这样实现一个 "interpreter"(用 C# 编写):
public static void ExecuteStatements(List<string> instructions)
{
int result = 0;
foreach (string instruction in instructions)
{
string[] action = instruction.Split(' ');
int number = int.Parse(action[1]);
switch (action[0].Trim().ToLower())
{
case "add":
result += number;
break;
case "subtract":
result -= number;
break;
case "divide":
result /= number;
break;
case "multiply":
result *= number;
break;
}
}
Console.WriteLine("Result: " + result);
}
ExecuteStatements
方法将被编译为机器代码。另外,我们有一个这样的文本文件:
add 1 subtract 1 add 10 multiply 50 divide 5
结果将是 100。这些字符串实际上从未被编译成任何东西——它们只是告诉解释器要采取什么行动。
显然,这个 "language" 甚至不是模糊的图灵完备,但关键是我们决不会以某种方式 "translating" 把它变成机器代码 - "interpreter" 只是执行指定的任何操作。
我实际上曾经写过一个解释器作为测试自动化框架的一部分。当有人对 API 进行调用时,我们将拦截调用,对其使用反射来确定调用是什么以及参数是什么,并将反射元数据序列化为 JSON。然后,我们稍后反序列化 JSON 并使用反射调用之前使用相同参数的任何方法 运行 。该引擎实际上并不需要机器代码 - 它只是使用反射来确定要执行的调用。
这是关键的见解:解释代码本身实际上什么都不做 - 所有它正在做的是向解释器提供它需要采取的行动。解释器已经 "knows" 如何执行您可以在解释语言中执行的所有操作,因此不需要额外的机器代码。
打个比方,将您解释的代码想象成菜谱,将解释器想象成厨师。食谱指定了 "add 1 cup of flour and mix." 之类的操作 厨师知道如何按照他在食谱中找到的任何指示进行操作,并且他自己会执行这些操作。严格来说,食谱实际上并没有做什么——它只是放在那里供厨师阅读,以便厨师知道要采取什么行动。食谱实际上不需要 成为 厨师才能完成食谱 - 它只需要知道如何遵循其指示的人。
TL;DR 你不需要 "translate" 它变成机器码 - 你只需要有足够的信息让你的解释器知道要采取什么行动.一个好的解释器已经 "knows" 如何执行该语言可以执行的任何操作,因此无需创建任何额外的机器代码。