Skip to content

Commit

Permalink
declaration memory for code
Browse files Browse the repository at this point in the history
  • Loading branch information
maniospas committed Oct 7, 2024
1 parent 24340a0 commit c45afb7
Show file tree
Hide file tree
Showing 11 changed files with 142 additions and 111 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
:rocket: Parallel
:goggles: Safe

## [Guidebook](https://maniospas.github.io/Blombly/)
## [Documentation](https://blombly.readthedocs.io/en/latest/)

## Install & run

Expand Down
Binary file modified blombly.exe
Binary file not shown.
117 changes: 67 additions & 50 deletions docs/advanced/preprocessor.md
Original file line number Diff line number Diff line change
@@ -1,58 +1,22 @@
# The preprocessor

Blombly's preprocessor can understand several instructions that transform the source code before compilation into `.bbvm` files. Three transformations are available: dependencies that make a source code file include another's code, macros that add to the language's grammar with higher-level expressions, and specification property declarations.
Blombly's preprocessor can understand several instructions that transform the source code before compilation into `.bbvm` files.
Preprocessor instractions are made distinct by prepending them with a `#` symbol.
Three main types of preprocessing are available: dependencies that make a source code file include another's code,
macros that add to the language's grammar with higher-level expressions, and specification property declarations.

Of those, specifications are already been described as a mechanism that attaches final values to code blocks by evaluating them just after the block's definition.
## Specifications

## Include and modules
Specifications have already been described as a mechanism that attaches final values to code blocks. Evaluating them
just after the block's definition.

Dependencies on other `.bb` files are stated with a pattern like so:
## Dependencies

```java
// main.bb
#include "std/oop"
```

This statement inlines the contents of the file `std/oop.bb`. Dependencies allow code modularization without loading overheads, as the compiled .bbvm packs all necessary instructions to run directly. Note that dependencies should not declare specifications, as these are the provenance of code blocks meant to run dynamically instead of immediately upon import. When creating reusable libraries, prefer constructing modules and final objects (this is automated with the module keyword of `std/oop`).

Below is an example of what a module may look like. Instead of specifications, `final` properties are set for documentation and versioning. However, methods (which are essentially code blocks meant to be called) obtain specifications. Notice that those specifications can depend on the library's other properties. To use the module in your code, include the module and call the relevant methods.

```java
// main.bb
#include "mylib"

mylib.tests();
print(mylib.add(1, 2));
```

```java
// mylib.bb
module mylib {
final name = "mylib";
final author = "Your Name";
final doc = "This is a simple numeric interface";
final version = 1.0;

fn add(x, y) {
#spec doc = "Computes an addition in " + name;
#spec version = version;
return x + y;
}

fn abs(x) {
#spec doc = "Computes the absolute value in " + name;
#spec version = version;
if (x < 0) return -x;
return x;
}

final tests = {
if (add(1, 2) != 3) fail("Invalid addition");
if (abs(-1) != 1) fail("Invalid abs");
print("Tests successful.");
}
}
```
Dependencies on other `.bb` files or folders are stated with an include statement that looks like this `#include "std/oop"`.
When an include is encountered inlines the contents of the file `std/oop.bb` or, if `std/oop` is a folder of the filter `std/oop/.bb`.
Dependencies allow code modularization without loading overheads, as the compilation outcome packs all necessary instructions to run itself.
Dependencies should not declare specifications, as these are the provenance of code blocks meant to run dynamically instead of immediately upon import.
When creating reusable libraries, prefer constructing modules and final objects (this is automated with the module keyword of `std/oop`).


## Macros
Expand Down Expand Up @@ -99,4 +63,57 @@ finder = new {Finder: number = 10;}

print(finder.next());
print(finder.next());
```
```

## The standard library

*This section is under construction.*


## Writing an new library

Below is an example of what a module may look like. Instead of specifications, `final` properties are set for documentation and versioning.
However, methods (which are essentially code blocks meant to be called) obtain specifications.
Notice that those specifications can depend on the library's other properties. To use the module in your code, include the module and call the relevant methods.

```java
// main.bb
#include "mylib"

mylib.tests();
print(mylib.add(1, 2));
```

```java
// mylib.bb
#include "libs/std"
enable oop;

module mylib {
final name = "mylib";
final author = "Your Name";
final doc = "This is a simple numeric interface";
final version = 1.0;

fn add(x, y) {
#spec doc = "Computes an addition in " + name;
#spec version = version;
return x + y;
}

fn abs(x) {
#spec doc = "Computes the absolute value in " + name;
#spec version = version;
if (x < 0) return -x;
return x;
}

final tests = {
if (add(1, 2) != 3)
fail("Invalid addition");
if (abs(-1) != 1)
fail("Invalid abs");
print("Tests successful.");
}
}
```
38 changes: 22 additions & 16 deletions docs/basics/builtins.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Sequential code

This section covers Blombly's most basic commands, namely comments, variable handling, builtin datatypes,
and control flow. Some concepts may be familiar, but the reader is encouraged to skim through this section
as it also touches some unique language features.
This section covers Blombly commands that are used in writing sequential code. These include the common concepts
of comments, variable handling, builtin datatypes, and flow control.
Some discussed concepts may be familiar, but we urge reader to skim through this section, because some unique features are added to the mix.

## Comments

Expand Down Expand Up @@ -117,18 +117,22 @@ while (counter<10) {
}
```

## Sneak peek at signals
## Trying until return

To get a full sense of how control flow may work with other commands,
here we briefly introduce signals -
the mechanism behind Blombly's error handling. We use these to create the
equivalent of continue and break statements without needing more keywords.
here we make a soft introduction to return signals; if errors indicate
unsuccessful algorithms, return statements indicate successfully concluding
algorithms. Here, we focus only on returning (and ignore additional error handling statements
that would be needed), as this lets us define
the equivalent of continue and break statements without needing more keywords.

In particular, the `value = try{@code}` intercepts both errors
and `return value;`
statements on its internal code segment. Given that brackets may again
be ommited for a segment of one command, one can use it to compute
one-line conditions like in this example:
To intercept both kinds of signals, use the `value = try{@code}` pattern,
from which a value can be retrieved with the statement`return value;`.
This is the same mechanism as the one we will [next](blocks.md) use to
return values from called methods.

By ommiting brackes for code comprising only one command, we can conveniently
combine interception mechanism with other control flows statements like so:

```java
// main.bb
Expand All @@ -137,17 +141,19 @@ sgn = try if(x>=0) return 1 else return -1;
print("Sign is "+str(sgn));
```

Asimilar syntax can stop loops, though we will not dabble on handling returned values for now.
What should be mentioned is that, contrary to errors, intercepting returns is lightweight.
A similar syntax can be used to halt loops beliw, though we will not dabble on
handling returned values for now.
It should be mentioned that, contrary to some additional computations that are
normally required for error handling, intercepting returns is lightweight.

```java
// main.bb
counter = 0;
try while (true) {
print("Counter is: " + str(counter));
counter = counter + 1;
print("Counter is: " + str(counter));
if(counter==10)
return;
return; // keeps exiting the code until intercepted by try
}
```

Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Blombly

[Get started](setup.md) | [Download](https://github.com/maniospas/Blombly/releases/latest)
[Get started](setup.md) | [Download](https://github.com/maniospas/Blombly/releases/latest) | [GitHub](https://github.com/maniospas/Blombly)

There are many programming languages for writing dynamic yet safe code. You might already have a favorite one! However, safety often comes at the cost of reduced expressiveness, like mutability restrictions, or may rely on non-infallible programmers. Blombly addresses these issues with a small instruction set that is easy to learn and use but which promises code and memory safety. It also focuses on algorithmic logic by automating memory management and parallelization.

Expand Down
15 changes: 9 additions & 6 deletions docs/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,21 @@ Use a Java syntax highlighter (but not a syntax checker), as the two languages s
print("Hello world!");
```

To run this file, download Blombly's latest release from [here](https://github.com/maniospas/Blombly/releases/latest). Extract the release to a folder
To run this file, download Blombly's latest [release](https://github.com/maniospas/Blombly/releases/latest). Extract the release to a folder
and add it to your filepath to let your operating system know where `blombly.exe` is located. If you don't want to add anything to your filepath,
use the full path to the executable everywhere. Then run the following console command:
use the full path to the executable everywhere. Instructions to build the library from source for your environment are provided in repository's
[GitHub](https://github.com/maniospas/Blombly) page.


Once you set up everything, run the following console command.
If a message that starts with `( ERROR )` appears, the language was set up properly but there was some syntax or logic error.
For example, brackets may have closed with a semicolon, or there could be some other type of infraction. More on errors below.

```bash
> blombly main.bb
Hello world!
```

Success! If a message that starts with (`<ERROR>`) appears, the language was set up properly but there was some syntax or logic error (more later).
For example, brackets may have closed with a semicolon, or there could be some other type of infraction. Read the error details carefully, as they highly descriptive
(more on this later).


## VM code
Expand Down Expand Up @@ -71,4 +74,4 @@ but this is a more advanced concept that we talk about [later](success-fail.md).
```java
// main.bb
print(x); // CREATES AN ERROR
```
```
19 changes: 12 additions & 7 deletions main.bb
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
counter = 0;
while (counter<10) try {
counter = counter + 1;
if (counter % 2==0)
return;
print("Odd value: " + str(counter));
}
method = new {
final test1 = {
print("test1");
}
final test2 = {
test1:
print("test2");
}
\call = test2;
}

method();
39 changes: 16 additions & 23 deletions main.bbvm
Original file line number Diff line number Diff line change
@@ -1,27 +1,20 @@
BUILTIN _bb0 I0
IS counter _bb0
BUILTIN _bb2 I10
lt _bb1 counter _bb2
BEGIN _bb2
BEGIN _bb3
BEGIN _bb6
BUILTIN _bb8 I1
add _bb7 counter _bb8
IS counter _bb7
BEGIN _bb9
return _bbresult0
BUILTIN _bb4 "test1"
print # _bb4
END
BUILTIN _bb11 I0
BUILTIN _bb13 I2
mod _bb12 counter _bb13
eq _bb10 _bb12 _bb11
if # _bb10 _bb9
str _bb15 counter
BUILTIN _bb16 "Odd value: "
add _bb14 _bb16 _bb15
print # _bb14
IS test1 _bb3
final # test1
BEGIN _bb5
inline _bb6 test1
BUILTIN _bb7 "test2"
print # _bb7
END
try _bb4 _bb6
BUILTIN _bb2 I10
lt _bb1 counter _bb2
IS test2 _bb5
final # test2
IS \call test2
return _bbresult0 this
END
while # _bb1 _bb3
new _bb0 _bb2
IS method _bb0
call _bb8 # method
5 changes: 3 additions & 2 deletions mkdocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ nav:
- Signals: advanced/signals.md
- Servers: advanced/servers.md
theme:
name: united
name: united

extra_css:
- css/extra.css
- css/extra.css

10 changes: 5 additions & 5 deletions src/interpreter/functional/handle_command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ void handleCommand(const std::shared_ptr<std::vector<Command*>>& program,
pos += 1;
}
bbassert(depth >= 0, "Code block never ended.");
auto cache = std::make_shared<Code>(program, i + 1, pos, nullptr);
auto cache = std::make_shared<Code>(program, i + 1, pos, memory);
auto val = std::make_shared<Code>(program, i + 1, pos, memory, cache->getAllMetadata());
val->scheduleForParallelExecution = true;
cache->scheduleForParallelExecution = true;
Expand Down Expand Up @@ -100,28 +100,28 @@ void handleCommand(const std::shared_ptr<std::vector<Command*>>& program,
}
auto code = std::static_pointer_cast<Code>(called);
if (true || !code->scheduleForParallelExecution || !Future::acceptsThread()) {
auto newMemory = std::make_shared<BMemory>(memory, LOCAL_EXPECTATION_FROM_CODE(code));
auto newMemory = std::make_shared<BMemory>(code->getDeclarationMemory(), LOCAL_EXPECTATION_FROM_CODE(code));
bool newReturnSignal(false);
if (context) {
bbassert(context->getType() == CODE, "Call context must be a code block.");
result = executeBlock(std::static_pointer_cast<Code>(context), newMemory, newReturnSignal, args);
}
if(newReturnSignal)
break;
newMemory->detach(memory);
newMemory->detach(code->getDeclarationMemory());
result = executeBlock(code, std::move(newMemory), newReturnSignal, args);
SCOPY(result);
newMemory.reset();
} else {
auto newMemory = std::make_shared<BMemory>(memory, LOCAL_EXPECTATION_FROM_CODE(code));
auto newMemory = std::make_shared<BMemory>(code->getDeclarationMemory(), LOCAL_EXPECTATION_FROM_CODE(code));
bool newReturnSignal(false);
if (context) {
bbassert(context->getType() == CODE, "Call context must be a code block.");
result = executeBlock(std::static_pointer_cast<Code>(context), newMemory, newReturnSignal, args);
}
if(newReturnSignal)
break;
newMemory->detach(memory);
newMemory->detach(code->getDeclarationMemory());

auto futureResult = std::make_shared<ThreadResult>();
auto future = std::make_shared<Future>(futureResult);
Expand Down
6 changes: 6 additions & 0 deletions src/utils/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,12 @@ void macros(std::vector<Token>& tokens, const std::string& first_source) {
break;
}
}
bbassert(position > tokens.size() - 1,
"Unexpected `#spec` encountered."
"\n \033[33m!!!\033[0m `#spec` declarations can only reside "
"\n in named code blocks. These refer to code blocks being"
"\n declared and assigned to at least one variable.\n"
+Parser::show_position(tokens, i));
bbassert(depth == 0, "Imbalanced parantheses or brackets.\n" + Parser::show_position(tokens, i));

std::vector<Token> newTokens;
Expand Down

0 comments on commit c45afb7

Please sign in to comment.