Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions data/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# QuasiLang Syntax Guide

QuasiLang is the source language parsed by **QuasiPiler**.
Sample programs: `test00.qc`–`test12.qc`.

---

## Basics

* **Comments**
* Line: `// comment`
* Block: `/* comment */` — may be inline or multi-line.
* **Whitespace**: ignored except as a separator.
* **Identifiers**: letters, digits, `_` (cannot start with a digit).
* **Literals**
* Numbers: integers, floats, exponents.
* Strings: `'...'` or `"..."` with escapes (`\n`, `\t`, `\\`).
* Booleans: `true`, `false`
* Null: `null`

---

## Expressions

* **Arithmetic & Assignment**: `+ - * / %` with `= += -= *= /= %=`
* **Comparison & Logic**: `== != < <= > >=` and `&& || !`
* **Bitwise & Shift**: `& | ^ << >>` with `&= |= ^= <<= >>=`
* **Increment/Decrement**: prefix and postfix `++ --`
* **Ternary**: `cond ? a : b`
* **Member access**: `obj.key`, `obj["key"]`
* **Indexing & slicing**:
* Single index: `arr[i]`
* Slices: `arr[start:end:step]`
* Any part may be omitted: `arr[:end]`, `arr[start:]`, `arr[::step]`, `arr[::]`
* **Function calls**: `f(arg1, arg2)`
* **Function declaration**: `fu(x,y){ return x+y; }`

---

## Data Structures

* **Lists**: `[1, 2, 3]`
* **Objects**: `{ "key": value, "other": 42 }`
*Keys must be constant strings.*
* **Tuples `()`**

* Foreach bundles (abstract streams)
* Not concrete lists unless materialized with `list(...)`

### Tuple-based foreach

Tuples drive vectorized foreach expansion when applied after an expression:

1. **Member/Index tuple**

```qc
obj.(a, b, c) // (obj.a, obj.b, obj.c)
arr[(i, j, k)] // (arr[i], arr[j], arr[k])
```

2. **Operators with tuples**

```qc
5 + (1,2,3) // (6, 7, 8)
list(5 + (1,2,3)) // [6, 7, 8]
```

3. **Chaining**

```qc
obj.(a,b).(x,y) // (obj.a.x, obj.a.y, obj.b.x, obj.b.y)
arr[(1,4)].(id,total) // (arr[1].id, arr[1].total, arr[4].id, arr[4].total)
```

4. **Slices with tuples**

```qc
arr[1:6:2][(2,3)] // (arr[1][2], arr[1][3], arr[3][2], arr[3][3], arr[5][2], arr[5][3])
list(arr[1:6:2])[2] // arr[5]
```

---

## Statements & Declarations

* **Variables**: `name = expression;`
* **Functions**: `name(p1,p2){ ... }` or `name = fu(p1){ ... };`
* **Conditionals**:

```qc
if (cond) { ... }
elif (other) { ... }
else { ... }
```

Bodies may omit braces for a single statement:

```qc
if (cond) do_something();
while (ok) step();
```

Return with conditional:

```qc
return if(a){b} else c; // valid
return if(a) b; else c; // invalid due to precedence
```
* **Loops**: `while(cond){...}`, `for(init; cond; step){...}`
Both also allow single-statement bodies without `{}`.
* **Exceptions**:

```qc
try { ... } catch (err) { ... } finally { ... }
```
* **Jumps**: `break;`, `continue;`, `return expr;`, `goto label;`
* **Labels**: `label_name:`

---

## Examples

**Foreach with tuples**

```qc
user = { "id": 7, "name": "Ada", "meta": { "city":"Paris", "tz":"CET" } };

user.(id, name) // 7, "Ada"
user.(meta).("city","tz") // "Paris", "CET"
orders[(0,2)].(id,total) // orders[0].id, orders[0].total, orders[2].id, orders[2].total
```

**Slices and foreach**

```qc
matrix = [row0,row1,row2,row3,row4,row5];
matrix[1:6:2][(2,3)]; // row1[2], row1[3], row3[2], row3[3], row5[2], row5[3]
list(matrix[1:6:2])[2]; // row5
```

**Arithmetic with tuples**

```qc
print(5 + (1,2,3)); // 6,7,8
print(list(5 + (1,2,3))); // [6,7,8]
```

**Control flow**

```qc
if (ready) start();
while (i < n) i++;
return if(ok){result} else null;
```

**Chained functions**

```qc
adder = fu(x){ return fu(y){ return fu(z){ return x+y+z; }; }; };
sum = adder(1)(2)(3);
```
2 changes: 2 additions & 0 deletions data/test07.qc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

a + b += c, c = a + b, d;

[a + b, c + d * e];

{a + b * c; d + e - f, g + h << (i >> j)}

return a + b - c * d / e % f ^ g << h >> i | j & k;
Expand Down
4 changes: 3 additions & 1 deletion data/test08.qc
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ main(a+b, c);

main(a, b, c) + d;

a + main(a, b, c);
a + main(a, b, c);

main(a)(b)(c);
16 changes: 16 additions & 0 deletions include/ast.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,22 @@ struct callexp_node : token_node {

using callexp_ptr = std::shared_ptr<callexp_node>;

struct imcallexp_node : ast_node {
ast_node_ptr callee;
ast_node_ptr paren;
bool has_paren { false };

explicit imcallexp_node(ast_node_ptr callee);
void set_paren(ast_node_ptr paren);

const position& get_start() const override;
void dump(
std::ostream& os, const std::string& prefix, bool is_last, bool full
) const override;
};

using imcallexp_ptr = std::shared_ptr<imcallexp_node>;

struct fundecl_node : callexp_node {
ast_node_ptr body;
bool has_body { false };
Expand Down
6 changes: 6 additions & 0 deletions include/expression.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#define EXPRESSION_HPP

#include "ast.hpp"
#include <source_location>
#include <string_view>
#include <unordered_map>
#include <vector>
Expand Down Expand Up @@ -75,6 +76,11 @@ class expression {
const std::vector<ast_node_ptr>& nodes, size_t pos, std::string_view op
);

static std::runtime_error make_error(
const std::string& message, const std::vector<item>& expression,
const std::source_location& location = std::source_location::current()
);

static const std::unordered_map<std::string, std::pair<int, bool>>
binary_ops;
static const std::unordered_map<std::string, int> prefix_ops;
Expand Down
17 changes: 15 additions & 2 deletions include/grouper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#define GROUPER_HPP

#include "ast.hpp"
#include <vector>

/**
* @brief Parses tokens into hierarchical groups and expressions.
Expand Down Expand Up @@ -62,13 +63,25 @@ class grouper {
[[nodiscard]] bool
handle_chain(const group_ptr& result, const group_ptr& inode) const;

[[nodiscard]] static bool is_secondary_keyword(const std::string& kw);
[[nodiscard]] static std::string
keyword_from_node(const ast_node_ptr& node);
[[nodiscard]] group_ptr fetch_previous_command(
const group_ptr& result, const std::string& kw, const group_ptr& inode
) const;
[[nodiscard]] std::string fetch_previous_keyword(
const group_ptr& prev, const std::string& kw, const group_ptr& inode
) const;
void validate_chain(
const std::string& prev_kw, const std::string& kw,
const group_ptr& inode
) const;

bool append_group(
const group_ptr& result, const ast_node_ptr& node,
bool& wait_for_condition, bool& wait_for_body, group_kind kind
) const;

void identify_body(const group_ptr& group) const;

void identify(const group_ptr& group, const group_ptr& result) const;
/**
* @brief Transform token groups representing arithmetic into AST nodes.
Expand Down
6 changes: 6 additions & 0 deletions include/reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ class reader {
* token_kind::floating.
*/
token_kind read_number(std::string& into);
// Helpers for numeric literal parsing
bool check_digit() const noexcept;
void read_digits(std::string& into);
void read_integer_part(std::string& into);
bool read_fraction_part(std::string& into);
bool read_exponent_part(std::string& into);

void init_token(token& t) const noexcept;
/**
Expand Down
Loading
Loading