Command line interpreter and REPL for JSCocoa

A few months ago I started working on a JavaScript to Objective-C bridge. We had already implemented Objective-C in JavaScript, so I figured “why not?”

Well, I never got very far, but thankfully Patrick Geiller apparently had the same idea and actually executed it: He announced JSCocoa today. It looks like it’s a solid bridge, about up to par with PyObjC and RubyCocoa.

While the included GUI interface for trying out JSCocoa is nice, I prefer command line interfaces for my languages, so I ripped out the few lines of code from my original bridge and plugged in JSCocoa.

Code and build instructions on GitHub.

It’s very bare bones at the moment: it will either read one or more file names from the command line arguments, or if no arguments are supplied it will present a no-frills REPL. Obviously line-editing, etc would be one of the next steps, but for now it works nicely with rlwrap.

#import <Foundation/Foundation.h>
#import "JSCocoaController.h"

void JSValuePrint(JSContextRef, JSValueRef, JSValueRef *);

int main (int argc, const char * argv[])
    [[NSAutoreleasePool alloc] init];
    id c = [JSCocoaController sharedController];
    JSGlobalContextRef ctx = [c ctx];
    if (argc > 1)
        for (int i = 1; i < argc; i++)
            [c evalJSFile:[NSString stringWithFormat:@"%s", argv[i]]];
        while (1)
            char buffer[1024];
            printf("js> ");
            if (fgets(buffer, 1024, stdin) == NULL)
            JSStringRef script = JSStringCreateWithUTF8CString(buffer);
            JSValueRef exception = NULL;
            if (JSCheckScriptSyntax(ctx, script, 0, 0, &exception) && !exception)
                JSValueRef value = JSEvaluateScript(ctx, script, 0, 0, 0, &exception);
                if (exception)
                    JSValuePrint(ctx, exception, NULL);
                if (value && !JSValueIsUndefined(ctx, value))
                    JSValuePrint(ctx, value, &exception);
                printf("Syntax error\n");

void JSValuePrint(
                  JSContextRef ctx,
                  JSValueRef value,
                  JSValueRef *exception)
    JSStringRef string = JSValueToStringCopy(ctx, value, exception);
    size_t length = JSStringGetLength(string);
    char *buffer = malloc(length+1);
    JSStringGetUTF8CString(string, buffer, length+1);

Presenting GCCalc: a horrible abuse of GCC

Following an [interesting discussion on Reddit]( about [first class functions]( in C, I was inspired to see what I could do with this new-found knowledge. The result is what I affectionately call “GCCalc”, for reasons that will become clear below.

GCCalc is a simple command line calculator, much like the common [bc]( calculator on many Unix systems. It’s implementation, however, is *very* different than most calculators. While bc is said to have “C-like syntax”, GCCalc’s syntax *is* C. Whatever you enter on the command line automatically gets compiled, loaded, and executed, and the result is returned (as a double) and printed to the screen.

You can either enter expressions like:


or you can enter whole C statements (as long as they’re on one line, for now) like:

int i; for (i=0;i<10;i++) { printf("hello world!\n"); } printf("goodbye\n"); Unfortunately variables are scoped to the function that wraps them, so they don't persist across multiple entries. However, you can access the last result using the "last" variable (a double). [Here's the source file](, and here's a syntax highlighted version: It's been tested on Mac OS X (Leopard) and Linux (Ubuntu Gutsy), with GCC 4. Compile with "gcc -o gccalc gccalc.c" on OS X, or "gcc -o gccalc gccalc.c -ldl" on Linux.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <unistd.h>

#ifdef __ELF__
#define GCC_FLAGS "-fPIC -shared"
#define EXTENSION "so"
#define GCC_FLAGS "-dynamiclib"
#define EXTENSION "dylib"

#define HEADERS "#include <stdio.h>\n#include<math.h>"

typedef double(func_return_double)(double);

unsigned count = 0;
char *cwd;
char tmp_path[1024] = {‘\0’};

void *lib = NULL;

int main(int argc, char **argv)
    double result = 0.0;
    char input_buffer[1024], code_buffer[2048], function_name[32], command_buffer[1024];
    // get out current directory, which we’ll use for tmp files (dlopen seems to need absolute paths)
    cwd = getcwd(NULL, 0);
    while (1)
        // for unique function and file names (needed for dlopen/dlsym to work correctly)
        // read in the next line
        printf(">> ");
        fgets(input_buffer, sizeof(input_buffer), stdin);
        // format the function name
        sprintf(function_name, "f%d", count);
        // format the code string: if it doesn’t contain a semicolon, assume it is just an expression
        if (strchr(input_buffer, ‘;’))
            sprintf(code_buffer, "%s\ndouble %s(double last) { %s\nreturn 0; }", HEADERS, function_name, input_buffer);
            sprintf(code_buffer, "%s\ndouble %s(double last) { return (%s); }", HEADERS, function_name, input_buffer);
        // format the filename string, delete the file if it exists
        sprintf(tmp_path, "%s/libtmp%d.%s", cwd, count, EXTENSION);
        // format the gcc command string
        sprintf(command_buffer, "gcc -Wall %s -x c – -o %s", GCC_FLAGS, tmp_path);
        // execute gcc command, write out the code
        FILE *fp = popen(command_buffer, "w");
        fwrite(code_buffer, 1, strlen(code_buffer), fp);
        fprintf(fp, "\n");
        // pclose waits for gcc to terminate (fclose/close do NOT thus compilation will sometimes not finish prior to the dlopen)

        void *ptr = NULL;
        // open the just-compiled dynamic library
        if ((lib = dlopen(tmp_path, RTLD_NOW|RTLD_LOCAL)) == NULL) {
        // get the function pointer
        else if ((ptr = dlsym(lib, function_name)) == NULL) {
        // execute it
        if (ptr != NULL)
            func_return_double *func = (func_return_double*)ptr;
            result = (*func)(result);
            // print the result
            printf("=> %.*lf\n", (result/((int)result)>1.0)?5:0, result);

        // clean up: close the library, delete the temp file

    return 0;

Thanks to jbert on Reddit for the initial code and inspiration.

If only I had known about this back when The Daily WTF has having their [OMG WTF]( crazy calculator programming contest…