本文系 Creating JVM language 翻譯的第五篇。 原文中的程式碼和原文有不一致的地方均在新的程式碼倉庫中更正過,建議參考新的程式碼倉庫。
原始碼
解析規則的更改
上一節中,我們定義了 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 )* ;
複製程式碼
- 一個檔案有且僅有一個類宣告
- 類宣告包含型別和大括號中的類主體
- 類主體包含變數和列印語句(如我們之前的定義)
更改後的語法規則視覺化如下所示:
Compiler 更改
主要改變是需要把原來的程式碼從 ByteCodeGenerator
移動到 CompilationUnit
和 ClassDeclaration
,邏輯如下:
- Compiler 從
SyntaxParseTreeTraverser
獲得 AST - Compiler 例項化
CompilationUnit
:
//Compiler.java
final CompilationUnit compilationUnit = new SyntaxTreeTraverser().getCompilationUnit(fileAbsolutePath);
//Check getCompilationUnit() method body on github
複製程式碼
CompilationUnit
負責例項化ClassDeclaration
(需要傳遞類名和指令序列)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));
複製程式碼
另外一點改動是: 輸出的 .class 檔名字以類名為依據,而不是以 .enk 檔名字為依據。
String className = compilationUnit.getClassName();
String fileName = className + ".class";
OutputStream os = new FileOutputStream(fileName);
IOUtils.write(byteCode,os);
複製程式碼