Skip to content

Latest commit

 

History

History

part-06

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

Part 6 - static function calls

In this part of the series, we add support for function calls to our language.

We implement two built-in JavaScript functions from the Math object, Math.abs() and Math.pow().

Grammar

The grammar is in the EasyScript.g4 file. The only changes compared to the grammar from part 5 are a few new expression types: we add call expressions, of course, but also complex references, so we can write Math.abs. We also introduce negation ("unary minus") expressions, like -3, so that we can test the Math.abs function with negative numbers.

TruffleLanguage class

Our TruffleLanguage class implements the parse(ParsingRequest) method identically like in the previous part -- by delegating to the parser, which uses the classes generated by ANTLR from the aforementioned EasyScript.g4 file to perform the parsing, and then translates the parse tree into the Truffle AST. Note that we convert a complex reference like Math.abs into a simple name "Math.abs". Since we don't allow periods in the name of variables, we know user-defined variables will never collide with these interpreter-created variables.

Inside the createContext() method, we populate the global scope (which is the same global scope we introduced in the previous article) with the built-in functions. Each function is represented by the FunctionObject class, which contains a CallTarget (something that we've been seeing since part 1), which contains a RootNode, which contains the expression Node representing the body of the function. This body Node has as its children as many ReadFunctionArgExprNode instances as arguments that function takes. This allows writing @Specializations in the expression body Node that simply use the values of the function arguments.

Note that, for the pow() function, we make its body expression Node, the PowFunctionBodyExprNode class, inherit from the BuiltInFunctionBodyExprNode class which uses Truffle's @GenerateNodeFactory annotation. This makes the Truffle DSL generate a PowFunctionBodyExprNodeFactory class which implements the NodeFactory<PowFunctionBodyExprNode> interface. That interface can then be used to write a helper method inside EasyScriptTruffleLanguage (in our case, that method is called defineBuiltInFunction()) that reduces duplication when creating an instance of the built-in function's body expression Node.

The invoking side

The invoking side is implemented by the FunctionCallExprNode class. It evaluates the target of the call, its arguments (we have to evaluate them explicitly, in a loop, as the Truffle DSL has a limitation where it doesn't support specializations with a variable amount of children Nodes), and then delegates to a FunctionDispatchNode.

The FunctionDispatchNode class uses specializations to optimize the performance of function calls, depending on how stable the target of the call is.

FunctionObject as a polyglot value

Finally, we need to make our FunctionObject class a polyglot value, similarly like we did for Undefined in the previous article, as functions can now be returned from EasyScript code (for example, in expressions like Math.abs;). So, we make FunctionObject implement the TruffleObject marker interface, and, to allow our functions to be called from other languages, override the isExecutable() and execute() messages from the interop library. When implementing execute(), we re-use the FunctionDispatchNode we saw in FunctionCallExprNode above, so that polyglot calls are optimized the same as in-language calls.

However, note that we now need to check whether the values we're called with are actually valid EasyScript values, as other languages have the ability to pass arbitrary types into our Nodes, which only handle int, double, Undefined and FunctionObject types. So, we perform a check inside FunctionObject.execute() to make sure the passed arguments are of one of the types we allow.


There is a unit test exercising a few common scenarios with function calls.