• 许童童
    2019-08-30
    变量的使用范围由作用域决定,作用域由词法规则决定,词法分析生成作用域链,之后查找变量就沿着这条作用域链查找,与函数调用栈就没有关系了。一般函数的生存期就是出栈后就结束了,如果是引用对象会在本次GC中回收,如果产生了闭包,那就要等到引用闭包的变量销毁,生存期才结束。

    作者回复: 很准确,很清晰!

    
     6
  • Johnson
    2019-08-30
    现在课程的做法相当于AST之后直接解析执行了,所有的逻辑都堆在AST和紧接着的语义分析,没有把AST转化成IR,然后在这个IR上做各种事情,最后再到interpreter执行。是因为前期为了简单起见,所以先这么直观的来么?

    作者回复: 因为目前是在讲前端,所以就先不引入IR。
    同时也是在告诉同学们,哪怕我们只拿到了AST,也已经能做很多事情了。

    IR在后端部分会讲。我会给出一个自己设计的IR的例子,用IR重新实现部分功能。然后再去采用LLVM的IR。

    
     6
  • Geek_f9ea2d
    2019-09-11
    functionDeclaration
        : typeTypeOrVoid? IDENTIFIER formalParameters ('[' ']')*
          (THROWS qualifiedNameList)?
          functionBody
    中的('[' ']')* 这个没明白什么意思,函数的声明,我觉得这样就够了:typeTypeOrVoid? IDENTIFIER formalParameters

    作者回复: 这是直接照搬的Java的语法。这是对数组的支持。目前playscript并没有支持数组,但语法也就先这么放着了。

     1
     3
  • mcuking
    2019-09-07
    其实 js 的 es6 版本已经支持块级作用域,可以用 let const 声明

    作者回复: 是的,我注意到了。
    严格的表述这样写可能比较好:如果只是像java和c那样声明变量,就没有块作用域:-)

     1
     3
  • ZYS
    2019-08-31
    宫老师,可否兼顾一下用c++的学员,介绍一下cpp版本playscript如何在visual studio2010或更高的版本运行?

    作者回复: 讲后端部分的时候,主要是用cpp版本实现的。那部分的指导资料我整理一下,写一个README.md,尽快更新到Github和码云上。

    先简单说一下:
    1.如果仅仅用cpp版本的Antlr,这个比较简单,你做练习的时候可以试用一下。
    2.把Antlr和LLVM一起用的时候,要配置的东西更多一些,好在有cmake。

    
     2
  • 北冥Master
    2019-08-30
    牛逼,越来越深入了,看的有点吃力了

    作者回复: 后几讲涉及的都是语义功能,并涉及了一部分运行期技术(给后端技术部分提前做铺垫)。
    语义上的差别是每种语言真正的差别,但底层有一些共通的机制。搞搞明白对我们学各种语言都有好处。

    
     2
  • 吃瓜群众路人丙
    2019-09-22
    也就是说,栈里的上一级栈桢,不一定是 Scope 的父节点。
    老师能举个反例吗

    作者回复: 递归函数的调用。
    int a;
    int foo(){
      a = a+1;
      if (a<10){
         return foo();
      }
      else{
        return a;
      }
    }

    在递归调用的时候,你在函数里仍然可以访问全局变量。这个全局变量不在上一级的函数栈桢里。而是在最底下那个全局变量的栈桢。

    
     1
  • Geek_89bbab
    2019-09-01
    private void pushStack(StackFrame frame) {
            // 如果新加入的frame是当前frame的下一级,则入栈
            if (stack.size() > 0) {

                for (int i = stack.size()-1; i>0; i--){
                    StackFrame f = stack.get(i);
                    if (f.scope.enclosingScope == frame.scope.enclosingScope){
                        frame.parentFrame = f.parentFrame;
                        break;
                    }
                    else if (f.scope == frame.scope.enclosingScope){
                        frame.parentFrame = f;
                        break;
                    }
                    else if (frame.object instanceof FunctionObject){
                        FunctionObject functionObject = (FunctionObject)frame.object;
                        if (functionObject.receiver != null && functionObject.receiver.enclosingScope == f.scope) {
                            frame.parentFrame = f;
                            break;
                        }
                    }
                }

                if (frame.parentFrame == null){
                    frame.parentFrame = stack.peek();
                }
            }

            stack.push(frame);

            if (traceStackFrame){
                dumpStackFrame();
            }
        }
    老师可以解释一下这个函数吗?
    展开

    作者回复: 我往代码里加了注释,你可以更新一下看看!
    我也把注释拷贝到这里。
    里面有些特性,比如一等公民函数,是还没讲到的,10讲就会讲。

    第一个if:
    /*
    如果新加入的栈桢,跟某个已有的栈桢的enclosingScope是一样的,那么这俩的parentFrame也一样。
    因为它们原本就是同一级的嘛。
    比如:
    void foo(){};
    void bar(foo());

    或者:
    void foo();
    if (...){
        foo();
    }
    */

    第二个if:
    /*
    如果新加入的栈桢,是某个已有的栈桢的下一级,那么就把把这个父子关系建立起来。比如:
    void foo(){
        if (...){ //把这个块往栈桢里加的时候,就符合这个条件。
        }
    }
    再比如,下面的例子:
    class MyClass{
        void foo();
    }
    MyClass c = MyClass(); //先加Class的栈桢,里面有类的属性,包括父类的
    c.foo(); //再加foo()的栈桢
        */

    第3个if:
    /*
    这是针对函数可能是一等公民的情况。这个时候,函数运行时的作用域,与声明时的作用域会不一致。
    我在这里设计了一个“receiver”的机制,意思是这个函数是被哪个变量接收了。要按照这个receiver的作用域来判断。
    */

    
     1
  • Aaaaaaaaaaayou
    2020-02-05
    老师,playscript-java/src/main/play/DefaultFunctionType.java 中 public static boolean isType(FunctionType type1, FunctionType type2) 函数中 List<Type> paramTypes2 = type1.getParamTypes(); 是不是写错了? type1 应该改为 type2
    
    
  • 草戊
    2019-12-22
    //全局变量
    int i = 0;
    {
        //这里引用的是全局变量
        i = 2;
        println(i); //输出:2

        //允许在块里新创建一个同名的变量
        int i = 3;
        println(i); //输出:3
    }
    您好,上面例子中 【i = 2;】这句话在块中使用全局变量,但是实际上此块中也定义了i变量,只不过位置比较靠后,这种情况,本文中的作用域有办法解决吗?如果优先在本块中查找,那么会不会找到自己块中的i,而不是全局的i呢?
    展开

    作者回复: 实际上,在每一行代码,可以见到的“可用表达式”的集合都是不一样的。也就是说,在每一行,你可以反问的变量都是不同的。
    比如,在int i = 3之后,你访问的i,和在它前面访问的i,是不一样的。
    也就是说,块作用域,并不是在整个块里可见,而是在这个块里变量声明之后才可见。
    在28讲,数据流分析中,专门有活跃性分析的一个算法,会教会你确定在每个位置的可用变量。

    
    
  • E
    2019-12-04
    有可以生成语义分析器的工具吗

    作者回复: 没有。
    但是antlr支持在语法规则里嵌入一些代码,在语法分析的过程中完成一些语义分析工作。

    
    
  • 风
    2019-10-01
    宫老师,请教两个问题,C++部分,playscript-cpp这个目录下,
    (1)antlr4.7.2-runtime 这个目录包含antlr4.7.2的源码吗,还是只是包含由antlr生成的parser等程序编译所需要的文件?antlr4.7.2这个工具有没有C++的实现源码?
    (2)lib目录中放的a文件和dylib文件是干嘛的呢?

    作者回复: 这里的代码我还没整理整齐。在后端部分的课程放出时,我会整理好。先简单的说一下:
    Antlr本身是Java写的,但它可以生成其他语言的编译工具。这些编译工具呢,要调用一些基础的功能,这些功能就是针对不同语言的运行时(runtime)。
    用C++编程时,需要用到头文件,然后用到它的库。a文件和dylib就是库。

    
    
  • David
    2019-09-28
    老师你好,javascript文件中定义一个function或者一个class,这个function也是放入全局作用域的栈帧中的吗?这些class在运行器中是放在栈帧中的吗?

    作者回复: 在第21讲,里面提了内存布局的设计问题,一般会分为代码区、静态数据区、栈区、堆区。
    如果只是function和class的定义,应该放在代码区才对。
    在运行某个函数的时候,运行期动态生成的临时数据,才会放入栈里。
    但每种语言在这方面的设计有它自己很大的灵活性。比如静态编译的语言和解释型语言的机制就很不一致。
    08讲的栈,还是比较概念化的,简单的模拟了一个栈。21、22、23三讲,有一个符合标准的调用约定的栈的实现,也就是说跟C语言的实现是一致的,甚至可以跟C语言生成的二进制目标文件链接到一起。

    
    
  • 赖阿甘
    2019-09-23
    // 函数声明
    functionDeclaration
        : typeTypeOrVoid? IDENTIFIER formalParameters ('[' ']')* //函数参数后面的方括号是做什么用的
          functionBody
        ;
    老师请问函数参数后面的方括号是做什么用的
    展开

    作者回复: 是用来支持数组的。我们并没有实现数组特性,但语法上留在那了。

    
    
  • 曾经瘦过
    2019-09-19
    基本看懂了 在巩固一下 跑跑代码 感觉越来越深入了 大概能明白为啥学好编译原理可以更好更快的学其他语言
    
    
  • 沉淀的梦想
    2019-09-02
    用PlayScript的代码运行课程中的示例时会报一个空指针异常
    String script = "int age = 44; for(int i = 0;i<10;i++) { age = age + 2;} int i = 8;";

    Exception in thread "main" java.lang.NullPointerException
        at play.ASTEvaluator.visitStatement(ASTEvaluator.java:617)
        at play.ASTEvaluator.visitBlockStatement(ASTEvaluator.java:363)
        at play.ASTEvaluator.visitBlockStatements(ASTEvaluator.java:723)
        at play.ASTEvaluator.visitProg(ASTEvaluator.java:733)
        at play.PlayScriptParser$ProgContext.accept(PlayScriptParser.java:2031)
        at org.antlr.v4.runtime.tree.AbstractParseTreeVisitor.visit(AbstractParseTreeVisitor.java:18)
        at play.PlayScriptCompiler.Execute(PlayScriptCompiler.java:28)
        at play.PlayScript.main(PlayScript.java:98)
    展开

    作者回复: 1.是运行双引号内部的部分,不是连String script= 也带上。
    2.是使用playscript-java这个工程吗?别用错了工程。
    如果还有问题,继续给我提问!
    希望不影响你继续动手实践的热情!

     1
    
  • 李懂
    2019-08-30
    原来栈里放的栈贞,栈贞是Scope,类似执行上下文,里面保存了变量!以前一直以为进栈,是放的执行函数体,跟上脚步!

    作者回复: 这个栈桢还是拿java模拟的,让大家有个概念。到学后端的时候,那里有更物理的栈桢实现。到时候你可以进一步加深一下认识:-D

    
    
我们在线,来聊聊吧