实现类作用域

在上一章,我们已经实现了一个语言的原型,在整个开发过程中,一个jvm语言的形成方案已经很明了,我们需要将原始代码编译为class字节码文件,后续的工作也随之清晰了,我们需要对Qing的语法进行不断扩充,使它可以承担更多的功能

而第一个需要扩充的语法显然是类作用域的定义

在java的语法中,我们需要通过Class关键字来定义,类似这种Class Dog { XXX },我们已经是一个新语言了,还用Class未免没有新意,调整一下用Type怎么样

Terminal window
type Dog{
var name = "小白"
print name
var age = 3
print age
}

而在本章节结束后,我们要达成的目标则是让上述的代码可以在Qing上运行

一、语法解析规则

标题为 一、语法解析规则

语言解析规则修改:

compilationUnit : ( variable | print )* EOF;

已更改为:

compilationUnit : classDeclaration * EOF;
classDeclaration : TYPE className '{' classBody '}';
classBody : ( variable | print )* ;
TYPE : 'type';
className : ID ;

这个规则要求:

  • 每个qing文件都必须要实现有且只有一个的类定义classDeclaration
  • 每个类定义以type 类名 { 主体代码 } 的格式声明
  • 主体代码和之前的规则一样,仍旧是变量声明和打印

二、调整编译器

标题为 二、调整编译器

我们引入了一个新的实体来维护解析过程的类定义信息

public class ClassDeclaration {
private String className;
Queue<Instruction> instructionsQueue = new ArrayDeque<>();
public ClassDeclaration(String className) {
this.className = className;
}
}

将其加入到监听器中

public void exitClassDeclaration(QingParser.ClassDeclarationContext ctx) {
super.exitClassDeclaration(ctx);
String className = ctx.className().getText();
classDeclaration= new ClassDeclaration(className);
classDeclaration.setInstructionsQueue(instructionsQueue);
}

调整编译器,此处主要是两处调整:

  • 解析树返回的是我们维护的ClassDeclaration
  • 修改生成的字节码文件名
public class QingCompiler {
public static void main(String[] args) throws Exception {
new QingCompiler().compile(args);
}
public void compile(String[] args) throws Exception {
File qingFile = new File(args[0]);
String fileAbsolutePath = qingFile.getAbsolutePath();
ClassDeclaration classDeclaration= new SyntaxTreeTraverser().getClassDeclaration(fileAbsolutePath);
writeBytecodeToClassFile(fileAbsolutePath, classDeclaration);
}
private static void writeBytecodeToClassFile(String fileName, ClassDeclaration classDeclaration) throws IOException {
byte[] byteCode = new BytecodeGenerator().generateBytecode(classDeclaration);
String classFile = fileName.substring(0,fileName.lastIndexOf(File.separatorChar)+1) + classDeclaration.getClassName()+".class";
Path path = Paths.get(classFile);
OutputStream os = Files.newOutputStream(path);
os.write(byteCode);
os.close();
}
}

值得注意的是生成的字节码文件不再是以文件名命名,变为了根据类定义中的类名命名