Create an executable for a program in an interpreted language: Difference between revisions

Content added Content deleted
(→‎{{header|Wren}}: Now embeds the Wren source code in the C program itself.)
Line 351: Line 351:


=={{header|Wren}}==
=={{header|Wren}}==

{{improve|Wren|The task was intended to be to create an executable that would run one specific program (like you would get by compiling a program in a compiled language), not a general interpreter.<br>Sorry it was a bit confusing, I hope the revisions (and name change) make it a bit clearer.<br>Hopefully, the changes required won't be large... The Wren VM sounds pretty impressive, BTW}}


Wren source code is always compiled first into an intermediate bytecode using a single-pass compiler which is part of its virtual machine (VM). The VM then interprets the bytecode at runtime with respect to the underlying platform which can be Linux, MacOS, Windows or (in theory at least) anything else for which a standard C99 compiler is available.
Wren source code is always compiled first into an intermediate bytecode using a single-pass compiler which is part of its virtual machine (VM). The VM then interprets the bytecode at runtime with respect to the underlying platform which can be Linux, MacOS, Windows or (in theory at least) anything else for which a standard C99 compiler is available.
Line 360: Line 358:
Wren is designed for embedding and, technically, even ''Wren-cli'' - which enables Wren scripts to be run from the command line - is just a host application for the Wren VM which uses the cross-platform library, ''libuv'', to provide additional functionality (mainly I/O) not provided by Wren's standard library itself.
Wren is designed for embedding and, technically, even ''Wren-cli'' - which enables Wren scripts to be run from the command line - is just a host application for the Wren VM which uses the cross-platform library, ''libuv'', to provide additional functionality (mainly I/O) not provided by Wren's standard library itself.


If we don't need this additional functionality, we can instead write a simple C host, link it to the Wren VM library and then use the latter to compile and run a Wren source code file. In fact, the VM is so small that its source could be embedded directly into the C host as could the Wren source itself but, in the interests of tidiness and versatility, we won't do that here.
If we don't need this additional functionality, we can instead write a simple C host, link it to the Wren VM library and then use the latter to compile and run some Wren source code which we can embed in the C program itself. In fact, the VM is so small that its source could also be embedded directly into the C host though we won't do that here.


So this is perhaps the nearest we can get to the spirit of this task. The following C program (''wrenc.c'') could be used:
So the following C program (countdown.c) is perhaps the nearest we can get to the spirit of this task.


<lang C>#include <stdio.h>
<lang C>/* gcc -O3 wrenc.c -o wrenc -lwren -lm */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "wren.h"
#include "wren.h"


static void writeFn(WrenVM* vm, const char* text) {
static void writeFn(WrenVM* vm, const char* text) {
printf("%s", text);
printf("%s", text);
}

char *readFile(const char *fileName) {
FILE *f = fopen(fileName, "r");
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
rewind(f);
char *script = malloc(fsize + 1);
size_t ret = fread(script, 1, fsize, f);
if (!ret) perror("Error reading wren source file.");
fclose(f);
script[fsize] = 0;
return script;
}

static void loadModuleComplete(WrenVM* vm, const char* module, WrenLoadModuleResult result) {
if( result.source) free((void*)result.source);
}

WrenLoadModuleResult loadModule(WrenVM* vm, const char* name) {
WrenLoadModuleResult result = {0};
if (strcmp(name, "random") != 0 && strcmp(name, "meta") != 0) {
result.onComplete = loadModuleComplete;
char fullName[strlen(name) + 6];
strcpy(fullName, name);
strcat(fullName, ".wren");
result.source = readFile(fullName);
}
return result;
}
}


int main(int argc, char **argv) {
int main(int argc, char **argv) {
if (argc != 2) {
printf("Please pass the name of the Wren file to be executed.\n");
return 1;
}
WrenConfiguration config;
WrenConfiguration config;
wrenInitConfiguration(&config);
wrenInitConfiguration(&config);
config.writeFn = &writeFn;
config.writeFn = &writeFn;
config.loadModuleFn = &loadModule;
WrenVM* vm = wrenNewVM(&config);
WrenVM* vm = wrenNewVM(&config);
const char* module = "main";
const char* module = "main";
char *script = "for (i in 5..0) System.print(i)"; /* Wren source code */
const char* fileName = argv[1];
char *script = readFile(fileName);
WrenInterpretResult result = wrenInterpret(vm, module, script);
WrenInterpretResult result = wrenInterpret(vm, module, script);
wrenFreeVM(vm);
wrenFreeVM(vm);
free(script);
return 0;
return 0;
}</lang>
}</lang>


We can now compile this code (using GCC on Linux) and run it, obtaining the expected output as follows:
If we now create a Wren source code file: ''countdown.wren'':
<lang ecmascript>for (i in 5..0) System.print(i)</lang>

we can compile and run it and obtain the expected output (on Linux) as follows:
{{out}}
{{out}}
<pre>
<pre>
$./wrenc countdown.wren
$ gcc countdown.c -o countdown -lwren -lm
$./countdown
5
5
4
4