[英]Implementing functions in an interpreted programming language
在我寫我的問題之前,只是一些背景信息。 我正在用 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)
我可以實現功能的唯一方法將是真正的hack-y,變成一團亂七八糟的意大利面條。 我想知道將函數實現為編程語言的“正確”方法。 關於語言的一些信息:我還沒有實現 AST,因為我還沒有學習它們,我為該語言編寫了一個詞法分析器,效果很好,解析器非常簡單,只是從上到下,從左到右進行解析(忘記了這個遞歸下降解析器的技術術語?)。
對不起,如果這是一個不好的問題,含糊不清,諸如此類。 以前從未在堆棧溢出上發布任何內容,我已經編寫了一些代碼來嘗試實現功能,但由於效果不佳而將其刪除(這是幾天前的),我之所以問這個是因為我想有一套實施計划,我相信它會奏效。
謝謝!
我建議您從使用Shunting-yard 算法的表達式評估開始。 使用 1 或 2 個堆棧很容易實現(取決於您是輸出 RPN 符號還是立即執行)。
對於函數,當然您需要定義一個結構來保存函數的所有信息,例如變量數量、局部變量名稱以及可以執行的函數代碼的一些表示。
如果使用調用堆棧,則可以將本地參數壓入堆棧。 然后您將需要“編譯”可執行表示,以便它使用堆棧偏移量而不是變量名稱。 或者,您可以使用哈希表堆棧作為命名空間堆棧。 然后,您需要從上到下在每個哈希表中查找變量,直到找到該變量。 使用這兩種方式中的任何一種,局部變量都會掩蓋具有相同名稱的全局變量(這是您想要的)。
使用調車場算法,您需要對括號進行一些記賬。 所以,以你的例子
PRINT whatever(5, 6)
PRINT
大概被識別為執行以下表達式然后打印結果的語句類型。 因此,您會將此表達式視為幾個不同的標記。
whatever ( 5 , 6 )
如果之前定義了whatever
可以被發現的函數名。 但是如果你想允許函數作為一等公民,在你看到括號之前這仍然可能不是函數調用。 (也許您只想打印函數的代碼。)
后跟左括號的標識符(
然而,顯然是函數調用的開始。然后我們需要遞歸計算每個逗號分隔的表達式,並安排這些結果用作函數的參數。使用調用- stack方法,只壓入兩個結果,用namespace stack,定義兩個變量,壓入hash-table。
然后調用您的函數調用處理函數來評估函數的代碼。 並使用結果作為評估整個表達式的結果,通過打印它。
這段代碼試圖通過使用自頂向下的遞歸下降方法來解釋一種非常簡單的編程語言。 它使用 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 的語言實現了一個小型解釋器(有更多限制),代碼發布在 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);
只需按照以下 4 個步驟操作:
獲取函數名稱,然后獲取代碼
(a) 獲取包含在兩個:
字符之間的函數名稱,例如:Name:
。
(b) 檢查函數名稱后的括號,例如:Name:(print "Hey")
。
將此代碼分配給字典中的函數名稱,例如Name: print "Hey"
。
創建另一個詞法分析器和解析器,因為它在第一個解析器和詞法分析器中不起作用。 它只是一直執行,或者只執行一行代碼。
獲取代碼,通過lexer運行它,然后通過parser運行它。 當用戶希望它運行時,您可以像這樣分配函數: :Name: (print "Hey")
。 要調用它,您可以添加一個調用,例如關鍵字是do
。 因此,如果是do :func:
它會檢查它是否在字典中。 如果是,則它通過lexer運行它,然后通過parser 。 如果沒有,它會給出一個錯誤。
該函數還沒有參數,但我會在弄清楚該怎么做后立即更新此答案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.