added table of contents to docs

This commit is contained in:
cozis
2021-11-05 15:48:28 +01:00
parent 3fc918402e
commit 795dfbcb54
6 changed files with 21 additions and 400 deletions
+21 -14
View File
@@ -2,6 +2,13 @@
# The Noja language # The Noja language
## Table of contents ## Table of contents
1. [Introduction](#introduction)
2. [Implementation overview](#implementation-overview)
3. [The first program](#the-first-program)
4. [Expressions](#expressions)
5. [Branches](#branches)
6. [Loops](#loops)
7. [Functions](#functions)
## Introduction ## Introduction
@@ -31,6 +38,7 @@ does things like:
- referring to instructions by their index. - referring to instructions by their index.
For example, by compiling the following snippet For example, by compiling the following snippet
```py ```py
define = true; define = true;
@@ -39,7 +47,9 @@ if define:
print(a, '\n'); print(a, '\n');
``` ```
one would obtain the following bytecode: one would obtain the following bytecode:
``` ```
0: PUSHTRU 0: PUSHTRU
1: ASS "define" 1: ASS "define"
@@ -57,10 +67,11 @@ one would obtain the following bytecode:
13: RETURN 13: RETURN
``` ```
as you can see, there are instructions like ASS and PUSHVAR that
as you can see, there are instructions like `ASS` and `PUSHVAR` that
assign to and read from variables by specifying names, and jumps assign to and read from variables by specifying names, and jumps
that refer to other points of the "executable" by specifying indices that refer to other points of the "executable" by specifying indices
(like JUMPIFNOTANDPOP) instead of raw addresses. (like `JUMPIFNOTANDPOP`) instead of raw addresses.
All values (objects) are allocated on a garbage-collected heap. All values (objects) are allocated on a garbage-collected heap.
For this reason all variables are simply references to these objects. For this reason all variables are simply references to these objects.
@@ -84,19 +95,22 @@ is a list of statements that can be of multiple kinds:
- composit statements - composit statements
In general, unless it's inside strings, whitespace is ignored and In general, unless it's inside strings, whitespace is ignored and
comments start with the # character. comments start with the `#` character.
The most basic yet interesting program is: The most basic yet interesting program is:
```py ```py
print('Hello, world!\n'); print('Hello, world!\n');
``` ```
as in other languages, this kind of statement is an expression. as in other languages, this kind of statement is an expression.
Expression statements require a ';' to determine their end. Expression statements require a ';' to determine their end.
The print function can take any number of arguments of any type The print function can take any number of arguments of any type
and doesn't add any spaces or newlines to the output. and doesn't add any spaces or newlines to the output.
```py ```py
print(1, 2, 3, '\n'); print(1, 2, 3, true, '\n');
``` ```
## Expressions ## Expressions
@@ -160,12 +174,6 @@ print(6 != 6, '\n'); # false
The equal and not equal operators are available on every type of object, The equal and not equal operators are available on every type of object,
while the others are only available for numeric types. while the others are only available for numeric types.
### Booleans
TODO
### None
TODO
## Branches ## Branches
It's possible to make the execution of a statement optional, based on the It's possible to make the execution of a statement optional, based on the
@@ -265,12 +273,11 @@ body are shared with the parent's context.
Functions can be defined using the following syntax: Functions can be defined using the following syntax:
```py ```py
# Define it .. # Define it
fun say_hello_to(name) fun say_hello_to(name)
print('Hello, ', name, '!\n\n'); print('Hello, ', name, '!\n\n');
# .. call it. # .. and then call it.
say_hello_to('Francesco'); say_hello_to('Francesco');
``` ```
@@ -318,7 +325,7 @@ test_func = 5;
# The following line, if executed, returns an error because the test_func # The following line, if executed, returns an error because the test_func
# identifier is now associated to 5, which is not a function. # identifier is now associated to 5, which is not a function.
# test_func(); test_func(); # Error!!
``` ```
Functions can return values exactly like in other languages: Functions can return values exactly like in other languages:
-71
View File
@@ -1,71 +0,0 @@
# ------------------------------------------------------------------------- #
# --- Introduction -------------------------------------------------------- #
#
# This language was written as a personal study of how interpreters
# and compilers work. For this reason, the language is very basic.
# One of the main inspirations was the CPython's source code since
# it's extremely readable and has a very simple and 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.
#
# ------------------------------------------------------------------------- #
# ------------------------------------------------------------------------- #
-112
View File
@@ -1,112 +0,0 @@
# ------------------------------------------------------------------------- #
# --- 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 of multiple kinds:
#
# - function declaractions
# - expressions
# - if-else branches
# - while loops
# - do-while loops
# - return statements
# - composit statements
#
# In general, unless it's inside strings, whitespace is ignored and
# comments start with the # character.
#
# 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.
#
# ------------------------------------------------------------------------- #
# --- The boolean type ---------------------------------------------------- #
#
# TODO
#
# ------------------------------------------------------------------------- #
# --- The none value ------------------------------------------------------ #
#
# TODO
#
# ------------------------------------------------------------------------- #
# ------------------------------------------------------------------------- #
-50
View File
@@ -1,50 +0,0 @@
# ------------------------------------------------------------------------- #
# --- 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');
}
# Variables defined inside an if-else statement's branch are defined
# in the parent's context. This implies that variables may or may not
# be defined when you access them, based on which branch is taken.
a = 1;
if a < 2:
x = 100;
# Now x is defined, but if "a" were to be higher or equal to 2, it
# wouldn't be defined and the runtime would return an error.
#
# ------------------------------------------------------------------------- #
# ------------------------------------------------------------------------- #
-39
View File
@@ -1,39 +0,0 @@
# ------------------------------------------------------------------------- #
# --- 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;
# Like for if-else statements, variables defined inside the loop
# body are shared with the parent's context.
#
# ------------------------------------------------------------------------- #
# ------------------------------------------------------------------------- #
-114
View File
@@ -1,114 +0,0 @@
# ------------------------------------------------------------------------- #
# --- Functions ----------------------------------------------------------- #
# Functions can be defined using the following syntax:
fun say_hello_to(name)
print('Hello, ', name, '!\n\n');
# and now we can call it by doing
say_hello_to('Francesco');
# Functions can have an arbitrary amount of arguments. If the function is
# called with more arguments than it expected, the extra values are thrown
# away. If the function is called with less arguments than it expected,
# the argument set if filled up with none values.
fun test_func(a, b, c)
{
print('a = ', a, '\n');
print('b = ', b, '\n');
print('c = ', c, '\n\n');
}
test_func();
# a = none
# b = none
# c = none
test_func(1, 2);
# a = 1
# b = 2
# c = none
test_func(1, 2, 3);
# a = 1
# b = 2
# c = 3
test_func(1, 2, 3, 4);
# a = 1
# b = 2
# c = 3
# Functions are actually variables like the ones that are be defined using
# the assignment operator. In fact, you can reassign them new values if you
# want.
test_func = 5;
# The following line, if executed, returns an error because the test_func
# identifier is now associated to 5, which is not a function.
# test_func();
# ------------------------------------------------------------------------- #
# --- Returns ------------------------------------------------------------- #
# Functions can return values exactly like in other languages:
fun multiply(x, y)
return x * y;
p = 4;
q = 7;
r = multiply(p, q);
print(p, ' * ', q, ' = ', r, '\n');
# ------------------------------------------------------------------------- #
# --- Scopes -------------------------------------------------------------- #
#
# 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
# scope (closures). There's no such mechanism in this language (at the
# moment).
#
# The only exception is made for the "built in" variables, which are
# provided by the runtime of the language and can't be modified by the
# user. The print function is one of these variables. One may override
# these variables but the effect only lasts for the lifetame of the
# context local to the assignment.
# Overwrite the print variable inside the global scope..
print = 5;
fun test()
{
# Now call print from inside the function.
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!
fun get_print_back()
return print;
print = get_print_back();
print('Hei! Print is back!\n');
# ------------------------------------------------------------------------- #
# ------------------------------------------------------------------------- #