端午的第一天我毛都没干,除了睡觉就是晃悠,还花了两三个小时在玩游戏。唯独看了看spring和mybatis,感觉也没什么可看的。今天睡觉睡到11点。现在我咬牙决定写会代码。
之前写到可以读取某个对象的属性了。今天打算做一下函数调用,假设对象的某个属性是一个funciton,应该可以调用这个function。这个功能也是核心功能,假设
$scope.hello=function(){
console.log('hello')
}
那么在html中,应该是这样调用的:
<button ng-click="hello()">打印</button>
事实上测试案例就是这样:
it('Function Call', function() {
var fn = parse('getName()');
expect(fn({getName:function(){return 'wangji'}})).toBe('wangji');
});
先想一下思路,对于这个字符串,产生的token是什么?应该是:getName
,(
,)
这三个。先去补完一下这个词法分析。
else if (this.is('{}[],:.()')) {//之前不能解析括号,现在加上
this.tokens.push({
text: currentChar,
identifier: true
})
this.index++;
在AST阶段如果遇到括号,就应该产生一个Call类型的节点。
else if (next.text === "(") {
primary = {type: ASTBuilder.CallExpression, callee: primary};
this.consume(')');
} else {
这个节点有一个属性是callee,因为既然是函数调用,肯定需要一个上下文。上下文就是左边的那个对象(左值和右值)。
现在可以脑子里想一下会生成一个什么样的AST树。
进入编译阶段的时候,先写一下针对ASTBuilder.CallExpression类型节点的写法。
下面这个思路已经很熟悉了,写过很多遍了,编译从AST的program开始,生成一个return xxxxx ; 的语句。然后进入AST.body节点,这个节点遇到的是CallExpression节点,需要return的是scope.callee()这个字符串。callee可以继续用recurse方法遍历。
case ASTBuilder.CallExpression:
var callee = this.recurse(ast.callee);
return callee+"&&"+callee+"()";
跑测试的时候发现不对头,结果通过跟踪发现少了一部分。
var next;
while ((next = this.expect('.', '[','('))) {//刚才忘了加左括号
if (next.text === "[") {
primary = {
type: ASTBuilder.MemberExpression,
object: primary,
property: this.primary(),
computed: true
}
this.consume(']');
} else if (next.text === "(") {
primary = { type: ASTBuilder.CallExpression, callee: primary };
this.consume(')');
} else {
现在空函数是有了。下一步是解析函数的参数。在创建CallExpression节点的时候,应该还有一个属性是参数。
假设一下解析这个add(100,200)
。
自己先想一下生成的tokens应该是什么。然后在AST构建的时候:
primary = { type: ASTBuilder.CallExpression, callee: primary ,arguments:解析参数方法,this.parseArguments()};
parseArguments方法,再次提醒:primary方法用于把token转为节点:
STBuilder.prototype.parseArguments = function () {
var args = [];
if (!this.peek(')')) {
do {
args.push(this.primary())
} while (this.expect(','));
}
return args;
}
在编译的过程里,只需要把参数也编译出来就行了。思路很简单,建立一个arguments数组,然后把相应的节点都编译一下推入数组,最后加入到生成的函数代码中。
case ASTBuilder.CallExpression:
var callee = this.recurse(ast.callee);
var arguments=[];
for(var i=0;i<ast.arguments.length;i++){
arguments.push(this.recurse(ast.arguments[i]));
}
return callee + "&&" + callee + "("+arguments.join(",")+")";
最开始的时候觉得函数参数很麻烦,现在想一下思路,
$scope.add = function (a,b) {
return a+b;
}
实际上生成的时候需要生成add(100,200)就可以了,不需要关注内在逻辑。现在跑一下试试。
成功。