Skip to content

Commit

Permalink
Merge pull request #273 from AnyDSL/ast
Browse files Browse the repository at this point in the history
Ast
  • Loading branch information
leissa authored May 16, 2024
2 parents 46569c7 + f21a72c commit 816bc68
Show file tree
Hide file tree
Showing 128 changed files with 4,054 additions and 2,329 deletions.
2 changes: 1 addition & 1 deletion docs/Doxyfile.in
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@ HIDE_UNDOC_CLASSES = NO
# documentation.
# The default value is: NO.

HIDE_FRIEND_COMPOUNDS = YES
HIDE_FRIEND_COMPOUNDS = NO

# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
# documentation blocks found inside the body of a function. If set to NO, these
Expand Down
2 changes: 1 addition & 1 deletion docs/coding.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ to configure the project.
### add_thorin_plugin

Registers a new Thorin plugin.
```
```cmake
add_thorin_plugin(<plugin-name>
[SOURCES <source>...]
[PRIVATE <private-item>...]
Expand Down
32 changes: 16 additions & 16 deletions docs/langref.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ Tuple patterns allow for *groups*:
* `[a b c: .Nat, d e: .Bool]` means `[a: .Nat, b: .Nat, c: .Nat, d: .Bool, e: .Bool]`.

You can introduce an optional name for the whole tuple pattern:
```
```rust
.let abc::(a, b, c) = (1, 2, 3);
```
This will bind
Expand All @@ -229,15 +229,15 @@ This will bind
* `abc` to `(1, 2, 3)`.

Here is another example:
```
```rust
Π.Tas::[T: *, as: .Nat][%mem.M, %mem.Ptr Tas] → [%mem.M, T]
```

#### Rebind

Finally, you can put a <tt>\`</tt> in front of an identifier of a `()`-style pattern to (potentially) rebind a name to a different value.
This is particularly useful, when dealing with memory:
```
```rust
.let (`mem, ptr) = %mem.alloc (I32, 0) mem;
.let `mem = %mem.store (mem, ptr, 23:I32);
.let (`mem, val) = %mem.load (mem, ptr);
Expand All @@ -255,7 +255,7 @@ This is particularly useful, when dealing with memory:
| e | `` | alias for `.Type (1:.Univ)` | [Type](@ref thorin::Type) |
| e | `.Nat` | natural number | [Nat](@ref thorin::Nat) |
| e | `.Idx` | builtin of type `.Nat → *` | [Idx](@ref thorin::Idx) |
| e | `.Bool` | alias for `.Bool` | [Idx](@ref thorin::Idx) |
| e | `.Bool` | alias for `.Bool` | [Idx](@ref thorin::Idx) |

#### Literals & Co.

Expand Down Expand Up @@ -319,15 +319,15 @@ Expressions nesting is disambiguated according to the following precedence table

@note The domain of a dependent function type binds slightly stronger than ``.
This has the effect that
```
```rust
Π T: *TT
```
has the expected binding like this:
```
```rust
T: *) → (TT)
```
Otherwise, `` would be consumed by the domain:
```
```rust
Π T: (* → (TT)) ↯
```

Expand All @@ -344,7 +344,7 @@ The following table summarizes the different tokens used for functions declarati
### Declarations

The following function *declarations* are all equivalent:
```
```rust
.lam f(T: *)((x y: T), return: T → ⊥)@(.ff):= return x;
.con f(T: *)((x y: T), return: .Cn T) = return x;
.fun f(T: *) (x y: T): T = return x;
Expand All @@ -355,7 +355,7 @@ Note that all partial evaluation filters default to `.tt` except for `.con`/`.cn

The following function *expressions* are all equivalent.
What is more, since they are bound by a *let declaration*, they have the exact same effect as the function *declarations* above:
```
```rust
.let f = λ (T: *)((x y: T), return: T → ⊥)@(.ff):= return x;
.let f = .lm (T: *)((x y: T), return: T → ⊥) := return x;
.let f = .cn (T: *)((x y: T), return: .Cn T) = return x;
Expand All @@ -365,15 +365,15 @@ What is more, since they are bound by a *let declaration*, they have the exact s
### Applications

The following expressions for applying `f` are also equivalent:
```
```rust
f .Nat ((23, 42), .cn res: .Nat = use(res))
.ret res = f .Nat $ (23, 42); use(res)
```

### Function Types

Finally, the following function types are all equivalent and denote the type of `f` above.
```
```rust
Π [T:*][[T, T], T → ⊥] → ⊥
.Cn [T:*][[T, T], .Cn T]
.Fn [T:*] [T, T] → T
Expand All @@ -393,16 +393,16 @@ Hence, using the symbol `_` will always result in a scoping error.
### Pis

@note **Only**
```
```rust
Π x: ee
```
introduces a new scope whereas
```
```rust
x: ee
```
is a syntax error.
If the variable name of a Pi's domain is elided and the domain is a sigma, its elements will be imported into the Pi's scope to make these elements available in the Pi's codomain:
```
```rust
Π [T: *, U: *] → [T, U]
```

Expand All @@ -417,12 +417,12 @@ Named elements of mutable sigmas are available for extracts/inserts.
@note
These names take precedence over the usual scope.
In the following example, `i` refers to the first element `i` of `X` and **not** to the `i` introduced via `.let`:
```
```rust
.let i = 1_2;
Π X: [i: .Nat, j: .Nat] → f X#i;
```
Use parentheses to refer to the `.let`-bounded `i`:
```
```rust
.let i = 1_2;
Π X: [i: .Nat, j: .Nat] → f X#(i);
```
7 changes: 4 additions & 3 deletions examples/hello.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#include <thorin/driver.h>

#include <thorin/fe/parser.h>
#include <thorin/ast/parser.h>
#include <thorin/pass/optimize.h>
#include <thorin/util/sys.h>

Expand All @@ -14,10 +14,11 @@ using namespace thorin::plug;
int main(int, char**) {
try {
Driver driver;
auto& w = driver.world();
auto& w = driver.world();
auto ast = ast::AST(w);
driver.log().set(&std::cerr).set(Log::Level::Debug);

auto parser = Parser(w);
auto parser = ast::Parser(ast);
for (auto plugin : {"compile", "core"}) parser.plugin(plugin);

// .Cn [%mem.M, I32, %mem.Ptr (I32, 0) .Cn [%mem.M, I32]]
Expand Down
2 changes: 1 addition & 1 deletion external/fe
28 changes: 12 additions & 16 deletions gtest/automaton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

#include <thorin/world.h>

#include <thorin/fe/parser.h>
#include <thorin/ast/ast.h>

#include <thorin/plug/regex/dfa2matcher.h>
#include <thorin/plug/regex/regex2nfa.h>
Expand Down Expand Up @@ -155,9 +155,8 @@ TEST(Automaton, NFAAorBplusA) {

TEST(Automaton, Regex2NFA) {
Driver driver;
World& w = driver.world();
auto parser = Parser(w);
for (auto plugin : {"compile", "mem", "core", "math", "regex"}) parser.plugin(plugin);
World& w = driver.world();
ast::load_plugins(w, {"compile", "mem", "core", "math", "regex"});

auto pattern = w.call<regex::conj>(
2, w.tuple({w.call<regex::lit>(w.lit_i8('a')), w.call<regex::lit>(w.lit_i8('b'))})); // (a & b)
Expand All @@ -168,9 +167,8 @@ TEST(Automaton, Regex2NFA) {

TEST(Automaton, Regex2NFAAorBplusA) {
Driver driver;
World& w = driver.world();
auto parser = Parser(w);
for (auto plugin : {"compile", "mem", "core", "math", "regex"}) parser.plugin(plugin);
World& w = driver.world();
ast::load_plugins(w, {"compile", "mem", "core", "math", "regex"});

auto pattern = w.call<regex::conj>(
2, w.tuple({w.call(regex::quant::plus, w.call<regex::disj>(2, w.tuple({w.call<regex::lit>(w.lit_i8('a')),
Expand All @@ -187,9 +185,9 @@ TEST(Automaton, Regex2NFAAorBplusA) {

TEST(Automaton, Regex2NFA1or5or9) {
Driver driver;
World& w = driver.world();
auto parser = Parser(w);
for (auto plugin : {"compile", "mem", "core", "math", "regex"}) parser.plugin(plugin);
World& w = driver.world();
auto ast = ast::AST(w);
ast::load_plugins(w, {"compile", "mem", "core", "math", "regex"});

// %regex.disj 2 (%regex.disj 2 (%regex.range ‹2; 49I8›, %regex.range ‹2; 53I8›), %regex.range ‹2;
// 57I8›)
Expand All @@ -207,9 +205,8 @@ TEST(Automaton, Regex2NFA1or5or9) {

TEST(Automaton, Regex2NFANot1or5or9) {
Driver driver;
World& w = driver.world();
auto parser = Parser(w);
for (auto plugin : {"compile", "mem", "core", "math", "regex"}) parser.plugin(plugin);
World& w = driver.world();
ast::load_plugins(w, {"compile", "mem", "core", "math", "regex"});

// %regex.not_ (%regex.disj 2 (%regex.disj 2 (%regex.range ‹2; 49I8›, %regex.range ‹2; 53I8›),
// %regex.range ‹2; 57I8›))
Expand All @@ -228,9 +225,8 @@ TEST(Automaton, Regex2NFANot1or5or9) {

TEST(Automaton, Regex2NFANotwds) {
Driver driver;
World& w = driver.world();
auto parser = Parser(w);
for (auto plugin : {"compile", "mem", "core", "math", "regex"}) parser.plugin(plugin);
World& w = driver.world();
ast::load_plugins(w, {"compile", "mem", "core", "math", "regex"});

// %regex.not_ (%regex.conj 3 (%regex.cls.w, %regex.cls.d, %regex.cls.s))
auto pattern = w.call<regex::not_>(
Expand Down
4 changes: 2 additions & 2 deletions gtest/helpers.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include "helpers.h"

#include <algorithm>

#include <gtest/gtest.h>

namespace thorin::gtest {

std::string test_name() {
Expand Down
7 changes: 2 additions & 5 deletions gtest/helpers.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
#ifndef THORIN_GTEST_HELPERS_H
#define THORIN_GTEST_HELPERS_H
#pragma once

#include <gtest/gtest.h>
#include <string>

namespace thorin::gtest {

std::string test_name();

}

#endif
66 changes: 46 additions & 20 deletions gtest/lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@

#include <thorin/driver.h>

#include <thorin/fe/lexer.h>
#include <thorin/ast/ast.h>
#include <thorin/ast/lexer.h>

using namespace std::literals;
using namespace thorin;
using namespace thorin::ast;

TEST(Lexer, Toks) {
Driver driver;
std::istringstream is("{ } ( ) [ ] ‹ › « » : , . .lam .Pi λ Π 23₀₁₂₃₄₅₆₇₈₉");
Lexer lexer(driver.world(), is);
auto& w = driver.world();
auto ast = AST(w);
std::istringstream is("{ } ( ) [ ] ‹ › « » : , . .lam λ Π 23₀₁₂₃₄₅₆₇₈₉");
Lexer lexer(ast, is);

EXPECT_TRUE(lexer.lex().isa(Tok::Tag::D_brace_l));
EXPECT_TRUE(lexer.lex().isa(Tok::Tag::D_brace_r));
Expand All @@ -29,7 +33,6 @@ TEST(Lexer, Toks) {
EXPECT_TRUE(lexer.lex().isa(Tok::Tag::T_comma));
EXPECT_TRUE(lexer.lex().isa(Tok::Tag::T_dot));
EXPECT_TRUE(lexer.lex().isa(Tok::Tag::K_lam));
EXPECT_TRUE(lexer.lex().isa(Tok::Tag::K_Pi));
EXPECT_TRUE(lexer.lex().isa(Tok::Tag::T_lm));
EXPECT_TRUE(lexer.lex().isa(Tok::Tag::T_Pi));
auto tok = lexer.lex();
Expand All @@ -40,41 +43,58 @@ TEST(Lexer, Toks) {

TEST(Lexer, Errors) {
Driver driver;
auto& w = driver.world();
auto ast = ast::AST(w);
std::istringstream is1("asdf \xc0\xc0");
Lexer l1(driver.world(), is1);
Lexer l1(ast, is1);
l1.lex();
EXPECT_ANY_THROW(l1.lex());
l1.lex();
EXPECT_GE(ast.error().num_errors(), 1);
EXPECT_TRUE(ast.error().msgs().front().str.starts_with("invalid UTF-8"));
ast.error().clear();

std::istringstream is2("foo \xaa");
Lexer l2(driver.world(), is2);
Lexer l2(ast, is2);
l2.lex();
l2.lex();
EXPECT_ANY_THROW(l2.lex());
EXPECT_GE(ast.error().num_errors(), 1);
EXPECT_TRUE(ast.error().msgs().front().str.starts_with("invalid UTF-8"));
ast.error().clear();

std::istringstream is3("+");
Lexer l3(driver.world(), is3);
EXPECT_ANY_THROW(l3.lex());
Lexer l3(ast, is3);
l3.lex();
EXPECT_GE(ast.error().num_errors(), 1);
EXPECT_TRUE(ast.error().msgs().front().str.starts_with("stray"));
ast.error().clear();

std::istringstream is4("-");
Lexer l4(driver.world(), is4);
EXPECT_ANY_THROW(l4.lex());
Lexer l4(ast, is4);
l4.lex();
EXPECT_GE(ast.error().num_errors(), 1);
EXPECT_TRUE(ast.error().msgs().front().str.starts_with("stray"));
ast.error().clear();
}

TEST(Lexer, Eof) {
Driver driver;
auto& w = driver.world();
auto ast = AST(w);
std::istringstream is("");

Lexer lexer(driver.world(), is);
Lexer lexer(ast, is);
for (int i = 0; i < 10; i++) EXPECT_TRUE(lexer.lex().isa(Tok::Tag::EoF));
}

class Real : public testing::TestWithParam<int> {};

TEST_P(Real, sign) {
Driver driver;
World& w = driver.world();
auto& w = driver.world();
auto ast = AST(w);

// clang-format off
auto check = [&w](std::string s, f64 r) {
auto check = [&ast](std::string s, f64 r) {
const auto sign = GetParam();
switch (sign) {
case 0: break;
Expand All @@ -84,7 +104,7 @@ TEST_P(Real, sign) {
}

std::istringstream is(s);
Lexer lexer(w, is);
Lexer lexer(ast, is);

auto tag = lexer.lex();
EXPECT_TRUE(tag.isa(Tok::Tag::L_f));
Expand All @@ -103,12 +123,18 @@ TEST_P(Real, sign) {
// clang-format on

std::istringstream is1("0x2.34");
Lexer l1(w, is1);
EXPECT_ANY_THROW(l1.lex());
Lexer l1(ast, is1);
l1.lex();
EXPECT_EQ(ast.error().num_errors(), 1);
EXPECT_TRUE(ast.error().msgs().front().str == "hexadecimal floating constants require an exponent"sv);
ast.error().clear();

std::istringstream is2("2.34e");
Lexer l2(w, is2);
EXPECT_ANY_THROW(l2.lex());
Lexer l2(ast, is2);
l2.lex();
EXPECT_EQ(ast.error().num_errors(), 1);
EXPECT_TRUE(ast.error().msgs().front().str == "exponent has no digits");
ast.error().clear();
}

INSTANTIATE_TEST_SUITE_P(Lexer, Real, testing::Range(0, 3));
Loading

0 comments on commit 816bc68

Please sign in to comment.