minor adjustments to the docs
This commit is contained in:
@@ -18,37 +18,56 @@ Noja is a very high level and dynamic language. It operates in the same range of
|
|||||||
This project aims at being an interpreter design reference, therefore it optimizes for code quality and readability. That's not to mean that it won't be feature-complete. The end goal is to have a language you can do arbitrarily complex things in.
|
This project aims at being an interpreter design reference, therefore it optimizes for code quality and readability. That's not to mean that it won't be feature-complete. The end goal is to have a language you can do arbitrarily complex things in.
|
||||||
|
|
||||||
## Show me the code!
|
## Show me the code!
|
||||||
Here's an example of a noja program that orders the items in list using a bubble sort
|
Here's an example of a noja program taken from the example HTTP server in `examples/http_server/main.noja`
|
||||||
```
|
```
|
||||||
L = [3, 2, 1];
|
router->plug("/login", {
|
||||||
|
GET: 'pages/login.html',
|
||||||
|
fun POST(req: Request) {
|
||||||
|
|
||||||
fun swap(a: int, b: int)
|
username, _, error = getUsername(req);
|
||||||
return b, a;
|
if error != none:
|
||||||
|
return respond(400, error);
|
||||||
|
if username != none:
|
||||||
|
return respond(400, "You're already logged in");
|
||||||
|
|
||||||
fun order(L: List) {
|
fun areValid(params) {
|
||||||
|
expect = {username: String, password: String};
|
||||||
do {
|
return istypeof(expect, params)
|
||||||
swapped = false;
|
and count(params.username) > 0
|
||||||
|
and count(params.password) > 0;
|
||||||
i = 0;
|
|
||||||
while i < count(L)-1 and not swapped: {
|
|
||||||
|
|
||||||
swapped = L[i+1] < L[i];
|
|
||||||
if swapped:
|
|
||||||
L[i], L[i+1] = swap(L[i], L[i+1]);
|
|
||||||
|
|
||||||
i = i+1;
|
|
||||||
}
|
}
|
||||||
} while swapped;
|
|
||||||
}
|
|
||||||
|
|
||||||
print(L, '\n'); # [3, 2, 1]
|
params = urlencoded.parse(req.body);
|
||||||
order(L);
|
if not areValid(params):
|
||||||
print(L, '\n'); # [1, 2, 3]
|
return respond(400, "Invalid parameters");
|
||||||
|
username = params.username;
|
||||||
|
password = params.password;
|
||||||
|
|
||||||
|
info = user_table[username];
|
||||||
|
|
||||||
|
if info == none:
|
||||||
|
return respond(200, "No such user");
|
||||||
|
if info != password:
|
||||||
|
return respond(200, "Wrong password");
|
||||||
|
|
||||||
|
# Log-in succeded
|
||||||
|
|
||||||
|
# Create a new session
|
||||||
|
session_id = random.generate(0, 1000);
|
||||||
|
session_table[session_id] = username;
|
||||||
|
|
||||||
|
message = string.cat("Welcome, ", username, "!");
|
||||||
|
headers = {
|
||||||
|
"Location" : "/home",
|
||||||
|
"Set-Cookie": string.cat("SESSID=", toString(session_id))
|
||||||
|
};
|
||||||
|
return respond(303, message, headers);
|
||||||
|
}
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## Implementation Overview
|
## Implementation Overview
|
||||||
The architecture is pretty much the same as CPython. The source code is executed by compilig it to bytecode. The bytecode is much more high level than what the CPU understands, it's more like a serialized version of the AST. For example, some bytecode instructions refer to variables by names, which means the compiler does very little static analisys. Memory is managed by a garbage collector that moves and compacts allocations.
|
The architecture is pretty much the same as CPython. The source code is executed by compilig it to bytecode. The bytecode is much more high level than what the CPU understands, it's more like a serialized version of the AST. For example, some bytecode instructions refer to variables by names, which means the compiler does very little static analisys. Memory is managed by a garbage collector that moves and compacts allocations.
|
||||||
|
|
||||||
(More detailed explanations are provided alongside the code.)
|
(More detailed explanations are provided alongside the code.)
|
||||||
|
|
||||||
@@ -56,7 +75,7 @@ The architecture is pretty much the same as CPython. The source code is executed
|
|||||||
I wrote it on a linux machine, but there should be very few places where a linux host is assumed. It should be very easy to port.
|
I wrote it on a linux machine, but there should be very few places where a linux host is assumed. It should be very easy to port.
|
||||||
|
|
||||||
## Development state
|
## Development state
|
||||||
The interpreter is fully functional, but lots of built-in functions that one would expect still need to be implemented. Unfortunately, I feel like, at the moment, this requires much more work than what it's worth. At this time the priority is writing documentation and tests so that more people can try it, give feedback and move forward without breaking the world.
|
The interpreter is fully functional, but many built-in functions that one would expect still need to be implemented. At this time the priority is writing documentation and tests so that more people can try it, give feedback and move forward without breaking the world!
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
To build the interpreter, run:
|
To build the interpreter, run:
|
||||||
@@ -65,20 +84,32 @@ $ make
|
|||||||
```
|
```
|
||||||
The `noja` executable will be generated, which is a command-line interface that runs Noja code.
|
The `noja` executable will be generated, which is a command-line interface that runs Noja code.
|
||||||
|
|
||||||
## Usage
|
By default, the compilation is in release mode. The mode can be specified through the `BUILD_MODE` variable
|
||||||
You can run files by doing:
|
|
||||||
```sh
|
```sh
|
||||||
location/of/noja <filename>
|
$ make -B BUILD_MODE=DEBUG
|
||||||
|
```
|
||||||
|
the `-B` option forces the makefile to rebuild all of the code, ignoring previous compilation artifacts. This is probably what you want when changing the build mode.
|
||||||
|
|
||||||
|
Also, a mode for analyzing code coverage is available if your interested in that kind of stuff
|
||||||
|
```sh
|
||||||
|
$ make -B BUILD_MODE=COVERAGE
|
||||||
```
|
```
|
||||||
|
|
||||||
or you can run strings by doing:
|
## Usage
|
||||||
|
You can run files by providing a path to the command line utility, or you can simply provide the string to be executed
|
||||||
```sh
|
```sh
|
||||||
location/of/noja -i <string>
|
$ noja <filename>
|
||||||
|
$ noja -i <string>
|
||||||
|
```
|
||||||
|
|
||||||
|
More usage information can be accessed using the `-h` option
|
||||||
|
```sh
|
||||||
|
$ noja -h
|
||||||
```
|
```
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
Running `make` will also generate the `test` executable, which is a program that lets you run the testcases in the `tests/` folder. A testcase is a text file with extension `.noja-test`.
|
Running `make` will also generate the `test` executable, a program necessary to run the testcases in the `tests/` folder. A testcase is a text file with extension `.noja-test`.
|
||||||
|
|
||||||
Tu run all tests, you can do:
|
Tu run all tests, you can do:
|
||||||
```sh
|
```sh
|
||||||
@@ -88,3 +119,5 @@ or you can execute specific suites of tests like this:
|
|||||||
```sh
|
```sh
|
||||||
$ ./test tests/compiler/expr tests/runtime/push
|
$ ./test tests/compiler/expr tests/runtime/push
|
||||||
```
|
```
|
||||||
|
|
||||||
|
By running `report_test_suite_coverage.sh`, the test suite will be executed and a report of the code coverage will be stored in the `/report` folder. Note that this script will compile the CLI in coverage mode, so you'll need to rebuild it again for generic use cases.
|
||||||
|
|||||||
+1
-1
@@ -22,7 +22,7 @@ Both integers and floats (floating point values) are signed and represented usin
|
|||||||
* multiplication `*`
|
* multiplication `*`
|
||||||
* division `/`
|
* division `/`
|
||||||
* modulo `%`
|
* modulo `%`
|
||||||
|
|
||||||
Here "modulo" refers to the remainder of the division. These operations mainly behave like one would expect and have the following type conversion rules:
|
Here "modulo" refers to the remainder of the division. These operations mainly behave like one would expect and have the following type conversion rules:
|
||||||
1. Operations involving integers evaluate to integers, except division. The result of a division is always a float.
|
1. Operations involving integers evaluate to integers, except division. The result of a division is always a float.
|
||||||
1. If an arithmetic operation involves a float, the result is also float.
|
1. If an arithmetic operation involves a float, the result is also float.
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ CC = gcc
|
|||||||
AR = ar
|
AR = ar
|
||||||
|
|
||||||
# Program flags
|
# Program flags
|
||||||
CFLAGS = -Wall -Wextra -fsanitize=undefined
|
CFLAGS = -Wall -Wextra
|
||||||
LFLAGS = -lm -fsanitize=undefined
|
LFLAGS = -lm
|
||||||
|
|
||||||
# Build the library with valgrind support.
|
# Build the library with valgrind support.
|
||||||
# Can be one of: YES, NO
|
# Can be one of: YES, NO
|
||||||
|
|||||||
+19
-31
@@ -39,27 +39,27 @@
|
|||||||
static void usage(FILE *stream, const char *name)
|
static void usage(FILE *stream, const char *name)
|
||||||
{
|
{
|
||||||
fprintf(stream,
|
fprintf(stream,
|
||||||
"USAGE\n"
|
"\n"
|
||||||
" $ %s [-h | -o <file> | -p | -H <heap size> | {-d | -a}] [--] <file>\n", name);
|
"Usage:\n"
|
||||||
|
" $ %s [-h | -o <file> | -p | -H <heap size> | {-d | -a}] <file>\n", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void help(FILE *stream, const char *name)
|
static void help(FILE *stream, const char *name)
|
||||||
{
|
{
|
||||||
usage(stream, name);
|
usage(stream, name);
|
||||||
fprintf(stream,
|
fprintf(stream,
|
||||||
"OPTIONS\n"
|
"\n"
|
||||||
" -h, --help Show this message\n"
|
"Options:\n"
|
||||||
" -d, --disassembly Output the bytecode associated to noja code\n"
|
" -h, --help Show this message\n"
|
||||||
" -i, --inline Execute a string of code instead of a file\n"
|
" -i, --inline Execute a string of code instead of a file\n"
|
||||||
" -a, --assembly Specify that the source is bytecode and not noja code\n"
|
" -a, --assembly Specify that the source is bytecode and not noja code\n"
|
||||||
" -p, --profile Profile the execution of the source (can't be used with -d)\n"
|
" -p, --profile Profile the execution of the source (can't be used with -d)\n"
|
||||||
" -o, --output Specify the output file of -p or -d\n"
|
" -o, --output <file> Specify the output file of -p or -d\n"
|
||||||
" -H, --heap <size> Heap size\n"
|
" -H, --heap <size> Specify the heap size of the runtime\n"
|
||||||
"\n");
|
"\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
Mode_DISASSEMBLY,
|
|
||||||
Mode_ASSEMBLY,
|
Mode_ASSEMBLY,
|
||||||
Mode_DEFAULT,
|
Mode_DEFAULT,
|
||||||
Mode_HELP,
|
Mode_HELP,
|
||||||
@@ -89,10 +89,6 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
mode = Mode_HELP;
|
mode = Mode_HELP;
|
||||||
|
|
||||||
} else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--disassembly")) {
|
|
||||||
|
|
||||||
mode = Mode_DISASSEMBLY;
|
|
||||||
|
|
||||||
} else if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "--inline")) {
|
} else if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "--inline")) {
|
||||||
|
|
||||||
no_file = true;
|
no_file = true;
|
||||||
@@ -146,7 +142,8 @@ int main(int argc, char **argv)
|
|||||||
case Mode_ASSEMBLY:
|
case Mode_ASSEMBLY:
|
||||||
{
|
{
|
||||||
if (input == NULL) {
|
if (input == NULL) {
|
||||||
fprintf(stderr, "No input file");
|
fprintf(stderr, "Error: No input file\n");
|
||||||
|
usage(stderr, argv[0]);
|
||||||
code = -1;
|
code = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -157,7 +154,7 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
runtime = Runtime_New(config);
|
runtime = Runtime_New(config);
|
||||||
if (runtime == NULL) {
|
if (runtime == NULL) {
|
||||||
fprintf(stderr, "Failed to initialize runtime");
|
fprintf(stderr, "Error: Failed to initialize runtime\n");
|
||||||
code = -1;
|
code = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -167,7 +164,7 @@ int main(int argc, char **argv)
|
|||||||
Error error;
|
Error error;
|
||||||
Error_Init(&error);
|
Error_Init(&error);
|
||||||
|
|
||||||
if (!Runtime_plugDefaultBuiltins(runtime, (Error*) &error)) {
|
if (!Runtime_plugDefaultBuiltins(runtime, &error)) {
|
||||||
Error_Print(&error, ErrorType_RUNTIME, stderr);
|
Error_Print(&error, ErrorType_RUNTIME, stderr);
|
||||||
Error_Free(&error);
|
Error_Free(&error);
|
||||||
Runtime_PrintStackTrace(runtime, stderr);
|
Runtime_PrintStackTrace(runtime, stderr);
|
||||||
@@ -179,14 +176,14 @@ int main(int argc, char **argv)
|
|||||||
bool ok;
|
bool ok;
|
||||||
if (mode == Mode_ASSEMBLY) {
|
if (mode == Mode_ASSEMBLY) {
|
||||||
if (no_file)
|
if (no_file)
|
||||||
ok = runBytecodeString(runtime, input, (Error*) &error);
|
ok = runBytecodeString(runtime, input, &error);
|
||||||
else
|
else
|
||||||
ok = runBytecodeFile(runtime, input, (Error*) &error);
|
ok = runBytecodeFile(runtime, input, &error);
|
||||||
} else {
|
} else {
|
||||||
if (no_file)
|
if (no_file)
|
||||||
ok = runString(runtime, input, (Error*) &error);
|
ok = runString(runtime, input, &error);
|
||||||
else
|
else
|
||||||
ok = runFile(runtime, input, (Error*) &error);
|
ok = runFile(runtime, input, &error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ok == false) {
|
if (ok == false) {
|
||||||
@@ -206,15 +203,6 @@ int main(int argc, char **argv)
|
|||||||
Runtime_Free(runtime);
|
Runtime_Free(runtime);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Mode_DISASSEMBLY:
|
|
||||||
if (input == NULL) {
|
|
||||||
fprintf(stderr, "No disassembly input file");
|
|
||||||
code = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* .. */
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return code;
|
return code;
|
||||||
|
|||||||
Reference in New Issue
Block a user