a bunch of grammar fixes
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@ Noja is a high level programming language implemented as a learning exercise. St
|
||||
The use-cases of Noja are the same as Python since their abstraction level is comparable. The syntax is more similar to the C-family of languages though (curly brackets to denote scope).
|
||||
|
||||
## A Noja program
|
||||
A Noja program is a sequence of statements separated by semi-colons (with some exceptions). The statements can be o various kinds:
|
||||
A Noja program is a sequence of statements separated by semi-colons (with some exceptions). The statements can be of various kinds:
|
||||
* expressions
|
||||
* function definitions
|
||||
* if-else branches
|
||||
|
||||
@@ -128,12 +128,12 @@ Lists are defined as a comma-separated list of expressions between square bracke
|
||||
[none, 2 + 1, true];
|
||||
```
|
||||
|
||||
The `n`-th list can be accessed using the `[]` notation:
|
||||
The `n`-th list element can be accessed using the `[]` notation:
|
||||
```py
|
||||
ls = [true, false, none];
|
||||
ls[1]; # false
|
||||
```
|
||||
where the list value is followed by `[..]` which contain the index of the item to retrieve.
|
||||
where the list value is followed by `[..]` which contains the index of the item to retrieve.
|
||||
|
||||
The index may be evaluated dynamically, but it will trigger a runtime error if it doesn't evaluate to an integer value
|
||||
```py
|
||||
@@ -142,7 +142,7 @@ ls[0 + 2]; # OK!
|
||||
ls[true or false]; # Runtime error!!
|
||||
```
|
||||
|
||||
To append a value to a list, increasing it's size, you can just insert the new value at the first unused position (which will have index equal to the current list size). As we'll see in the built-in chapter, to get the size of a list one can use the `count` function. Which means appending to a list can be done like this:
|
||||
To append a value to a list, increasing its size, you can just insert the new value at the first unused position (which will have index equal to the current list size). As we'll see in the built-in chapter, to get the size of a list one can use the `count` function. Which means appending to a list can be done like this:
|
||||
```py
|
||||
ls = [1, 2, 3];
|
||||
ls[count(ls)] = 4;
|
||||
@@ -170,7 +170,7 @@ a = {"name": "Francesco", "age": 25};
|
||||
b = {name: "Francesco", age: 25};
|
||||
```
|
||||
|
||||
If you don't want to use the identifier as key but evaluate it as a variable to use it's value as key, then you can explicitly tell the interpreter that it's an expression
|
||||
If you don't want to use the identifier's text as key but evaluate it as a variable to use it's value as key, then you must explicitly tell the interpreter that it's an expression
|
||||
```py
|
||||
name = "x";
|
||||
|
||||
@@ -179,6 +179,7 @@ a = { "x": "Francesco", "age": 25};
|
||||
b = {(name): "Francesco", age: 25};
|
||||
c = { +name: "Francesco", age: 25};
|
||||
```
|
||||
Note that the `+` unary operator and the `(..)` are no-operations in this context.
|
||||
|
||||
Because of compound statements (discussed in the next chapter), an expression can't start with the `{` of a map literal because the interpreter will assume it's a compount statement.
|
||||
The following statement is invalid
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ Unlike expressions statements, they don't end with a `;`.
|
||||
|
||||
The condition may be any type of expression, but must evaluate to a boolean type. No implicit casts are performed.
|
||||
|
||||
When the `else` block is empty, in can me omitted:
|
||||
When the `else` block is empty, it can me omitted:
|
||||
```py
|
||||
if condition: {
|
||||
# Executed when the condition is true
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ while condition: {
|
||||
```
|
||||
as you can see, it doesn't need an ending `;`.
|
||||
|
||||
when the interpreter encounters a while statement, it evaluates the condition. If the condition is true, it executes it's body. When the execution of the inner block of code is completed, the execution jumps back to the condition, evaluating it again. If it's again true, the inner code is executed again, else the execution jumps after the while statement. This mechanism can go on potentially for ever!
|
||||
When the interpreter encounters a while statement, it evaluates the condition. If the condition is true, it executes its body. When the execution of the inner block of code is completed, the execution jumps back to the condition, evaluating it again. If it's again true, the inner code is executed again, else the execution jumps after the while statement. This mechanism can go on potentially for ever!
|
||||
|
||||
Like for if-else statements, when the inner block only has one statement, you can drop the `{}`
|
||||
```py
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Functions
|
||||
Function definition statements always start with the `fun` keyword, followed by it's name, argument list and then body.
|
||||
Function definition statements always start with the `fun` keyword, followed by its name, argument list and then body.
|
||||
|
||||
Here are some examples of function definitions:
|
||||
```py
|
||||
@@ -46,7 +46,7 @@ abc = 10;
|
||||
```
|
||||
|
||||
## Functions calls and return values
|
||||
To execute a function in an expression, it's name must be followed by an argument list enclosed in brackets `(..)`. All functions return at least one value, which may be `none` when a function has no useful value to be returned.
|
||||
To execute a function in an expression, its name must be followed by an argument list enclosed in brackets `(..)`. All functions return at least one value, which may be `none` when a function has no useful value to be returned.
|
||||
|
||||
To specify the return value of a function, one must use the `return` statement
|
||||
```py
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# This script implements a circular queue and
|
||||
# it's relative tests!
|
||||
# its relative tests!
|
||||
|
||||
fun newCircularQueue(T: Type = int, max: int = 3) {
|
||||
|
||||
|
||||
+21
-55
@@ -5,9 +5,10 @@ fun isSpace(c: String)
|
||||
or c == "\n";
|
||||
|
||||
fun isDigit(c: String) {
|
||||
u = unicode(c);
|
||||
d = u - unicode("0");
|
||||
return d >= 0 and d <= 9;
|
||||
d = string.ord(c)
|
||||
- string.ord("0");
|
||||
return d >= 0
|
||||
and d <= 9;
|
||||
}
|
||||
|
||||
fun skipSpaces(context: Map) {
|
||||
@@ -68,7 +69,8 @@ Func = type(dummy);
|
||||
|
||||
fun skip(context: Map, test_callback: Func) {
|
||||
k = context.cur;
|
||||
while k < count(context.str) and test_callback(context.str[k]):
|
||||
while k < count(context.str)
|
||||
and test_callback(context.str[k]):
|
||||
k = k + 1;
|
||||
return k;
|
||||
}
|
||||
@@ -80,7 +82,7 @@ fun parseInteger(context: Map) {
|
||||
|
||||
do {
|
||||
c = context.str[context.cur];
|
||||
d = unicode(c) - unicode('0');
|
||||
d = string.ord(c) - string.ord('0');
|
||||
buffer = buffer * 10 + d;
|
||||
context.cur = context.cur + 1;
|
||||
} while context.cur < count(context.str)
|
||||
@@ -90,7 +92,8 @@ fun parseInteger(context: Map) {
|
||||
}
|
||||
|
||||
fun parseFloating(context: Map) {
|
||||
assert(context.cur < count(context.str) and isDigit(context.str[context.cur]));
|
||||
assert(context.cur < count(context.str)
|
||||
and isDigit(context.str[context.cur]));
|
||||
|
||||
# It's ensured by the caller (parseIntegerOrFloating)
|
||||
# that now the strings contains two sequences of
|
||||
@@ -101,7 +104,7 @@ fun parseFloating(context: Map) {
|
||||
# Scan the integer part.
|
||||
do {
|
||||
c = context.str[context.cur];
|
||||
d = unicode(c) - unicode('0');
|
||||
d = string.ord(c) - string.ord('0');
|
||||
buffer = buffer * 10 + d;
|
||||
context.cur = context.cur + 1;
|
||||
} while context.str[context.cur] != ".";
|
||||
@@ -112,18 +115,19 @@ fun parseFloating(context: Map) {
|
||||
q = 1;
|
||||
do {
|
||||
c = context.str[context.cur];
|
||||
d = unicode(c) - unicode('0');
|
||||
d = string.ord(c) - string.ord('0');
|
||||
q = q / 10;
|
||||
buffer = buffer + q * d;
|
||||
context.cur = context.cur + 1;
|
||||
} while context.cur < count(context.str)
|
||||
and isDigit(context);
|
||||
and isDigit(context.str[context.cur+1]);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
fun parseIntegerOrFloating(context: Map) {
|
||||
assert(context.cur < count(context.str) and isDigit(context.str[context.cur]));
|
||||
assert(context.cur < count(context.str)
|
||||
and isDigit(context.str[context.cur]));
|
||||
|
||||
# Is the first digit sequence followed by a dot
|
||||
# and then one or more digits?
|
||||
@@ -212,20 +216,13 @@ fun parseValue(context: Map) {
|
||||
return none, "Source ended where a value was expected";
|
||||
|
||||
c = context.str[context.cur];
|
||||
if c == "{":
|
||||
val, err = parseObject(context);
|
||||
else if c == "[":
|
||||
val, err = parseArray(context);
|
||||
else if c == '"':
|
||||
val, err = parseString(context);
|
||||
else if isDigit(c):
|
||||
val, err = parseIntegerOrFloating(context);
|
||||
else if c == 'n':
|
||||
val, err = parseNull(context);
|
||||
else if c == 't':
|
||||
val, err = parseTrue(context);
|
||||
else if c == 'f':
|
||||
val, err = parseFalse(context);
|
||||
if c == "{": val, err = parseObject(context);
|
||||
else if c == "[": val, err = parseArray(context);
|
||||
else if c == '"': val, err = parseString(context);
|
||||
else if isDigit(c): val, err = parseIntegerOrFloating(context);
|
||||
else if c == 'n': val, err = parseNull(context);
|
||||
else if c == 't': val, err = parseTrue(context);
|
||||
else if c == 'f': val, err = parseFalse(context);
|
||||
else {
|
||||
val = none;
|
||||
err = string.cat("Unexepected character \"", c, "\" where a value was expected");
|
||||
@@ -234,39 +231,8 @@ fun parseValue(context: Map) {
|
||||
}
|
||||
|
||||
fun parse(str: String) {
|
||||
|
||||
context = {str: str, cur: 0};
|
||||
skipSpaces(context);
|
||||
val, err = parseValue(context);
|
||||
return val, err;
|
||||
}
|
||||
|
||||
fun compareAny(A, B) {
|
||||
|
||||
fun compareLists(A: List, B: List) {
|
||||
n = count(A);
|
||||
if n != count(B):
|
||||
return flase;
|
||||
|
||||
i = 0;
|
||||
while i < n: {
|
||||
if not compareAny(A[i], B[i]):
|
||||
return false;
|
||||
i = i + 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
T = type(A);
|
||||
|
||||
if T != type(B):
|
||||
return false;
|
||||
|
||||
if T == List:
|
||||
return compareLists(A, B);
|
||||
|
||||
if T == Map:
|
||||
error("Maps aren't supported yet!");
|
||||
|
||||
return A == B;
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ CLI_SRCDIR = $(SRCDIR)/$(CLI_SUBDIR)
|
||||
TST_SRCDIR = $(SRCDIR)/$(TST_SUBDIR)
|
||||
|
||||
# Each program that is being build uses as object
|
||||
# cache a subfolder of OBJDIR called like the it's
|
||||
# cache a subfolder of OBJDIR called like the its
|
||||
# source tree base folder in SRCDIR.
|
||||
LIB_OBJDIR = $(OBJDIR)/$(LIB_SUBDIR)
|
||||
CLI_OBJDIR = $(OBJDIR)/$(CLI_SUBDIR)
|
||||
|
||||
@@ -145,7 +145,7 @@ static bool parseIntegerOperand(Context *ctx, Error *error, Operand *op)
|
||||
|
||||
long long int buffer = 0;
|
||||
do {
|
||||
// Transform each digit into it's integer value.
|
||||
// Transform each digit into its integer value.
|
||||
int d = ctx->str[ctx->cur] - '0';
|
||||
assert(d >= 0 && d <= 9);
|
||||
|
||||
@@ -179,7 +179,7 @@ static bool parseFloatingOperand(Context *ctx, Error *error, Operand *op)
|
||||
|
||||
double buffer = 0;
|
||||
do {
|
||||
// Transform each digit into it's integer value.
|
||||
// Transform each digit into its integer value.
|
||||
int d = ctx->str[ctx->cur] - '0';
|
||||
assert(d >= 0 && d <= 9);
|
||||
|
||||
@@ -196,7 +196,7 @@ static bool parseFloatingOperand(Context *ctx, Error *error, Operand *op)
|
||||
|
||||
double q = 1;
|
||||
do {
|
||||
// Transform each digit into it's integer value.
|
||||
// Transform each digit into its integer value.
|
||||
int d = ctx->str[ctx->cur] - '0';
|
||||
assert(d >= 0 && d <= 9);
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
Here are implemented the language's builtin variables. Each file defines it's values and exposes them through an array of `StaticMapSlot`s. These values can then be accessed from within the language by wrapping these arrays in static map objects (check src/o_staticmap.c to know more).
|
||||
Here are implemented the language's builtin variables. Each file defines its values and exposes them through an array of `StaticMapSlot`s. These values can then be accessed from within the language by wrapping these arrays in static map objects (check src/o_staticmap.c to know more).
|
||||
|
||||
The array of `StaticMapSlot`s defined by `basic.c` is the root of the builtin object tree. All other arrays are referred by it.
|
||||
@@ -17,7 +17,7 @@ Executable *compile(Source *src, Error *error, CompilationErrorType *errtyp)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// NOTE: The AST is stored in the BPAlloc. It's
|
||||
// NOTE: The AST is stored in the BPAlloc. Its
|
||||
// lifetime is the same as the pool.
|
||||
AST *ast = parse(src, alloc, error);
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
** | HOW ARE POINTERS UPDATED? |
|
||||
** | Basically, when an object is moved from the old to the new heap, the |
|
||||
** | location of the object in the old heap is overwritten with a placeholder |
|
||||
** | object that holds the new location. Then all of it's references are |
|
||||
** | object that holds the new location. Then all of its references are |
|
||||
** | iterated over and if they refer to placeholders they're updated with the |
|
||||
** | new location of the object. If the references don't refer to placeholder |
|
||||
** | objects, then the referred objects are moved too. This is a recursive |
|
||||
|
||||
@@ -99,7 +99,7 @@ Source *Source_FromFile(const char *file, Error *error)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Open the file and get it's size.
|
||||
// Open the file and get its size.
|
||||
// at the end of the block, the file
|
||||
// cursor will point at the start of
|
||||
// the file.
|
||||
|
||||
@@ -243,7 +243,7 @@ int utf8_sequence_to_utf32_codepoint(const char *utf8_data, int nbytes, uint32_t
|
||||
** value is equal to [nbytes].
|
||||
**
|
||||
** NOTE: You can check the validity of a UTF-8 string
|
||||
** by calling this function and checking that it's
|
||||
** by calling this function and checking that its
|
||||
** return value is not negative.
|
||||
*/
|
||||
int utf8_strlen(const char *utf8_data, int nbytes)
|
||||
|
||||
Reference in New Issue
Block a user