SI413 Class 16: Built-ins, scripting applications



Homework Review
We went over the previous homework assignment. It's very important that you understand how function calls work with Central Reference Tables --- specifically how the parameters work. Make sure you understand how Central Reference Tables naturally provide dynamic scope semantics.

Implementing function calls
Here's what we know about implementing function calls with various semantics:
  1. dynamic scope - by using a Central Reference Table to store variable bindings, we naturally get dynamic scope.
  2. static scope (aka lexical scope) without nested functions - this case is like C, we only have local and global scopes. So we store a function call's local variables in its activation record on the stack. Whenever a name gets referenced, it's either found in the current activation record or in the global variable pool. Simple, simple, simple.
  3. static scope (aka lexical scope) with nested functions, but functions not 1st class - in this case, we can still store local variables on the stack, but non-local references might not be referring to global variables; they might be referring instead to variables that are local to some enclosing scope. These references will be found lower down in the call stack. From an implementation perspective, we talked about static links as a way to find non-local references.
  4. static scope (aka lexical scope) with 1st class functions - in this case we have the problem, as discussed in class, that some variables whose scope is local to the function have to live even after the function has returned. We know that such functions cannot be stack allocated, but we'll have to wait 'til next class to talk about how this will really be implemented.

Builtin functions
For purposes of this class, a "builtin" function (in an interpreted language) refers to a function that is not evaluated by interpreting code. Suppose that you wanted to provide the users of your SPL interpreter with some simple mathematical functions, like pow2(x), which would return 2x. You have two choices: first, you could create a library of SPL-coded functions that would always be loaded into the interpreter. It might contain a definition like this:
new pow2 := lambda x{ if (x = 0) { res := 1; } else { res := 2*pow2(x-1); } };
In this scenario, a user can use pow2 as if they'd coded it themselves, without the hassle of actually having to code it.

However, the above approach may not be efficient enough. If we put C++ code into the interpreter that computed pow2, it would be lots faster, because it wouldn't have the overhead of the interpretation process ... and because you can use some hardware tricks to compute pow2 extra fast. Thus, our second approach is to actually implement pow2 in C++ as part of our interpreter code, and have that executed whenever the SPL-programmer uses pow2. This is a builtin function.

Scripting languages
It is common for large applications to allow for some kind of scripting. For example, Photoshop can be scripted in Javascript, emacs in Lisp (like Scheme), and Microsoft Word in Visual Basic. The way that usually works is that there are new functions added to the language that actually change things within the application. These functions are, of necessity, builtin functions --- because they need to communicate not with the interpreter, but with the running application.

Christopher W Brown
Last modified: Mon Nov 2 09:22:36 EST 2009