手拉手教你實(shí)現(xiàn)一門編程語(yǔ)言 Enkel, 系列 13

本文系 Creating JVM language 翻譯的第 13 篇捐凭。
原文中的代碼和原文有不一致的地方均在新的代碼倉(cāng)庫(kù)中更正過(guò),建議參考新的代碼倉(cāng)庫(kù)篮撑。

源碼

Github

范圍 for 循環(huán)

本節(jié)中我們來(lái)實(shí)現(xiàn)范圍循環(huán)配猫,在范圍內(nèi)迭代值,在 Java 中大概張這個(gè)樣子:
for (int i=0;i<=5;i++)

Enkel 的等價(jià)形式:
for i from 0 to 5

我實(shí)現(xiàn)另外一個(gè)特性砂豌,循環(huán)會(huì)自動(dòng)檢測(cè)是遞增還是遞減:

for i from 0 to 5 //increment i from 0 to 5  - for (int i=0;i<=5;i++)

for i from 5 to 0 //decremenet i from 5 to 0 - for (int i=5;i>=0;i--)

遞增或者遞減必須在運(yùn)行時(shí)推斷,因?yàn)榉秶闹悼赡苁欠椒ㄕ{(diào)用的返回值光督。

for while 循環(huán)或者容器迭代器都很相似阳距,本節(jié)不做描述。

語(yǔ)法規(guī)則更改

statement : block
           //other statement alternatives
           | forStatement ;

forStatement : 'for' ('(')? forConditions (')')? statement ;
forConditions : iterator=varReference  'from' startExpr=expression range='to' endExpr=expression ;
  • forConditions 是迭代的條件表達(dá)式
  • = 提高可讀性
  • 迭代器必須是變量的名字
  • startExpression 用來(lái)初始化迭代器
  • endExpressions 是迭代器的終止值

for (i from 0 to 5) print i 圖形化的解析樹(shù)如下所示:

image

匹配 Antlr 上下文對(duì)象

Antlr 根據(jù)語(yǔ)法規(guī)則會(huì)生成 ForStatementContext 對(duì)象结借,我們用它生成對(duì)編譯器更加友好的類筐摘。可以解決迭代器變量未生明的問(wèn)題船老。

public class ForStatementVisitor extends EnkelBaseVisitor<RangedForStatement> {

    //other stuff
    
    @Override
    public RangedForStatement visitForStatement(@NotNull ForStatementContext ctx) {
        EnkelParser.ForConditionsContext forExpressionContext = ctx.forConditions();
        Expression startExpression = forExpressionContext.startExpr.accept(expressionVisitor);
        Expression endExpression = forExpressionContext.endExpr.accept(expressionVisitor);
        VarReferenceContext iterator = forExpressionContext.iterator;
        String varName = iterator.getText();
        //If variable referenced by iterator already exists in the scope
        if(scope.localVariableExists(varName)) { 
            //register new variable value
            Statement iteratorVariable = new AssignmentStatement(varName, startExpression); 
            //get the statement (usually block))
            Statement statement = ctx.statement().accept(statementVisitor); 
            return new RangedForStatement(iteratorVariable, startExpression, endExpression,statement, varName, scope); 
        //Variable has not been declared in the scope
        } else { 
            //create new local variable and add to the scope
            scope.addLocalVariable(new LocalVariable(varName,startExpression.getType())); 
            //register variable declaration statement
            Statement iteratorVariable = new VariableDeclarationStatement(varName,startExpression); 
            Statement statement = ctx.statement().accept(statementVisitor);
            return new RangedForStatement(iteratorVariable, startExpression, endExpression,statement, varName,scope);
        }
    }
}

迭代器變量可能在作用中存在或者未生明蓄拣,這兩種情況都需要被妥善處理:

var iterator = 0
for (iterator from 0 to 5) print iterator

迭代器已經(jīng)聲明過(guò),賦值給 startExpression努隙。
new AssignmentStatement(varName,startExpression);

    for (iterator from 0 to 5) print iterator

迭代器沒(méi)有聲明球恤,首先聲明,然后賦值給 startExpression荸镊。
new VariableDeclarationStatement(varName,startExpression);

字節(jié)碼生成

RangedForStatement 生成后咽斧,下面我們開(kāi)始生成字節(jié)碼。

JVM 中沒(méi)有為 for 循環(huán)設(shè)計(jì)特殊的指令躬存。一種實(shí)現(xiàn)方式就是使用控制流指令张惹。

public void generate(RangedForStatement rangedForStatement) {
    Scope newScope = rangedForStatement.getScope();
    StatementGenerator scopeGeneratorWithNewScope = new StatementGenerator(methodVisitor, newScope);
    ExpressionGenrator exprGeneratorWithNewScope = new ExpressionGenrator(methodVisitor, newScope);
    Statement iterator = rangedForStatement.getIteratorVariableStatement();
    Label incrementationSection = new Label();
    Label decrementationSection = new Label();
    Label endLoopSection = new Label();
    String iteratorVarName = rangedForStatement.getIteratorVarName();
    Expression endExpression = rangedForStatement.getEndExpression();
    Expression iteratorVariable = new VarReference(iteratorVarName, rangedForStatement.getType());
    ConditionalExpression iteratorGreaterThanEndConditional = new ConditionalExpression(iteratorVariable, endExpression, CompareSign.GREATER);
    ConditionalExpression iteratorLessThanEndConditional = new ConditionalExpression(iteratorVariable, endExpression, CompareSign.LESS);

    //generates varaible declaration or variable reference (istore)
    iterator.accept(scopeGeneratorWithNewScope);

    //Section below checks whether the loop should be iterating or decrementing
    //If the range start is smaller than range end (i from 0 to 5)  then iterate (++)
    //If the range start is greater than range end (i from 5 to 0) then decrement (--)

    //Pushes 0 or 1 onto the stack 
    iteratorLessThanEndConditional.accept(exprGeneratorWithNewScope);
    //IFNE - is value on the stack (result of conditional) different than 0 (success)?
    methodVisitor.visitJumpInsn(Opcodes.IFNE,incrementationSection);

    iteratorGreaterThanEndConditional.accept(exprGeneratorWithNewScope);
    methodVisitor.visitJumpInsn(Opcodes.IFNE,decrementationSection);

    //Incrementation section
    methodVisitor.visitLabel(incrementationSection);
    rangedForStatement.getStatement().accept(scopeGeneratorWithNewScope); //execute the body
    methodVisitor.visitIincInsn(newScope.getLocalVariableIndex(iteratorVarName),1); //increment iterator
    iteratorGreaterThanEndConditional.accept(exprGeneratorWithNewScope); //is iterator greater than range end?
    methodVisitor.visitJumpInsn(Opcodes.IFEQ,incrementationSection); //if it is not go back loop again 
    //the iterator is greater than end range. Break out of the loop, skipping decrementation section
    methodVisitor.visitJumpInsn(Opcodes.GOTO,endLoopSection); 

    //Decrementation section
    methodVisitor.visitLabel(decrementationSection);
    rangedForStatement.getStatement().accept(scopeGeneratorWithNewScope);
    methodVisitor.visitIincInsn(newScope.getLocalVariableIndex(iteratorVarName),-1); //decrement iterator
    iteratorLessThanEndConditional.accept(exprGeneratorWithNewScope);
    methodVisitor.visitJumpInsn(Opcodes.IFEQ,decrementationSection);

    methodVisitor.visitLabel(endLoopSection);
}

這看起來(lái)有點(diǎn)復(fù)雜,因?yàn)檫f增遞減的推測(cè)邏輯是在運(yùn)行時(shí)決定的岭洲。

for (i from 0 to 5) 為例宛逗,我們來(lái)看一下整個(gè)流程:

  1. 聲明迭代器變量 i 并且賦予初始值 0
  2. 檢測(cè)迭代器的值 0 是否大于結(jié)束值 5
  3. 因?yàn)?0 < 5, 因此遞增,跳到遞增部分
  4. 執(zhí)行 for 循環(huán)體內(nèi)的語(yǔ)句
  5. 遞增 1
  6. 檢查迭代器的值是否大于 5
  7. 如果條件不成立盾剩,跳到 4
  8. 循環(huán)體執(zhí)行 5 次后雷激,跳到結(jié)束部分

示例

如下 Enkel 代碼:

Loops {
    main(string[] args) {
        for i from 1 to 5 {
            print i
        }
    }
}

生成后的字節(jié)碼反編譯后的 Java 代碼:

public class Loops {
    public static void main(String[] var0) {
        int var1 = 1;
        if(var1 >= 5 ) { //should it be decremented?
            do {
                System.out.println(var1);
                --var1;
            } while(var1 >= 5);
        } else { //should it be incremented?
            do {
                System.out.println(var1);
                ++var1;
            } while(var1 <= 5);
        }

    }
}

運(yùn)行結(jié)果:

$ java Loops 
1
2
3
4
5
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末替蔬,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子屎暇,更是在濱河造成了極大的恐慌承桥,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件根悼,死亡現(xiàn)場(chǎng)離奇詭異凶异,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)挤巡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門剩彬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人矿卑,你說(shuō)我怎么就攤上這事喉恋。” “怎么了粪摘?”我有些...
    開(kāi)封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)绍坝。 經(jīng)常有香客問(wèn)我徘意,道長(zhǎng),這世上最難降的妖魔是什么轩褐? 我笑而不...
    開(kāi)封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任椎咧,我火速辦了婚禮,結(jié)果婚禮上把介,老公的妹妹穿的比我還像新娘勤讽。我一直安慰自己,他們只是感情好拗踢,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布脚牍。 她就那樣靜靜地躺著,像睡著了一般巢墅。 火紅的嫁衣襯著肌膚如雪诸狭。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天君纫,我揣著相機(jī)與錄音驯遇,去河邊找鬼。 笑死蓄髓,一個(gè)胖子當(dāng)著我的面吹牛叉庐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播会喝,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼陡叠,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼玩郊!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起匾竿,我...
    開(kāi)封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤瓦宜,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后岭妖,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體临庇,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年昵慌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了假夺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡斋攀,死狀恐怖已卷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情淳蔼,我是刑警寧澤侧蘸,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站鹉梨,受9級(jí)特大地震影響讳癌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜存皂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一晌坤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧旦袋,春花似錦骤菠、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至祭阀,卻和暖如春截亦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背柬讨。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工崩瓤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人踩官。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓却桶,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子颖系,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法嗅剖,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法嘁扼,繼承相關(guān)的語(yǔ)法信粮,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 31,581評(píng)論 18 399
  • 第2章 基本語(yǔ)法 2.1 概述 基本句法和變量 語(yǔ)句 JavaScript程序的執(zhí)行單位為行(line)趁啸,也就是一...
    悟名先生閱讀 4,118評(píng)論 0 13
  • 過(guò)年回家不傅,最可怕的就是走親訪友的拜年了 各種尷尬問(wèn)題撲面而來(lái)旅掂。 找女朋友了嗎? 工資多少胺萌ⅰ商虐? 年終獎(jiǎng)發(fā)了嗎?崖疤?秘车? ...
    茄子懸賞閱讀 236評(píng)論 2 1
  • 出自《牽手情·婚姻的藍(lán)圖》李長(zhǎng)安、馮志梅 1. 需要 【創(chuàng)2:18-20】 耶和華神說(shuō):“那人獨(dú)居不好劫哼,我要為他造...
    JoshuaHuang閱讀 558評(píng)論 0 0
  • 陽(yáng)光落在午后茶盞上叮趴,白云在空中自由飄蕩,徐徐清風(fēng)吹來(lái)了花香沦偎,書墨芬芳縈繞在書房疫向。幸福的味道充盈在心間咳蔚,心靈儲(chǔ)存的美...
    絲竹水韻閱讀 242評(píng)論 0 1