added some docs
This commit is contained in:
+18
-9
@@ -341,6 +341,13 @@ r = multiply(p, q);
|
|||||||
print(p, ' * ', q, ' = ', r, '\n');
|
print(p, ' * ', q, ' = ', r, '\n');
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If the function doesn't return any values, then the `none` value is returned.
|
||||||
|
As an example, the `print` function always returns `none`
|
||||||
|
|
||||||
|
```py
|
||||||
|
print(print()); # none
|
||||||
|
```
|
||||||
|
|
||||||
Functions are always "pure", in the sense that the only values that the
|
Functions are always "pure", in the sense that the only values that the
|
||||||
function body can access are the ones provided as arguments. Usually in
|
function body can access are the ones provided as arguments. Usually in
|
||||||
other languages, functions can access the global scope and the parent
|
other languages, functions can access the global scope and the parent
|
||||||
@@ -354,24 +361,26 @@ these variables but the effect only lasts for the lifetame of the
|
|||||||
context local to the assignment.
|
context local to the assignment.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
# Overwrite the print variable inside the global scope..
|
# Overwrite the print variable inside the global
|
||||||
|
# scope..
|
||||||
print = 5;
|
print = 5;
|
||||||
|
|
||||||
|
# The reference to the print function is lost
|
||||||
|
# withing this scope.
|
||||||
|
|
||||||
fun test()
|
fun test()
|
||||||
{
|
{
|
||||||
# Now call print from inside the function.
|
# If the previous assignment were to overwrite the
|
||||||
|
# print function globally, the next statement would
|
||||||
|
# fail because the value 5 isn't a function. But
|
||||||
|
# it doesn't fail!
|
||||||
print('Not overwritten here!\n');
|
print('Not overwritten here!\n');
|
||||||
|
|
||||||
# If the previous assignment were to overwrite the print function
|
|
||||||
# globally, the previous statement would fail because the value 5
|
|
||||||
# isn't a function.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test();
|
test();
|
||||||
|
|
||||||
# Now that i think about it, we lost the reference to the print function
|
# We can take the reference to the print function
|
||||||
# inside this scope. But we can take it back by returning it from a
|
# by taking it from a function!
|
||||||
# function!
|
|
||||||
|
|
||||||
fun get_print_back()
|
fun get_print_back()
|
||||||
return print;
|
return print;
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
|
|
||||||
|
/* -- WHAT IS THIS FILE? --
|
||||||
|
*
|
||||||
|
* This file implements the routines that transform the AST
|
||||||
|
* into a list of bytecodes. The functionalities of this file
|
||||||
|
* are exposed through the `compile` function, that takes as
|
||||||
|
* input an `AST` and outputs an `Executable`.
|
||||||
|
* The function that does the heavy lifting is `emit_instr_for_node`
|
||||||
|
* which walks the tree and writes instructions to the `ExeBuilder`.
|
||||||
|
* Some semantic errors are catched at this phase, in which
|
||||||
|
* case, they are reported by filling out the `error` structure
|
||||||
|
* and aborting.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|||||||
+101
-11
@@ -1,3 +1,29 @@
|
|||||||
|
|
||||||
|
/* -- WHAT IS THIS FILE? --
|
||||||
|
*
|
||||||
|
* This file implements the parser of the language, that transforms
|
||||||
|
* `Source` objects into `AST` objects. The functionalities of this
|
||||||
|
* file are exposed throigh the `parse` function.
|
||||||
|
*
|
||||||
|
* It's mainly composed by routines that can each parse specific
|
||||||
|
* parts of a noja source string. For example, `parse_expression`
|
||||||
|
* parses expressions and `parse_while_statement` parses while statements.
|
||||||
|
* These functions call each other recursively to parse the source
|
||||||
|
* and build the abstract syntax tree (AST) that can be then compiled
|
||||||
|
* into bytecode. If at any point the parsing fails because of an
|
||||||
|
* external or internal error, then the error is reported and the parsing
|
||||||
|
* is aborted.
|
||||||
|
* Since the nodes of the AST always have the same lifetime (they're
|
||||||
|
* allocated at the same time and die all together), the allocator
|
||||||
|
* scheme of choise is a bump-pointer allocator. This way each of the
|
||||||
|
* parsing routines can allocate memory if it need it but doesn't need
|
||||||
|
* to free it if an error occurres.
|
||||||
|
* The parsing routines don't operate directly on the source text, but
|
||||||
|
* on the tokenized version of it. Before parsing a linked list of
|
||||||
|
* tokens is produced through the `tokenize` function.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@@ -81,21 +107,36 @@ static inline _Bool isoper(char c)
|
|||||||
c == '=';
|
c == '=';
|
||||||
}
|
}
|
||||||
|
|
||||||
AST *parse(Source *src, BPAlloc *alloc, Error *error)
|
/* Symbol: tokenize
|
||||||
|
*
|
||||||
|
* Build a list of tokens that represents the
|
||||||
|
* provided source code.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
*
|
||||||
|
* src: The source code to be tokenized.
|
||||||
|
* alloc: The allocator that will contain all of the
|
||||||
|
* generated tokens.
|
||||||
|
* error: Error information structure that is filled out if
|
||||||
|
* an error occurres.
|
||||||
|
*
|
||||||
|
* None of the arguments are optional.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* A pointer to the first node of a linked list of tokens.
|
||||||
|
* If an error occurres, NULL is returned and the `error`
|
||||||
|
* structure is filled out.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static Token *tokenize(Source *src, BPAlloc *alloc, Error *error)
|
||||||
{
|
{
|
||||||
assert(src != NULL);
|
|
||||||
assert(alloc != NULL);
|
|
||||||
|
|
||||||
const char *str = Source_GetBody(src);
|
const char *str = Source_GetBody(src);
|
||||||
int len = Source_GetSize(src);
|
int len = Source_GetSize(src);
|
||||||
assert(str != NULL);
|
assert(str != NULL);
|
||||||
assert(len >= 0);
|
assert(len >= 0);
|
||||||
|
|
||||||
AST *ast = BPAlloc_Malloc(alloc, sizeof(AST));
|
|
||||||
|
|
||||||
if(ast == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
Token *head = NULL,
|
Token *head = NULL,
|
||||||
*tail = NULL;
|
*tail = NULL;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@@ -343,9 +384,58 @@ AST *parse(Source *src, BPAlloc *alloc, Error *error)
|
|||||||
tail = tok;
|
tail = tok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Symbol: parse
|
||||||
|
*
|
||||||
|
* Build an AST that represents the provided source code.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
*
|
||||||
|
* src: The source code to be parsed.
|
||||||
|
* alloc: The allocator that will contain all of the garbage
|
||||||
|
* the function needs and the final AST.
|
||||||
|
* error: Error information structure that is filled out if
|
||||||
|
* an error occurres.
|
||||||
|
*
|
||||||
|
* None of the arguments are optional.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
*
|
||||||
|
* A pointer to the generated AST object. The AST object and
|
||||||
|
* all of the stuff that's referenced by it will be stored
|
||||||
|
* onto the provided allocator, therefore the AST will have
|
||||||
|
* the same lifetime of the allocator. If an error occurres,
|
||||||
|
* NULL is returned and the `error` structure is filled out.
|
||||||
|
*
|
||||||
|
* Notes:
|
||||||
|
* The AST structure holds a weak reference to the source
|
||||||
|
* object, therefore it will be invalidated if the source
|
||||||
|
* is freed before the AST.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
AST *parse(Source *src, BPAlloc *alloc, Error *error)
|
||||||
|
{
|
||||||
|
assert(src != NULL);
|
||||||
|
assert(alloc != NULL);
|
||||||
|
assert(error != NULL);
|
||||||
|
|
||||||
|
AST *ast = BPAlloc_Malloc(alloc, sizeof(AST));
|
||||||
|
|
||||||
|
if(ast == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
Token *tokens = tokenize(src, alloc, error);
|
||||||
|
|
||||||
|
if(tokens == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
Context ctx;
|
Context ctx;
|
||||||
ctx.src = str;
|
ctx.src = Source_GetBody(src);
|
||||||
ctx.token = head;
|
ctx.token = tokens;
|
||||||
ctx.alloc = alloc;
|
ctx.alloc = alloc;
|
||||||
ctx.error = error;
|
ctx.error = error;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,20 @@
|
|||||||
|
|
||||||
|
/* -- WHAT IS THIS FILE? --
|
||||||
|
*
|
||||||
|
* This file implements the routines that serialize the AST
|
||||||
|
* into JSON format. The JSON manipulation is handled by the
|
||||||
|
* third party library xJSON (written by me, still).
|
||||||
|
* The serialization functionality is exposed through the
|
||||||
|
* `serialize` function, that takes as an `AST` as argument
|
||||||
|
* and outputs a string of valid JSON. Therefore the xJSON
|
||||||
|
* dependency isn't exposed to the caller and can be regarded
|
||||||
|
* as an implementation detail.
|
||||||
|
* The way the serialization occurres is by converting the
|
||||||
|
* AST's representation native to the compiler to one native
|
||||||
|
* to xJSON, an then calling xj_encode on the converted AST.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <xjson.h>
|
#include <xjson.h>
|
||||||
#include "serialize.h"
|
#include "serialize.h"
|
||||||
|
|||||||
Reference in New Issue
Block a user