在我写下我的问题之前,只需要一些背景信息。我用Java编写玩具编程语言,因为我对编译器/解释器等很着迷。我用这种小语言得到了基础知识,它的基础是:
ADD 5, 6 -> c
PRINT c
# will store 11 into c
这是非常基本的,但它是一个开始。由于我只有16岁,我只是不能阅读有关技术事项的书籍,他们对我非常沉闷/乏味,我喜欢在互联网上阅读文章,或者人们在HN上发布的小教程(写作方案)例如C)。无论如何,我真的很困惑如何用语言实现功能,例如
# only integers since that's easier than multiple data types
FUNC whatever(a, b) -> return a + b
# used like
PRINT whatever(5, 6)
我能实现功能的唯一方法就是真的很糟糕,变成乱七八糟的意大利面。我想知道'适当的'将函数实现为编程语言的方法。关于语言的一点信息:我还没有实现AST,因为我还没有学过它们,我为这种语言编写了一个词法分析器,它运行良好,解析器非常简单,只是解析从上到下,从左到右(忘记了这个技术术语,递归下降解析器?)。
对不起,如果这是一个糟糕的问题,含糊不清,就像那样。从来没有在堆栈溢出上发布任何内容,并且我已经编写了一些代码来尝试实现功能但是删除它因为它没有太大的工作(这是几天前),而且我是问这个,因为我想制定一套实施计划,并且我相信它会有效。
谢谢!
答案 0 :(得分:2)
我建议您先使用Shunting-yard algorithm进行表达式评估。使用1或2个堆栈很容易实现(取决于您是输出RPN符号还是立即执行)。
我已将several little interpreters的分流码算法用于数学表达式(discussion)。
对于函数,当然你需要定义一个结构来保存所有函数的信息,比如变量的数量,局部变量名,以及函数的一些表示代码可以执行。
如果使用调用堆栈,则可以将本地参数推送到堆栈。然后你需要"编译"可执行表示,因此它使用堆栈偏移而不是变量名称。或者,您可以使用一堆哈希表作为命名空间堆栈。然后,您需要从上到下查找每个哈希表中的变量,直到找到变量。使用这些方法中的任何一种,局部变量都会使用相同的名称来模糊全局变量(这就是你想要的)。
使用分流码算法,您需要执行一些记账以响应括号。所以,用你的例子
PRINT whatever(5, 6)
PRINT
可能被认为是一个语句类型,它执行以下表达式然后打印结果。因此,您将此表达式视为几个不同的标记。
whatever ( 5 , 6 )
如果先前已定义,则可以发现 whatever
为函数名称。但是如果你想允许函数作为一等公民,那么在看到parens之前,这仍然可能不是函数调用。 (也许你只想打印函数的代码。)
然后,左侧paren (
后面的标识符显然是函数调用的开始。然后,我们需要递归计算每个逗号分隔的表达式,并安排将这些结果用作函数的参数。使用call-stack方法,只需按下两个结果即可。使用命名空间堆栈,定义两个变量并推送哈希表。
然后调用函数调用处理函数来评估函数的代码。并通过打印来评估整个表达式的结果。
答案 1 :(得分:0)
此代码尝试使用自上而下的递归下降方法来解释一种非常简单的编程语言。它使用split()方法分隔标记。用Python编写。
stmt-list = stmt | stmt stmt-list
stmt = id ":=" expr ";" | print expr ";"
expr = term {("+"|"-") term} .
term = factor {("*"|"/") factor} .
factor = id | intnum | floatnum | "(" expr ")" .
import sys
from sets import Set
parsed = []
words = []
token_set = Set(['+', '-', '*', '/', '(', ')'])
token_map={'+':'SUM','-':'SUM','*':'DIV','/':'DIV','(':'LEFT_PAR', ')':'RIGHT_PAR'}
def stmt(line):
for word in line.split():
if word == 'SUM':
expr(line)
elif word == 'DIV':
term(line)
elif word == 'LEFT_PAR':
checker = False
wordgrp = words(line.split())
for word in wordgrp:
if word!='LEFT_PAR' and not checker:
break
elif word == 'LEFT_PAR' and not checker:
checker = True
elif checker and word != 'RIGHT_PAR':
parsed.append(word)
elif checker and word == 'RIGHT_PAR':
stmt(parsed)
else:
break
def expr(line):
for word in line.split():
if (word == '+'):
factor(line);
elif word == '-':
factor(line);
else:
break
def term(line):
for word in line.split():
if (word == '*'):
factor(line);
elif word == '/':
factor(line);
else:
break
def factor(line):
syntax_checker = True
if (syntax_checker):
print(line)
syntax_checker = False
else:
print("Syntax Error");
file = open(sys.argv[-1], "r")
lines = file.readlines()
for line in lines:
stmt(line)
file.close()
答案 2 :(得分:0)
我知道这是一个旧线程,但前段时间我已经实现了一个类似于JavaScript的语言的小型解释器(有更多限制),代码发布在Github的https://github.com/guilhermelabigalini/interpreter
但它确实支持IF / CASE / LOOPS / FUNCTIONs,见下文:
function factorial(n) {
if (n == 1)
return 1;
return n * factorial(n - 1);
}
var x = factorial(6);
答案 3 :(得分:0)
只需遵循以下 4 个步骤:
获取函数名和代码
(a) 获取包含在两个 :
字符之间的函数名称,例如 :Name:
。
(b) 检查函数名后面的括号,例如:Name:(print "Hey")
。
将此代码分配给字典中的函数名称,例如Name: print "Hey"
。
创建另一个词法分析器和解析器,因为它在第一个解析器和词法分析器中不起作用。它只是一直执行,或者只执行一行代码。
获取代码,通过词法分析器,然后通过解析器运行它。当用户希望它运行时,您可以像这样分配函数::Name: (print "Hey")
。要调用它,您可以添加一个 call,例如 关键字 是 do
。因此,如果它是 do :func:
,它会检查它是否在字典中。如果是,则它通过 lexer 运行它,然后通过 parser。如果没有,就会报错。
该函数还没有参数,但我会在想出如何操作后立即更新此答案。