added some docs
This commit is contained in:
+18
-9
@@ -341,6 +341,13 @@ r = multiply(p, q);
|
||||
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
|
||||
function body can access are the ones provided as arguments. Usually in
|
||||
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.
|
||||
|
||||
```py
|
||||
# Overwrite the print variable inside the global scope..
|
||||
# Overwrite the print variable inside the global
|
||||
# scope..
|
||||
print = 5;
|
||||
|
||||
# The reference to the print function is lost
|
||||
# withing this scope.
|
||||
|
||||
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');
|
||||
|
||||
# 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();
|
||||
|
||||
# Now that i think about it, we lost the reference to the print function
|
||||
# inside this scope. But we can take it back by returning it from a
|
||||
# function!
|
||||
# We can take the reference to the print function
|
||||
# by taking it from a function!
|
||||
|
||||
fun get_print_back()
|
||||
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 <setjmp.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 <string.h>
|
||||
#include <assert.h>
|
||||
@@ -81,21 +107,36 @@ static inline _Bool isoper(char 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);
|
||||
int len = Source_GetSize(src);
|
||||
assert(str != NULL);
|
||||
assert(len >= 0);
|
||||
|
||||
AST *ast = BPAlloc_Malloc(alloc, sizeof(AST));
|
||||
|
||||
if(ast == NULL)
|
||||
return NULL;
|
||||
|
||||
Token *head = NULL,
|
||||
*tail = NULL;
|
||||
int i = 0;
|
||||
@@ -343,9 +384,58 @@ AST *parse(Source *src, BPAlloc *alloc, Error *error)
|
||||
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;
|
||||
ctx.src = str;
|
||||
ctx.token = head;
|
||||
ctx.src = Source_GetBody(src);
|
||||
ctx.token = tokens;
|
||||
ctx.alloc = alloc;
|
||||
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 <xjson.h>
|
||||
#include "serialize.h"
|
||||
|
||||
Reference in New Issue
Block a user