手拉手教你實現一門程式語言 Enkel, 系列 5

KevinOfNeu發表於2018-09-06

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

原始碼

Github

解析規則的更改

上一節中,我們定義了 Enkel 語言的特性。本節中我們來實現 “類”。

原來的解析規則:

compilationUnit : ( variable | print )* EOF;
variable : VARIABLE ID EQUALS value;
複製程式碼

變更後的規則:

compilationUnit : classDeclaration EOF ; //root rule - our code consist consist only of variables and prints (see definition below)
classDeclaration : className '{' classBody '}' ;
className : ID ;
classBody :  ( variable | print )* ;
複製程式碼
  • 一個檔案有且僅有一個類宣告
  • 類宣告包含型別和大括號中的類主體
  • 類主體包含變數和列印語句(如我們之前的定義)

更改後的語法規則視覺化如下所示:

手拉手教你實現一門程式語言 Enkel, 系列 5

Compiler 更改

主要改變是需要把原來的程式碼從 ByteCodeGenerator 移動到 CompilationUnitClassDeclaration,邏輯如下:

  1. Compiler 從 SyntaxParseTreeTraverser 獲得 AST
  2. Compiler 例項化 CompilationUnit
//Compiler.java
final CompilationUnit compilationUnit = new SyntaxTreeTraverser().getCompilationUnit(fileAbsolutePath);
//Check getCompilationUnit() method body on github
複製程式碼
  1. CompilationUnit 負責例項化 ClassDeclaration(需要傳遞類名和指令序列)
  2. ClassDeclaration 處理類定義指令,以及迴圈處理 ClassScopeInstructions
 //ClassDeclaration.java
 MethodVisitor mv = classWriter.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
 instructions.forEach(classScopeInstruction -> classScopeInstruction.apply(mv));
複製程式碼

手拉手教你實現一門程式語言 Enkel, 系列 5

另外一點改動是: 輸出的 .class 檔名字以類名為依據,而不是以 .enk 檔名字為依據。

String className = compilationUnit.getClassName();
String fileName = className + ".class";
OutputStream os = new FileOutputStream(fileName);
IOUtils.write(byteCode,os);
複製程式碼

相關文章