手拉手教你實現(xiàn)一門編程語言 Enkel, 系列 9

本文系 Creating JVM language 翻譯的第 9 篇。
原文中的代碼和原文有不一致的地方均在新的代碼倉庫中更正過,建議參考新的代碼倉庫。

源碼

Github

1. 語法規(guī)則改動

我們新建一個規(guī)則 “returnStatement”。
那為什么不叫 “returnExpression” 呢还惠?畢竟表達式總是返回值的,語句沒有返回值么?
這聽起來有點繞口私杜,但是返回值并不總是返回一個值蚕键。在 Java 中,代碼 int x = return 5; 沒有意義衰粹, 在 Enkel 中也是如此嚎幸。換句話說,表達式總可以給一個變量賦值寄猩。這就是為什么返回是語句嫉晶,而不是表達式。

statement : variableDeclaration
           //other statements rules
           | returnStatement ;

variableDeclaration : VARIABLE name EQUALS expression;
printStatement : PRINT expression ;
returnStatement : 'return' #RETURNVOID
                | ('return')? expression #RETURNWITHVALUE;

返回語句有兩種形式:

  • RETURNVOID - 用在沒有返回值的方法中田篇。return 關(guān)鍵字是必須的替废,后面不需要表達式
  • RETURNWITHVALUE - 用在有返回值的方法中。return 關(guān)鍵字不是必須的泊柬,但是需要一個表達式

因此椎镣,方法可以顯示或者隱士的返回一個值:

SomeClass {
    fun1 {
       return  //explicitly return from void method
    }
    
    fun2 {
        //implicitly return from void method
    }
    
    int fun2 {
        return 1  //explicitly return "1" from int method
    }
    
    int fun3 {
        1  //implicitly return "1" from int method
    }
}

上述代碼經(jīng)過解析后,AST 圖形展示如下:


image

我們可以看到兽赁,AST 中并沒有處理 fun2 中的隱士返回值状答。這是因為方法是空的語句塊冷守,匹配空的語句塊作為返回值并不是一個好的想法。因此惊科,確實的返回語句會在字節(jié)碼生成階段手動添加拍摇。

2. 匹配 Antlr 上下文對象

經(jīng)過解析后,返回語句從 antlr 的上下文對象轉(zhuǎn)換成 POJO 類 ReturnStatement 馆截。這一步的目的是僅匹配字節(jié)碼生成需要的數(shù)據(jù)充活,而不是直接從 antlr 生成的對象中取數(shù)據(jù),這樣會讓代碼看起來很丑陋蜡娶。

public class StatementVisitor extends EnkelBaseVisitor<Statement> {

    //other stuff
    
    @Override
    public Statement visitRETURNVOID(@NotNull EnkelParser.RETURNVOIDContext ctx) {
        return new ReturnStatement(new EmptyExpression(BultInType.VOID));
    }
    
    @Override
    public Statement visitRETURNWITHVALUE(@NotNull EnkelParser.RETURNWITHVALUEContext ctx) {
        Expression expression = ctx.expression().accept(expressionVisitor); 
        return new ReturnStatement(expression);
    }   
}

3. 檢測隱士空返回

假設(shè)方法中包含有隱士返回混卵,在解析階段是不會生成返回語句的,這就是為什么我們需要檢測這種情景窖张,并且在字節(jié)碼生成階段手動添加返回語句幕随。

public class MethodGenerator {
    //other stuff
    private void appendReturnIfNotExists(Function function, Block block,StatementGenerator statementScopeGenrator) {
        Statement lastStatement = block.getStatements().get(block.getStatements().size() - 1);
        boolean isLastStatementReturn = lastStatement instanceof ReturnStatement;
        if(!isLastStatementReturn) {
            EmptyExpression emptyExpression = new EmptyExpression(function.getReturnType());
            ReturnStatement returnStatement = new ReturnStatement(emptyExpression);
            returnStatement.accept(statementScopeGenrator);
        }
    }
}

上述方法檢測方法最后的語句是不是返回語句,如果不是就添加返回指令宿接。

4. 生成字節(jié)碼

public class StatementGenerator {
    //oher stuff
    public void generate(ReturnStatement returnStatement) {
        Expression expression = returnStatement.getExpression();
        Type type = expression.getType();
        expression.accept(expressionGenrator); //generate bytecode for expression itself (puts the value of expression onto the stack)
        if(type == BultInType.VOID) {
            methodVisitor.visitInsn(Opcodes.RETURN);
        } else if (type == BultInType.INT) {
            methodVisitor.visitInsn(Opcodes.IRETURN);
        }
    }
}

因此赘淮,return 5 會經(jīng)過如下階段:

  • 從返回語句中獲得表達式(這里是5,類型是值)
  • 生成 5 對應(yīng)的字節(jié)碼澄阳。(expression.accept(expressionGenerator) 調(diào)用 ExpressionGenerator.generate(Value value))
  • 字節(jié)碼生成階段,會生成一個新的值 5 并壓入操作數(shù)棧
  • IRETURN 指令將操作數(shù)棧棧頂數(shù)據(jù)出棧踏拜,并返回

字節(jié)碼表示:

 bipush        5
 ireturn

5. 示例

假設(shè)我們又如下 Enkel 代碼:

SumCalculator {

    void main(string[] args) {
        print sum(5,2)
    }

    int sum (int x ,int y) {
        x+y
    }
}

生成的字節(jié)碼如下:

$ javap -c  SumCalculator
public class SumCalculator {
  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #12                 //get static field java/lang/System.out:Ljava/io/PrintStream;
       3: bipush        5
       5: bipush        2
       7: invokestatic  #16                 // call method sum (with the values on operand stack 5,2)
      10: invokevirtual #21                 // call method println (with the value on stack - the result of method sum)
      13: return                           //return

  public static int sum(int, int);
    Code:
       0: iload_0
       1: iload_1
       2: iadd
       3: ireturn //return the value from operand stack (result of iadd)
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末碎赢,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子速梗,更是在濱河造成了極大的恐慌肮塞,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姻锁,死亡現(xiàn)場離奇詭異枕赵,居然都是意外死亡,警方通過查閱死者的電腦和手機位隶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門拷窜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人涧黄,你說我怎么就攤上這事篮昧。” “怎么了笋妥?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵懊昨,是天一觀的道長。 經(jīng)常有香客問我春宣,道長酵颁,這世上最難降的妖魔是什么嫉你? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮躏惋,結(jié)果婚禮上幽污,老公的妹妹穿的比我還像新娘。我一直安慰自己其掂,他們只是感情好油挥,可當我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著款熬,像睡著了一般深寥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上贤牛,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天惋鹅,我揣著相機與錄音,去河邊找鬼殉簸。 笑死闰集,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的般卑。 我是一名探鬼主播武鲁,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蝠检!你這毒婦竟也來了沐鼠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤叹谁,失蹤者是張志新(化名)和其女友劉穎饲梭,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體焰檩,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡憔涉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了析苫。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片兜叨。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖衩侥,靈堂內(nèi)的尸體忽然破棺而出浪腐,到底是詐尸還是另有隱情,我是刑警寧澤顿乒,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布议街,位于F島的核電站,受9級特大地震影響璧榄,放射性物質(zhì)發(fā)生泄漏特漩。R本人自食惡果不足惜吧雹,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望涂身。 院中可真熱鬧雄卷,春花似錦、人聲如沸蛤售。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽悴能。三九已至揣钦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間漠酿,已是汗流浹背冯凹。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留炒嘲,地道東北人宇姚。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像夫凸,于是被迫代替她去往敵國和親浑劳。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容