用解释编程语言实现函数



在我写我的问题之前,只需要一些背景信息。我正在用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,因为我还没有学会它们,我为该语言编写了一个lexer,它运行良好,解析器非常简单,只从上到下、从左到右进行解析(忘记了递归下降解析器的技术术语了吗?)。

很抱歉,如果这是一个糟糕的问题,含糊的,诸如此类的。以前从未发布过任何关于堆栈溢出的内容,我写过一些代码试图实现函数,但由于它的工作效果不太好(这是几天前的事),所以删除了它。我之所以这么问,是因为我想有一套实现计划,我相信它会起作用。

谢谢!

我建议您从使用调车场算法的表达式评估开始。使用1或2个堆栈很容易实现(取决于您是输出RPN表示法还是立即执行)。

我已经为几个数学表达式(讨论)的小解释器使用了调车场算法。

当然,对于函数,您需要定义一个结构来保存函数的所有信息,如变量数量、局部变量名称,以及可以执行的函数代码的一些表示形式。

如果使用调用堆栈,则可以将本地参数推送到堆栈中。然后,您需要"编译"可执行文件表示,使其使用堆栈偏移量而不是变量名。或者,可以使用哈希表堆栈作为命名空间堆栈。然后,您需要从上到下在每个哈希表中查找变量,直到找到变量为止。使用这两种方法中的任何一种,局部变量都会模糊具有相同名称的全局变量(这正是您想要的)。

使用调车场算法,您需要根据括号进行一些记账。因此,以为例

PRINT whatever(5, 6)

PRINT可能被识别为执行以下表达式然后打印结果的语句类型。因此,您将看到这个表达式是几个不同的标记。

whatever    (   5   ,   6   )

如果CCD_ 2是先前定义的,则可以发现它是一个函数名称。但是,如果您希望允许函数作为一级公民,那么在您看到parens之前,这可能仍然不是一个函数调用。(也许你只想打印函数的代码。)

然而,后面跟有左括号(的标识符显然是函数调用的开始。然后,我们需要递归地评估每个逗号分隔的表达式,并安排将这些结果用作函数的参数。使用调用堆栈方法,只需推送两个结果。使用名称空间堆栈,定义这两个变量并推送哈希表。

然后调用函数调用处理函数来评估函数的代码。并通过打印将结果用作对整个表达式求值的结果。

此代码试图通过使用自上而下的递归下降方法来解释一种非常简单的编程语言。它使用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()

我知道这是一个旧线程,但不久前我已经为类似JavaScript的语言实现了一个小型解释器(有更多限制),代码发布在Githubhttps://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);       

只需遵循以下4个步骤:

  1. 获取函数的名称,然后获取代码

    (a) 获取两个:字符之间的函数名,例如:Name:

    (b) 检查函数名后面的括号,例如:Name:(print "Hey")

  2. 将此代码分配给字典中的函数名,例如Name: print "Hey"

  3. 创建另一个lexer解析器,因为它在第一个解析器和lexer中不起作用。它只是执行整个时间,或者只执行一行代码。

  4. 获取代码,通过lexer运行,然后通过解析器。当用户希望它运行时,您分配了如下函数::Name: (print "Hey")。要调用它,您可以添加一个调用,例如关键字do。因此,如果它是do :func:,它会检查它是否在字典中。如果是,那么它将通过lexer运行它,然后通过解析器。如果不是,则会出现错误。

缺少参数

这个函数还没有参数,但我会在弄清楚如何操作后尽快更新这个答案。

最新更新