Skip to content

Commit

Permalink
enabled std aliases by default
Browse files Browse the repository at this point in the history
  • Loading branch information
maniospas committed Oct 7, 2024
1 parent dc87785 commit 9d7924c
Show file tree
Hide file tree
Showing 42 changed files with 991 additions and 2,301 deletions.
13 changes: 13 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
version: 2

build:
os: ubuntu-latest
tools:
python: "3.12"

python:
install:
- requirements: docs/requirements.txt

mkdocs:
configuration: mkdocs.yaml
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Compiler flags
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexec-charset=UTF-8 -finput-charset=UTF-8 /utf-8 -O2")
#add_definitions(-D_WIN32_WINNT=0x0A00)

# Include directories: add your project's include folder
Expand Down
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
# Blombly

:computer: Expressive<br/>
:duck: Dynamic<br/>
:rocket: Parallel<br/>
:goggles: Safe<br/>
:computer: Expressive
:duck: Dynamic
:rocket: Parallel
:goggles: Safe

[Learn more...](https://maniospas.github.io/Blombly/)
## [Guidebook](https://maniospas.github.io/Blombly/)

# Very quick start
## Install & run

Find the latest release [here](https://github.com/maniospas/Blombly/releases/tag/latest).
Find the latest release [here](https://github.com/maniospas/Blombly/releases/latest).
<br>Current build targets: *Windows x64*

Unzip the folder in a directory and create a file `main.bb` with the following contents:
Unzip the folder in a directory and create a file `main.bb` (or any name but with the same extension). Add the following contents:

```cpp
#include "libs/fmt"
Expand All @@ -25,7 +25,7 @@ print("Hello ", name, "!");
Run `blombly.exe main.bb`, where the executable and main files can be any path, and check that everything is working properly.
Do not move the executable without all the `libs/` and dynamically linked libraries that are packaged with it.
# Build from source
## Build from source
Clone this repository and install gcc in your system. Then, follow the steps below, which include installing the `vcpkg` dependency manager:
Expand All @@ -43,7 +43,7 @@ cmake -B ./build
cmake --build ./build --config Release # rerun this after making changes
```

# Credits
## Credits

Author: Emmanouil (Manios) Krasanakis<br/>
Contact: maniospas@hotmail.com<br/>
Expand Down
5 changes: 3 additions & 2 deletions blombly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ std::string get_executable_directory(const std::string& argv0) {
int main(int argc, char* argv[]) {
blombly_executable_path = get_executable_directory(argv[0]);
Terminal::enableVirtualTerminalProcessing();
initializeOperationMapping();
initializeOperationMapping();
std::cout<<"\033[0m";

std::string fileName = "main.bb";
int threads = std::thread::hardware_concurrency();
Expand Down Expand Up @@ -80,7 +81,7 @@ int main(int argc, char* argv[]) {
std::cerr << e.what() << "\n";
return 1;
}

program_start = std::chrono::steady_clock::now();
return vm(fileName, threads);
}
Binary file modified blombly.exe
Binary file not shown.
102 changes: 102 additions & 0 deletions docs/advanced/preprocessor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# 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.

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.

## Include and modules

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

```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.");
}
}
```


## Macros

Macros are transformations for reducing boilerplate code. They are declared with statements of the form `#macro (@expression) = (@transformation);`
Both the expression and transformation parts consist of fixed "keyword" tokens and named wildcard tokens, prepended with att (`@`).
Wildcards represent any sequence of tokens and matched between the expression and the transformation.
To support faster compilation and improve comprehension, the first token of the expression needs to be a keyword (e.g., `fn @name (@args)` is valid).

Here is an example of how macros simplify code, defining a class and function with macros from the std/oop module.
Using these, one can now define classes, modules, and functions more concisely:

```java
// oop.bb
#macro (class @name {@code}) = (final @name = {@code});
#macro (fn @name(@args) {@code}) = (final @name(@args) = {@code});
#macro (module @name {@code}) = (final @name = new {@code});
```

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

class Finder {
fn contains(number) {
i = 2;
i_max = int(number^0.5);
while (i <= i_max) {
if (number % i == 0) return false;
i = i + 1;
}
return true;
}

fn next() {
while (true) {
this.number = this.number + 1;
if (this.contains(number)) return this.number;
}
}
}

finder = new {Finder: number = 10;}

print(finder.next());
print(finder.next());
```
64 changes: 64 additions & 0 deletions docs/advanced/servers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Servers

## Routes

Blombly offers the ability to set up REST services.
Instantiating a server is as simpleas calling `routes=std::server(port)`,
where the port is provided.
Treat the generated server as a map from resource location strings to code blocsk
to be called when the respective resource is requested. Blocks called this
ways should returned either a string plain text or a request result struct (see below).
Parts of resource names that reside in angular brackets `<...>` indicate that the respective
part of therequest should be treated as a string argument to the callable.

For example, the following snippet uses the standard library to create the alias `server`
for `std::server` and then redirects `echo/<input>` to echo the provided input;
run code and open the browser and visit `localhost:8000/echo/MyTest` to see this in action.

```java
// main.bb
#include "libs/std"
routes = server(8000);
routes["/echo/<input>"] = {return input;}
while(true) {} // wait indefinitely
```


## Request data

In addition to the keyword argument values obtained by parsing the request, calls
to route code blocks may be enriched with several positional arguments, if available.
These are listed below:

| Argument | Type | Details |
| -------- | ---- | ----------- |
| method | str | "GET" or "POST". |
| content | str | Any received message content. This requires further parsing. |
| ip | str | The user's ip address. |
| http | str | The http protocol's version. |
| query | str | Any query parameters; those following after the questionmark (`?`). |
| ssl | bool | Whether HTTPS or WS is used (SSL/TLS used). |
| uri | str | The request's full uri. |


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

new {
value = 0;
routes = server(8000);
routes["/hi/<number>"] = {
if((number as int(number))==false)
return "The number of hi must be an integer.";
if(number<=0)
return "Need a positive number of hi. Why must you take them away? :-(";
this.value = this.value + 1;
return str(this.value+number)+" hello your "+str(number)+" hi";
}
}

print("Give me some greetings at localhost:8000/hi/<number>");
while(true) {} // wait indefinitely
```
41 changes: 41 additions & 0 deletions docs/advanced/signals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Signals

Blombly has a success-fail mechanism to capture algorithm outcomes. This consists of two parts. First is a `try` statement, which is applied on a block of code to obtain the outcome of any internal `return` or `fail` signals; these are otherwise ways of escaping calling methods by immediately returning values or creating errors, but `try` ensures that all such signals are intercepted. Its outcome is the intercepted signal and can be assigned to a variable for further handling, like so:

```java
result = try {
x = x + 1; // invalid code that creates an error (missing variable x)
}
print(result);
print("Successfully intercepted the above error");
```

Similarly to other control flow statements, brackets are optional for a `try` that is applied to only one statement. Omitting brackets can be used to create concise variations of other control flow statements. For example, one can compute exiting values, break loops, or detect if loops were not broken. For example, an one-liner that computes the sign of a variable `x` is `sgn = try if(x >= 0) return 1 else return -1;`

We typically want to differentiate between those try results that hold errors and those that do not. This is achieved through the catch statement; this is near-identical to a conditional statement with the only difference being that it checks whether the condition is an error (instead of checking a boolean value). An example usage of this statement is demonstrated below. In this, the try statement is responsible for intercepting the returned value of i, which stops the loop immediately.

If no value was returned, try assigns an error value to the result.

```java
least = 4;
i = 0;
result = try while (i <= 100) {
i = i + 3;
if (i >= least) return i;
}
print("Finished searching.");
catch (result) fail("Found nothing: " + str(result));
print("The least multiple of 3 in range [" + str(least) + ", 100] is: " + str(result));
```

If nothing is intercepted by a try statement, then a missing value is yielded. In this case, use the as syntax like normally to identify if there is a return value. For example, the following snippet inlines an algorithm and checks that it returns a value. Remove the return statement from the algorithm code block (leave it empty) and see how the fail statement correctly creates an error.

```java
algorithm = {return "result";}

if (x as try algorithm:)
print(x) // ; cannot be the last symbol before else
else
fail("Nothing was returned");
```

Loading

0 comments on commit 9d7924c

Please sign in to comment.