美文网首页
手拉手教你实现一门编程语言 Enkel, 系列 11

手拉手教你实现一门编程语言 Enkel, 系列 11

作者: KevinOfNeu | 来源:发表于2018-09-08 01:32 被阅读0次

    本文系 Creating JVM language 翻译的第 11 篇。
    原文中的代码和原文有不一致的地方均在新的代码仓库中更正过,建议参考新的代码仓库。

    源码

    Github

    Java 以及默认参数

    Java 中没有默认参数是我感觉最不爽的一点。有些人建议我使用 Builder 模式,但是我觉得这样会产生大量的模板代码。我一直搞不懂为何 Java 这么久以来忽略了这个特性。虽然实现起来并不困难。

    实参(Argument) VS 形参 (Parameter)

    这两个概念非常容易被混淆。简单说来就是:

    • 形参 - 方法的签名
    • 实参 - 方法调用的传参

    方法调用的时候传递的表达式是实际参数,而形参是用来表示一个方法的签名。

    概念

    思路:在方法调用的时候,查找对应的方法签名,并且获得参数的默认值。这样字节码生成部分无需修改。方法调用的时候,把没明确给出值的参数用默认值代替,模拟正常的调用。

    语法规则更改

    语法规则变动仅发生在 functionParameterRule:

    functionParameter : type ID functionParamdefaultValue? ;
    functionParamdefaultValue : '=' expression ;
    

    方法参数包含了类型以及名字,以及后面可选的 = 以及紧随其后的默认值。

    匹配 Antlr 上下文对象

    这部分的改动也是很小的。在 FunctionParameter 类中添加新的字段 defaultValue ,字段中保存了 Optional<Expression> 。如果解析器发现 defaultValue 有明确传值,那么 Optional 中有值,否则是 empty。

    public class FunctionSignatureVisitor extends EnkelBaseVisitor<FunctionSignature> {
    
        @Override
        public FunctionSignature visitFunctionDeclaration(@NotNull EnkelParser.FunctionDeclarationContext ctx) {
           //other stuff
            for(int i=0;i<argsCtx.size();i++) { //for each parsed argument
            //other stuff
                Optional<Expression> defaultValue = getParameterDefaultValue(argCtx);
                FunctionParameter functionParameters = new FunctionParameter(name, type, defaultValue);
                parameters.add(functionParameters);
            }
            //other stuff
        }
    
        private Optional<Expression> getParameterDefaultValue(FunctionParameterContext argCtx) {
            if(argCtx.functionParamdefaultValue() != null) {
                EnkelParser.ExpressionContext defaultValueCtx = argCtx.functionParamdefaultValue().expression();
                return Optional.of(defaultValueCtx.accept(expressionVisitor));
            }
            return Optional.empty();
        }
    }
    

    生成字节码

    方法调用的字节码生成需要添加如下处理:

    • 检查实际传参的数量是否多余方法签名参数数量
    • 对于没有传递的参数,需要对默认的表达式求值

    缺失参数的索引值是这样安排的:参数的索引在方法签名的最后一个参数之前(闭区间)和方法调用的实际传参索引之间(开区间)。

    举例:
    signature: fun(int x,int x2=5,int x3=4)

    call: fun(2)

    缺失的参数是 x2 (索引是 1)和 x3(索引值是2),因为方法调用的参数索引是0, 方法签名的索引值最大是 2,所以缺失参数的索引分别是 1 和 2。

    public class ExpressionGenrator {
        public void generate(FunctionCall functionCall) {
            //other stuff
            if(arguments.size() > parameters.size()) {  
                throw new BadArgumentsToFunctionCallException(functionCall);
            }
            arguments.forEach(argument -> argument.accept(this));
            for(int i=arguments.size();i<parameters.size();i++) {
                Expression defaultParameter = parameters.get(i).getDefaultValue()
                        .orElseThrow(() -> new BadArgumentsToFunctionCallException(functionCall));
                defaultParameter.accept(this);
            }
            //other stuff   
        }
    }
    

    示例

    如下 Enkel 代码:

    DefaultParamTest {
    
        main(string[] args) {
             greet("andrew")
             print ""
             greet("kuba","enkel")
        }
    
        greet (string name,string favouriteLanguage="java") {
            print "Hello my name is "
            print name
            print "and my favourite langugage is "
            print favouriteLanguage
        }
    }
    

    得到的字节码输出:

    public class DefaultParamTest {
      public static void main(java.lang.String[]);
        Code:
           0: ldc           #8                  //push String "andrew" onto the stack
           2: ldc           #10   // push String "java" onto the stack  <-- implicit argument value
           4: invokestatic  #14                 // invoke static method greet:(Ljava/lang/String;Ljava/lang/String;)V
           7: getstatic     #20                 // get static field java/lang/System.out:Ljava/io/PrintStream;
          10: ldc           #22                 // push  empty String (empty line)
          12: invokevirtual #27                 // call Method "Ljava/io/PrintStream;".println:(Ljava/lang/String;)V to print empty line
          15: ldc           #29                 // push String "kuba"
          17: ldc           #31   // push String "enkel" <-- explicit argument value
          19: invokestatic  #14                 //invoke static method greet:(Ljava/lang/String;Ljava/lang/String;)V
          22: return
    
      public static void greet(java.lang.String, java.lang.String);
        Code:
           0: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
           3: ldc           #33                 // String Hello my name is
           5: invokevirtual #27                 // Method "Ljava/io/PrintStream;".println:(Ljava/lang/String;)V
           8: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
          11: aload_0                           // load (push onto stack) variable at index 0 (first parameter of a method)
          12: invokevirtual #27                 // Method "Ljava/io/PrintStream;".println:(Ljava/lang/String;)V
          15: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
          18: ldc           #35                 // String and my favourite langugage is
          20: invokevirtual #27                 // Method "Ljava/io/PrintStream;".println:(Ljava/lang/String;)V
          23: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
          26: aload_1                           // load (push onto stack) variable at index 1 (second parameter of a method)
          27: invokevirtual #27                 // Method "Ljava/io/PrintStream;".println:(Ljava/lang/String;)V
          30: return
    }
    

    运行输出是:

    Hello my name is 
    andrew
    and my favourite langugage is 
    java
    
    Hello my name is 
    kuba
    and my favourite langugage is 
    enkel
    

    相关文章

      网友评论

          本文标题:手拉手教你实现一门编程语言 Enkel, 系列 11

          本文链接:https://www.haomeiwen.com/subject/rhfkgftx.html