fixed bug and added a little documentation

This commit is contained in:
Francesco Cozzuto
2021-11-05 00:10:45 +01:00
parent b224f57361
commit 9458632b27
8 changed files with 298 additions and 20 deletions
+72
View File
@@ -0,0 +1,72 @@
# ------------------------------------------------------------------------- #
# --- Introduction -------------------------------------------------------- #
#
# This language was written as a personal study of how interpreters
# and compilers work. For this reason, the language is very basic and
# not innovative.
# One of the main inspirations was the CPython's source code since
# it's extremely readable and has a very simple clean architecture.
#
# This file was intended for people who already program in other
# high level languages (such as Python, Javascript, Ruby) and don't
# need to be introduced to basic programming concepts (variables,
# expressions and branches). This way, there is more space for the
# comparison of the language's features with the mainstream languages.
#
# ------------------------------------------------------------------------- #
# --- Implementation ------------------------------------------------------ #
#
# The interpreter works by compiling the provided source to a bytecode
# format and executing it. The bytecode is very high level since it
# does things like:
#
# - explicitly referring to variables by name.
#
# - treating values as atomic things: from the perspective of the
# bytecode, a list and an integer occupy the same space on the
# stack, which is 1.
#
# - referring to instructions by their index.
#
# For example, by compiling the following snippet
define = true;
if define:
a = 33;
print(a, '\n');
# one would obtain the following bytecode:
#
# 0: PUSHTRU
# 1: ASS [define]
# 2: POP 1
# 3: PUSHVAR [define]
# 4: JUMPIFNOTANDPOP 8
# 5: PUSHINT 33
# 6: ASS [a]
# 7: POP 1
# 8: PUSHSTR [\n]
# 9: PUSHVAR [a]
# 10: PUSHVAR [print]
# 11: CALL 2
# 12: POP 1
# 13: RETURN
#
# as you can see, there are instructions like ASS and PUSHVAR that
# assign to and read from variables by specifying names, and jumps
# that refer to other points of the "executable" by specifying indices
# (like JUMPIFNOTANDPOP) instead of raw addresses.
#
# All values (objects) are allocated on a garbage-collected heap.
# For this reason all variables are simply references to these objects.
# The garbage collection algorithm is a copy-and-compact one. It
# behaves as a bump-pointer allocator until there is space left,
# and when space runs out, it creates a new heap, copies all of the
# alive object into it, calls the destructors of the dead objects
# and frees the old one.
#
# ------------------------------------------------------------------------- #
# ------------------------------------------------------------------------- #
+94
View File
@@ -0,0 +1,94 @@
# ------------------------------------------------------------------------- #
# --- The first program --------------------------------------------------- #
#
# The sintax is similar to Python's but is more C-like. A Noja script
# is a list of statements that can be:
#
# - function declaractions
# - expressions
# - if-else branches
# - while loops
# - do-while loops
# - return statements
# - composit statements
#
# The most basic yet interesting program is:
print('Hello, world!\n');
# as in other languages, this kind of statement is an expression.
# Expression statements require a ';' to determine their end.
#
# The print function can take any number of arguments of any type
# and doesn't add any spaces or newlines to the output.
print(1, 2, 3, '\n');
# ------------------------------------------------------------------------- #
# --- Variables and expressions ------------------------------------------- #
#
# You can set variables without declaring them first by using the
# assignment operator:
a = 5;
# which is similar to Python's assignment, but is a little different.
# In this language, assignments are considered as expressions, in fact
# you can do things like
a = (b = 1) + 1;
# The value resulting from an assignment is the assigned value.
# After this expression, b's value is 1 and a's value is 2.
print('b = ', b, '\n'); # b = 1
print('a = ', a, '\n'); # a = 2
# all of the basic arithmetic operators are available:
x = 1 + 1;
y = 1 - 2;
z = 3 * 2;
w = 10 / 3;
print('x = ', x, '\n'); # x = 2
print('y = ', y, '\n'); # y = -1
print('z = ', z, '\n'); # z = 6
print('w = ', w, '\n'); # w = 3
# Note how the division returns the rounded down version of the result.
# This is because the division was performed on integers. By making one
# of the operands a floating point value, also a floating point result
# is returned:
w = 10 / 3.0;
print('w = ', w, '\n');
# Arithmetic operators are only available for numeric types of objects.
# If you try to apply them on other kinds of types, you get a runtime
# error:
# (Uncomment the following line and run this file to get the error)
# p = 5 + 'hello';
# And relational operators are also available:
print(1 < 2, '\n'); # true
print(1 > 2, '\n'); # false
print(1 >= 0, '\n'); # true
print(1 <= 0, '\n'); # false
print(1 == 5, '\n'); # false
print(6 == 6, '\n'); # true
print(1 != 5, '\n'); # true
print(6 != 6, '\n'); # false
# The equal and not equal operators are available on every type of object,
# while the others are only available for numeric types.
#
# ------------------------------------------------------------------------- #
# ------------------------------------------------------------------------- #
+38
View File
@@ -0,0 +1,38 @@
# ------------------------------------------------------------------------- #
# --- Branches ------------------------------------------------------------ #
#
# It's possible to make the execution of a statement optional, based on the
# result of an expression. Like in other languages, you do this using if-else
# statements:
if 1 < 2:
print('Took the branch!\n'); # This is executed!
if 1 > 2:
print('Didn\'t take the branch\n'); # This isn't!
# or you can specify an alternative branch, which is executed when the
# condition isn't true:
if 1 > 2:
print('Not executed..\n');
else
print('Executed!\n');
# You can have multiple statements inside a branch by having them inside a
# compound statement. Compound statements are statement lists wrapped inside
# curly brackets, like this:
{ print('Hello from a '); print('compound statement!\n'); }
# This way they count as one statement.
if 1 == 1:
{
print('Executed\n');
print('Also executed\n');
}
#
# ------------------------------------------------------------------------- #
# ------------------------------------------------------------------------- #
+38
View File
@@ -0,0 +1,38 @@
# ------------------------------------------------------------------------- #
# --- Loops --------------------------------------------------------------- #
#
# Looping constructs are available in the form of while and do-while
# statements. The while statement checks the condition before each
# iteration:
i = 0;
while i < 10:
i = i + 1;
# This loop runs for 10 times. As for the if-else statement, a single
# statement is expected as the body of the while statement. You can
# provide it a compound statement tho.
i = 0;
while i < 10:
{
print('While iteration no. ', i, '\n');
i = i + 1;
}
# The do-while statement checks the condition at the end of each
# iteration. This means that at least one iteration is performed!
i = 0;
do
{
print('Do-while iteration no. ', i, '\n');
i = i + 1;
}
while i < 10;
#
# ------------------------------------------------------------------------- #
# ------------------------------------------------------------------------- #
+19 -1
View File
@@ -1,5 +1,23 @@
if 'hello' == 'hello': a = 'Francesco';
b = 'Gennaro';
print('Are ', a, ' and ', b, ' the same name?\n');
if a == b:
print('Yup!\n');
else
print('Nop!\n');
# Now they are!
a = 'Francesco';
b = 'Francesco';
print('Are ', a, ' and ', b, ' the same name?\n');
if a == b:
print('Yup!\n'); print('Yup!\n');
else else
print('Nop!\n'); print('Nop!\n');
+3 -1
View File
@@ -1,5 +1,7 @@
if false: define = true;
if define:
a = 33; a = 33;
print(a, '\n'); print(a, '\n');
+15 -14
View File
@@ -1,25 +1,26 @@
p = true; # ------------------------------------- #
# -- While loop ----------------------- #
while p: i = 0;
while i < 3:
{ {
print('Hello!\n'); print('Hello from the while loop!\n');
p = false; i = i + 1;
} }
# ------------------------------------- # # ------------------------------------- #
# -- Do-Wile loop --------------------- #
i = 0;
do do
{ {
print('Hello 2!\n'); print('Hello from the do-while loop!\n');
}
while false;
# ------------------------------------- #
i = 0;
while i < 10:
{
print('i = ', i+1, '\n');
i = i + 1; i = i + 1;
} }
while i < 3;
# ------------------------------------- #
# ------------------------------------- #
+17 -2
View File
@@ -209,8 +209,20 @@ AST *parse(Source *src, BPAlloc *alloc, Error *error)
i += 1; // Skip the starting quote. i += 1; // Skip the starting quote.
while(i < len && str[i] != f) while(1)
i += 1; {
while(i < len && str[i] != '\\' && str[i] != f)
i += 1;
if(str[i] == '\\')
{
i += 1; // Consume the \.
if(i < len && (str[i] == '\'' || str[i] == '"'))
i += 1;
}
else break;
}
if(i == len) if(i == len)
{ {
@@ -593,9 +605,12 @@ static Node *parse_string_primary_expression(Context *ctx)
{ {
switch(src[i]) switch(src[i])
{ {
case '"': temp[temp_used++] = '"'; break;
case 'n': temp[temp_used++] = '\n'; break; case 'n': temp[temp_used++] = '\n'; break;
case 't': temp[temp_used++] = '\t'; break; case 't': temp[temp_used++] = '\t'; break;
case 'r': temp[temp_used++] = '\r'; break; case 'r': temp[temp_used++] = '\r'; break;
case '\\': temp[temp_used++] = '\\'; break;
case '\'': temp[temp_used++] = '\''; break;
default: default:
Error_Report(ctx->error, 0, "Invalid escape sequence \\%c", src[i]); Error_Report(ctx->error, 0, "Invalid escape sequence \\%c", src[i]);