Introduced concurrent block notation
Additionally removed wierd behaviour with Interpreter::play where empty chords were played as default length. Don't know why this was introduced
This commit is contained in:
parent
c509b6ccc5
commit
532727b7d1
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- `{}` notation for concurrently executing blocks
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- release build script was producing executable with wrong path
|
- release build script was producing executable with wrong path
|
||||||
|
@ -7,8 +7,8 @@ hand1_pool := (
|
|||||||
|
|
||||||
hand2_pool := (d8, d#8, g8, g#8, d9, d#9),
|
hand2_pool := (d8, d#8, g8, g#8, d9, d#9),
|
||||||
|
|
||||||
concurrent
|
play {
|
||||||
(while true (play ((pick hand1_pool) (pick hn dhn))))
|
while true ((pick hand1_pool) (pick hn dhn)),
|
||||||
(while true (play ((pick hand2_pool) (1/64))))
|
while true ((pick hand2_pool) (1/64))
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -242,27 +242,27 @@ static auto builtin_program_change(Interpreter &i, std::vector<Value> args) -> R
|
|||||||
/// Plays sequentialy notes walking into arrays and evaluation blocks
|
/// Plays sequentialy notes walking into arrays and evaluation blocks
|
||||||
///
|
///
|
||||||
/// @invariant default_action is play one
|
/// @invariant default_action is play one
|
||||||
static inline std::optional<Error> sequential_play(Interpreter &i, Value v)
|
static inline std::optional<Error> sequential_play(Interpreter &interpreter, Value v)
|
||||||
{
|
{
|
||||||
if (auto array = get_if<Array>(v)) {
|
if (auto array = get_if<Array>(v)) {
|
||||||
for (auto &el : array->elements) {
|
for (auto &el : array->elements) {
|
||||||
Try(sequential_play(i, std::move(el)));
|
Try(sequential_play(interpreter, std::move(el)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (auto block = get_if<Block>(v)) {
|
else if (auto block = get_if<Block>(v)) {
|
||||||
Try(sequential_play(i, Try(i.eval(std::move(block->body)))));
|
Try(sequential_play(interpreter, Try(interpreter.eval(std::move(block->body)))));
|
||||||
}
|
}
|
||||||
else if (auto chord = get_if<Chord>(v)) {
|
else if (auto chord = get_if<Chord>(v)) {
|
||||||
return i.play(*chord);
|
return interpreter.play(*chord);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Play what's given
|
/// Play what's given
|
||||||
static std::optional<Error> action_play(Interpreter &i, Value v)
|
static std::optional<Error> action_play(Interpreter &interpreter, Value v)
|
||||||
{
|
{
|
||||||
Try(sequential_play(i, std::move(v)));
|
Try(sequential_play(interpreter, std::move(v)));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -602,38 +602,6 @@ static Result<Value> builtin_scan(Interpreter &interpreter, std::vector<Value> a
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result<Value> builtin_concurrent(Interpreter &interpreter, std::span<Ast> args)
|
|
||||||
{
|
|
||||||
auto const jobs_count = args.size();
|
|
||||||
std::vector<std::future<Value>> futures;
|
|
||||||
std::optional<Error> error;
|
|
||||||
std::mutex mutex;
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < jobs_count; ++i) {
|
|
||||||
futures.push_back(std::async(std::launch::async, [interpreter = interpreter.clone(), i, args, &mutex, &error]() mutable -> Value {
|
|
||||||
auto result = interpreter.eval((Ast)args[i]);
|
|
||||||
if (result) {
|
|
||||||
return *std::move(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::lock_guard guard{mutex};
|
|
||||||
if (!error) {
|
|
||||||
error = result.error();
|
|
||||||
}
|
|
||||||
return Value{};
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Value> results;
|
|
||||||
for (auto& future : futures) {
|
|
||||||
if (error) {
|
|
||||||
return *error;
|
|
||||||
}
|
|
||||||
results.push_back(future.get());
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Execute blocks depending on condition
|
/// Execute blocks depending on condition
|
||||||
static Result<Value> builtin_if(Interpreter &i, std::span<Ast> args) {
|
static Result<Value> builtin_if(Interpreter &i, std::span<Ast> args) {
|
||||||
static constexpr auto guard = Guard<2> {
|
static constexpr auto guard = Guard<2> {
|
||||||
@ -666,7 +634,7 @@ static Result<Value> builtin_if(Interpreter &i, std::span<Ast> args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Loop block depending on condition
|
/// Loop block depending on condition
|
||||||
static Result<Value> builtin_while(Interpreter &i, std::span<Ast> args) {
|
static Result<Value> builtin_while(Interpreter &interpreter, std::span<Ast> args) {
|
||||||
static constexpr auto guard = Guard<2> {
|
static constexpr auto guard = Guard<2> {
|
||||||
.name = "while",
|
.name = "while",
|
||||||
.possibilities = {
|
.possibilities = {
|
||||||
@ -678,11 +646,16 @@ static Result<Value> builtin_while(Interpreter &i, std::span<Ast> args) {
|
|||||||
return guard.yield_error();
|
return guard.yield_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
while (Try(i.eval((Ast)args.front())).truthy()) {
|
while (Try(interpreter.eval((Ast)args.front())).truthy()) {
|
||||||
|
Value result;
|
||||||
if (args[1].type == Ast::Type::Block) {
|
if (args[1].type == Ast::Type::Block) {
|
||||||
Try(i.eval((Ast)args[1].arguments.front()));
|
result = Try(interpreter.eval((Ast)args[1].arguments.front()));
|
||||||
} else {
|
} else {
|
||||||
Try(i.eval((Ast)args[1]));
|
result = Try(interpreter.eval((Ast)args[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interpreter.default_action) {
|
||||||
|
Try(interpreter.default_action(interpreter, std::move(result)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Value{};
|
return Value{};
|
||||||
@ -1151,7 +1124,6 @@ void Interpreter::register_builtin_functions()
|
|||||||
global.force_define("call", builtin_call);
|
global.force_define("call", builtin_call);
|
||||||
global.force_define("ceil", apply_numeric_transform<&Number::ceil>);
|
global.force_define("ceil", apply_numeric_transform<&Number::ceil>);
|
||||||
global.force_define("chord", builtin_chord);
|
global.force_define("chord", builtin_chord);
|
||||||
global.force_define("concurrent", builtin_concurrent);
|
|
||||||
global.force_define("down", builtin_range<Range_Direction::Down>);
|
global.force_define("down", builtin_range<Range_Direction::Down>);
|
||||||
global.force_define("duration", builtin_duration);
|
global.force_define("duration", builtin_duration);
|
||||||
global.force_define("flat", builtin_flat);
|
global.force_define("flat", builtin_flat);
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <future>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
midi::Connection *Interpreter::midi_connection = nullptr;
|
midi::Connection *Interpreter::midi_connection = nullptr;
|
||||||
std::unordered_map<std::string, Intrinsic> Interpreter::operators {};
|
std::unordered_map<std::string, Intrinsic> Interpreter::operators {};
|
||||||
@ -61,6 +63,50 @@ Interpreter::Interpreter(Interpreter::Clone)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Result<Value> eval_concurrent(Interpreter &interpreter, Ast &&ast)
|
||||||
|
{
|
||||||
|
ensure(ast.type == Ast::Type::Concurrent, "Only conccurent AST nodes can be evaluated in eval_concurrent");
|
||||||
|
|
||||||
|
std::span<Ast> jobs = ast.arguments;
|
||||||
|
std::vector<std::future<Value>> futures;
|
||||||
|
std::optional<Error> error;
|
||||||
|
std::mutex mutex;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < jobs.size(); ++i) {
|
||||||
|
futures.push_back(std::async(std::launch::async, [interpreter = interpreter.clone(), i, jobs, &mutex, &error]() mutable -> Value {
|
||||||
|
auto result = interpreter.eval(std::move(jobs[i]));
|
||||||
|
if (result) {
|
||||||
|
if (interpreter.default_action) {
|
||||||
|
// TODO Code duplication between this section and last section in this lambda function
|
||||||
|
if (auto produced_error = interpreter.default_action(interpreter, *std::move(result))) {
|
||||||
|
std::lock_guard guard{mutex};
|
||||||
|
if (!error) {
|
||||||
|
error = produced_error;
|
||||||
|
}
|
||||||
|
return Value{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *std::move(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard guard{mutex};
|
||||||
|
if (!error) {
|
||||||
|
error = result.error();
|
||||||
|
}
|
||||||
|
return Value{};
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Value> results;
|
||||||
|
for (auto& future : futures) {
|
||||||
|
if (error) {
|
||||||
|
return *error;
|
||||||
|
}
|
||||||
|
results.push_back(future.get());
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
Result<Value> Interpreter::eval(Ast &&ast)
|
Result<Value> Interpreter::eval(Ast &&ast)
|
||||||
{
|
{
|
||||||
switch (ast.type) {
|
switch (ast.type) {
|
||||||
@ -163,6 +209,9 @@ Result<Value> Interpreter::eval(Ast &&ast)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Ast::Type::Concurrent:
|
||||||
|
return eval_concurrent(*this, std::move(ast));
|
||||||
|
|
||||||
case Ast::Type::Sequence:
|
case Ast::Type::Sequence:
|
||||||
{
|
{
|
||||||
Value v;
|
Value v;
|
||||||
@ -243,7 +292,6 @@ std::optional<Error> Interpreter::play(Chord chord)
|
|||||||
auto &ctx = *current_context;
|
auto &ctx = *current_context;
|
||||||
|
|
||||||
if (chord.notes.size() == 0) {
|
if (chord.notes.size() == 0) {
|
||||||
std::this_thread::sleep_for(ctx.length_to_duration(ctx.length));
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,7 +348,9 @@ static void snapshot(std::ostream& out, Note const& note) {
|
|||||||
|
|
||||||
static void snapshot(std::ostream &out, Ast const& ast) {
|
static void snapshot(std::ostream &out, Ast const& ast) {
|
||||||
switch (ast.type) {
|
switch (ast.type) {
|
||||||
break; case Ast::Type::Sequence:
|
break;
|
||||||
|
case Ast::Type::Sequence:
|
||||||
|
case Ast::Type::Concurrent:
|
||||||
{
|
{
|
||||||
for (auto const& a : ast.arguments) {
|
for (auto const& a : ast.arguments) {
|
||||||
snapshot(out, a);
|
snapshot(out, a);
|
||||||
@ -308,7 +358,10 @@ static void snapshot(std::ostream &out, Ast const& ast) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break; case Ast::Type::Block:
|
break; case Ast::Type::Block:
|
||||||
|
// TODO
|
||||||
ensure(ast.arguments.size() == 1, "Block can contain only one node which contains its body");
|
ensure(ast.arguments.size() == 1, "Block can contain only one node which contains its body");
|
||||||
|
if (ast.arguments.front().type == Ast::Type::Concurrent)
|
||||||
|
unimplemented("Concurrent code snapshoting is not supported yet");
|
||||||
out << "(";
|
out << "(";
|
||||||
snapshot(out, ast.arguments.front());
|
snapshot(out, ast.arguments.front());
|
||||||
out << ")";
|
out << ")";
|
||||||
|
@ -88,8 +88,10 @@ auto Lexer::next_token() -> Result<std::variant<Token, End_Of_File>>
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (peek()) {
|
switch (peek()) {
|
||||||
case '(': consume(); return Token { Token::Type::Open_Block, finish(), token_location };
|
case '(': consume(); return Token { Token::Type::Open_Sequential, finish(), token_location };
|
||||||
case ')': consume(); return Token { Token::Type::Close_Block, finish(), token_location };
|
case ')': consume(); return Token { Token::Type::Close_Sequential, finish(), token_location };
|
||||||
|
case '{': consume(); return Token { Token::Type::Open_Concurrent, finish(), token_location };
|
||||||
|
case '}': consume(); return Token { Token::Type::Close_Concurrent, finish(), token_location };
|
||||||
case '[': consume(); return Token { Token::Type::Open_Index, finish(), token_location };
|
case '[': consume(); return Token { Token::Type::Open_Index, finish(), token_location };
|
||||||
case ']': consume(); return Token { Token::Type::Close_Index, finish(), token_location };
|
case ']': consume(); return Token { Token::Type::Close_Index, finish(), token_location };
|
||||||
case ',': consume(); return Token { Token::Type::Expression_Separator, finish(), token_location };
|
case ',': consume(); return Token { Token::Type::Expression_Separator, finish(), token_location };
|
||||||
@ -257,16 +259,18 @@ std::ostream& operator<<(std::ostream& os, Token::Type type)
|
|||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Token::Type::Chord: return os << "CHORD";
|
case Token::Type::Chord: return os << "CHORD";
|
||||||
case Token::Type::Close_Block: return os << "CLOSE BLOCK";
|
case Token::Type::Close_Concurrent: return os << "CLOSE CONCURRENT";
|
||||||
|
case Token::Type::Close_Index: return os << "CLOSE INDEX";
|
||||||
|
case Token::Type::Close_Sequential: return os << "CLOSE SEQUENTIAL";
|
||||||
case Token::Type::Expression_Separator: return os << "EXPRESSION SEPARATOR";
|
case Token::Type::Expression_Separator: return os << "EXPRESSION SEPARATOR";
|
||||||
case Token::Type::Keyword: return os << "KEYWORD";
|
case Token::Type::Keyword: return os << "KEYWORD";
|
||||||
case Token::Type::Numeric: return os << "NUMERIC";
|
case Token::Type::Numeric: return os << "NUMERIC";
|
||||||
case Token::Type::Open_Block: return os << "OPEN BLOCK";
|
case Token::Type::Open_Concurrent: return os << "OPEN CONCURRENT";
|
||||||
|
case Token::Type::Open_Index: return os << "OPEN INDEX";
|
||||||
|
case Token::Type::Open_Sequential: return os << "OPEN SEQUENTIAL";
|
||||||
case Token::Type::Operator: return os << "OPERATOR";
|
case Token::Type::Operator: return os << "OPERATOR";
|
||||||
case Token::Type::Parameter_Separator: return os << "PARAMETER SEPARATOR";
|
case Token::Type::Parameter_Separator: return os << "PARAMETER SEPARATOR";
|
||||||
case Token::Type::Symbol: return os << "SYMBOL";
|
case Token::Type::Symbol: return os << "SYMBOL";
|
||||||
case Token::Type::Open_Index: return os << "OPEN INDEX";
|
|
||||||
case Token::Type::Close_Index: return os << "CLOSE INDEX";
|
|
||||||
}
|
}
|
||||||
unreachable();
|
unreachable();
|
||||||
}
|
}
|
||||||
@ -275,13 +279,15 @@ std::string_view type_name(Token::Type type)
|
|||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Token::Type::Chord: return "chord";
|
case Token::Type::Chord: return "chord";
|
||||||
case Token::Type::Close_Block: return ")";
|
case Token::Type::Close_Concurrent: return "}";
|
||||||
case Token::Type::Close_Index: return "]";
|
case Token::Type::Close_Index: return "]";
|
||||||
|
case Token::Type::Close_Sequential: return ")";
|
||||||
case Token::Type::Expression_Separator: return "|";
|
case Token::Type::Expression_Separator: return "|";
|
||||||
case Token::Type::Keyword: return "keyword";
|
case Token::Type::Keyword: return "keyword";
|
||||||
case Token::Type::Numeric: return "numeric";
|
case Token::Type::Numeric: return "numeric";
|
||||||
case Token::Type::Open_Block: return "(";
|
case Token::Type::Open_Concurrent: return "{";
|
||||||
case Token::Type::Open_Index: return "[";
|
case Token::Type::Open_Index: return "[";
|
||||||
|
case Token::Type::Open_Sequential: return "(";
|
||||||
case Token::Type::Operator: return "operator";
|
case Token::Type::Operator: return "operator";
|
||||||
case Token::Type::Parameter_Separator: return "parameter separator";
|
case Token::Type::Parameter_Separator: return "parameter separator";
|
||||||
case Token::Type::Symbol: return "symbol";
|
case Token::Type::Symbol: return "symbol";
|
||||||
|
@ -17,10 +17,12 @@ struct Token
|
|||||||
Numeric, ///< numeric literal (floating point or integer)
|
Numeric, ///< numeric literal (floating point or integer)
|
||||||
Parameter_Separator, ///< "|" separaters arguments from block body
|
Parameter_Separator, ///< "|" separaters arguments from block body
|
||||||
Expression_Separator, ///< "," separates expressions. Used mainly to separate calls, like `foo 1 2; bar 3 4`
|
Expression_Separator, ///< "," separates expressions. Used mainly to separate calls, like `foo 1 2; bar 3 4`
|
||||||
Open_Block, ///< "(" starts anonymous block of code (potentially a function)
|
|
||||||
Close_Block, ///< ")" ends anonymous block of code (potentially a function)
|
|
||||||
Open_Index, ///< "[" starts index section of index expression
|
Open_Index, ///< "[" starts index section of index expression
|
||||||
Close_Index ///< "]" ends index section of index expression
|
Close_Index, ///< "]" ends index section of index expression
|
||||||
|
Open_Sequential, ///< "(" starts anonymous sequential block of code (potentially a function)
|
||||||
|
Close_Sequential, ///< ")" ends anonymous sequential block of code (potentially a function)
|
||||||
|
Open_Concurrent, ///< "{" starts anonymous concurrent block
|
||||||
|
Close_Concurrent, ///< "}" ends anonymous concurrent block
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Type of token
|
/// Type of token
|
||||||
|
@ -12,19 +12,22 @@ struct Ast
|
|||||||
static Ast binary(Token, Ast lhs, Ast rhs);
|
static Ast binary(Token, Ast lhs, Ast rhs);
|
||||||
|
|
||||||
/// Constructs block
|
/// Constructs block
|
||||||
static Ast block(Location location, Ast seq = sequence({}));
|
static Ast block(Location location, Ast seq);
|
||||||
|
|
||||||
/// Constructs call expression
|
/// Constructs call expression
|
||||||
static Ast call(std::vector<Ast> call);
|
static Ast call(std::vector<Ast> call);
|
||||||
|
|
||||||
/// Constructs block with parameters
|
/// Constructs block with parameters
|
||||||
static Ast lambda(Location location, Ast seq = sequence({}), std::vector<Ast> parameters = {});
|
static Ast lambda(Location location, Ast body = sequential({}), std::vector<Ast> parameters = {});
|
||||||
|
|
||||||
/// Constructs constants, literals and variable identifiers
|
/// Constructs constants, literals and variable identifiers
|
||||||
static Ast literal(Token);
|
static Ast literal(Token);
|
||||||
|
|
||||||
/// Constructs sequence of operations
|
/// Constructs sequence of operations
|
||||||
static Ast sequence(std::vector<Ast> call);
|
static Ast sequential(std::vector<Ast> call);
|
||||||
|
|
||||||
|
/// Constructs concurrent collection of operations
|
||||||
|
static Ast concurrent(std::vector<Ast> ops);
|
||||||
|
|
||||||
/// Constructs variable declaration
|
/// Constructs variable declaration
|
||||||
static Ast variable_declaration(Location loc, std::vector<Ast> lvalues, std::optional<Ast> rvalue);
|
static Ast variable_declaration(Location loc, std::vector<Ast> lvalues, std::optional<Ast> rvalue);
|
||||||
@ -38,6 +41,7 @@ struct Ast
|
|||||||
Call, ///< Function call application like `print 42`
|
Call, ///< Function call application like `print 42`
|
||||||
Literal, ///< Compile time known constant like `c` or `1`
|
Literal, ///< Compile time known constant like `c` or `1`
|
||||||
Sequence, ///< Several expressions sequences like `42`, `42; 32`
|
Sequence, ///< Several expressions sequences like `42`, `42; 32`
|
||||||
|
Concurrent, ///< Conccurrent collection of expressions like inside {} block
|
||||||
Variable_Declaration, ///< Declaration of a variable with optional value assigment like `var x = 10` or `var y`
|
Variable_Declaration, ///< Declaration of a variable with optional value assigment like `var x = 10` or `var y`
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -65,10 +65,11 @@ Result<Ast> Parser::parse(std::string_view source, std::string_view filename, un
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const result = parser.parse_sequence();
|
auto const result = parser.parse_sequence(true);
|
||||||
|
|
||||||
if (result.has_value() && parser.token_id < parser.tokens.size()) {
|
if (result.has_value() && parser.token_id < parser.tokens.size()) {
|
||||||
if (parser.expect(Token::Type::Close_Block)) {
|
// FIXME There should be also check for closing index ] and closing concurrent block }
|
||||||
|
if (parser.expect(Token::Type::Close_Sequential)) {
|
||||||
auto const tok = parser.consume();
|
auto const tok = parser.consume();
|
||||||
return Error {
|
return Error {
|
||||||
.details = errors::Closing_Token_Without_Opening {
|
.details = errors::Closing_Token_Without_Opening {
|
||||||
@ -84,10 +85,10 @@ Result<Ast> Parser::parse(std::string_view source, std::string_view filename, un
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<Ast> Parser::parse_sequence()
|
Result<Ast> Parser::parse_sequence(bool is_sequential)
|
||||||
{
|
{
|
||||||
auto seq = Try(parse_many(*this, &Parser::parse_expression, Token::Type::Expression_Separator, At_Least::Zero));
|
auto seq = Try(parse_many(*this, &Parser::parse_expression, Token::Type::Expression_Separator, At_Least::Zero));
|
||||||
return Ast::sequence(std::move(seq));
|
return is_sequential ? Ast::sequential(std::move(seq)) : Ast::concurrent(std::move(seq));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<Ast> Parser::parse_expression()
|
Result<Ast> Parser::parse_expression()
|
||||||
@ -185,10 +186,11 @@ Result<Ast> parse_sequence_inside(
|
|||||||
Location start_location,
|
Location start_location,
|
||||||
bool is_lambda,
|
bool is_lambda,
|
||||||
std::vector<Ast> &¶meters,
|
std::vector<Ast> &¶meters,
|
||||||
auto &&dont_arrived_at_closing_token
|
auto &&dont_arrived_at_closing_token,
|
||||||
|
bool is_sequential
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
auto ast = Try(parser.parse_sequence());
|
auto ast = Try(parser.parse_sequence(is_sequential));
|
||||||
if (not parser.expect(closing_token)) {
|
if (not parser.expect(closing_token)) {
|
||||||
Try(dont_arrived_at_closing_token());
|
Try(dont_arrived_at_closing_token());
|
||||||
return Error {
|
return Error {
|
||||||
@ -206,7 +208,7 @@ Result<Ast> parse_sequence_inside(
|
|||||||
return Ast::lambda(start_location, std::move(ast), std::move(parameters));
|
return Ast::lambda(start_location, std::move(ast), std::move(parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure(ast.type == Ast::Type::Sequence, "I dunno if this is a valid assumption tbh");
|
ensure(ast.type == Ast::Type::Sequence || ast.type == Ast::Type::Concurrent, "I dunno if this is a valid assumption tbh");
|
||||||
if (ast.arguments.size() == 1) {
|
if (ast.arguments.size() == 1) {
|
||||||
return std::move(ast.arguments.front());
|
return std::move(ast.arguments.front());
|
||||||
}
|
}
|
||||||
@ -231,7 +233,8 @@ Result<Ast> Parser::parse_index_expression()
|
|||||||
{},
|
{},
|
||||||
[]() -> std::optional<Error> {
|
[]() -> std::optional<Error> {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
},
|
||||||
|
true
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -258,12 +261,16 @@ Result<Ast> Parser::parse_atomic_expression()
|
|||||||
case Token::Type::Symbol:
|
case Token::Type::Symbol:
|
||||||
return Ast::literal(consume());
|
return Ast::literal(consume());
|
||||||
|
|
||||||
case Token::Type::Open_Block:
|
case Token::Type::Open_Sequential:
|
||||||
|
case Token::Type::Open_Concurrent:
|
||||||
{
|
{
|
||||||
auto opening = consume();
|
auto const opening = consume();
|
||||||
if (expect(Token::Type::Close_Block)) {
|
bool const is_sequential = opening.type == Token::Type::Open_Sequential;
|
||||||
|
auto const closing_token_type = is_sequential ? Token::Type::Close_Sequential : Token::Type::Close_Concurrent;
|
||||||
|
|
||||||
|
if (expect(closing_token_type)) {
|
||||||
consume();
|
consume();
|
||||||
return Ast::block(std::move(opening).location);
|
return Ast::block(std::move(opening).location, is_sequential ? Ast::sequential({}) : Ast::concurrent({}));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto start = token_id;
|
auto start = token_id;
|
||||||
@ -286,7 +293,7 @@ Result<Ast> Parser::parse_atomic_expression()
|
|||||||
|
|
||||||
return parse_sequence_inside(
|
return parse_sequence_inside(
|
||||||
*this,
|
*this,
|
||||||
Token::Type::Close_Block,
|
closing_token_type,
|
||||||
opening.location,
|
opening.location,
|
||||||
is_lambda,
|
is_lambda,
|
||||||
std::move(parameters),
|
std::move(parameters),
|
||||||
@ -321,7 +328,8 @@ Result<Ast> Parser::parse_atomic_expression()
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
},
|
||||||
|
is_sequential
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -490,7 +498,7 @@ Ast Ast::call(std::vector<Ast> call)
|
|||||||
return ast;
|
return ast;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast Ast::sequence(std::vector<Ast> expressions)
|
Ast Ast::sequential(std::vector<Ast> expressions)
|
||||||
{
|
{
|
||||||
Ast ast;
|
Ast ast;
|
||||||
ast.type = Type::Sequence;
|
ast.type = Type::Sequence;
|
||||||
@ -501,6 +509,17 @@ Ast Ast::sequence(std::vector<Ast> expressions)
|
|||||||
return ast;
|
return ast;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ast Ast::concurrent(std::vector<Ast> expressions)
|
||||||
|
{
|
||||||
|
Ast ast;
|
||||||
|
ast.type = Type::Concurrent;
|
||||||
|
if (!expressions.empty()) {
|
||||||
|
ast.location = expressions.front().location;
|
||||||
|
ast.arguments = std::move(expressions);
|
||||||
|
}
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
|
||||||
Ast Ast::block(Location location, Ast seq)
|
Ast Ast::block(Location location, Ast seq)
|
||||||
{
|
{
|
||||||
Ast ast;
|
Ast ast;
|
||||||
@ -586,6 +605,10 @@ bool operator==(Ast const& lhs, Ast const& rhs)
|
|||||||
case Ast::Type::Lambda:
|
case Ast::Type::Lambda:
|
||||||
case Ast::Type::Sequence:
|
case Ast::Type::Sequence:
|
||||||
case Ast::Type::Variable_Declaration:
|
case Ast::Type::Variable_Declaration:
|
||||||
|
// TODO Maybe concurrent blocks should resolve duplicates at AST level, and not execution level?
|
||||||
|
// Statements { play c, play c } should be parse time known to be { play c } I think.
|
||||||
|
// Anyway thing to reconsider later
|
||||||
|
case Ast::Type::Concurrent:
|
||||||
return lhs.arguments.size() == rhs.arguments.size()
|
return lhs.arguments.size() == rhs.arguments.size()
|
||||||
&& std::equal(lhs.arguments.begin(), lhs.arguments.end(), rhs.arguments.begin());
|
&& std::equal(lhs.arguments.begin(), lhs.arguments.end(), rhs.arguments.begin());
|
||||||
}
|
}
|
||||||
@ -603,6 +626,7 @@ std::ostream& operator<<(std::ostream& os, Ast::Type type)
|
|||||||
case Ast::Type::Literal: return os << "LITERAL";
|
case Ast::Type::Literal: return os << "LITERAL";
|
||||||
case Ast::Type::Sequence: return os << "SEQUENCE";
|
case Ast::Type::Sequence: return os << "SEQUENCE";
|
||||||
case Ast::Type::Variable_Declaration: return os << "VAR";
|
case Ast::Type::Variable_Declaration: return os << "VAR";
|
||||||
|
case Ast::Type::Concurrent: return os << "CONCURRENT";
|
||||||
}
|
}
|
||||||
unreachable();
|
unreachable();
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ struct Parser
|
|||||||
static Result<Ast> parse(std::string_view source, std::string_view filename, unsigned line_number = 0);
|
static Result<Ast> parse(std::string_view source, std::string_view filename, unsigned line_number = 0);
|
||||||
|
|
||||||
/// Parse sequence, collection of expressions
|
/// Parse sequence, collection of expressions
|
||||||
Result<Ast> parse_sequence();
|
Result<Ast> parse_sequence(bool is_sequential);
|
||||||
|
|
||||||
/// Parse either infix expression or variable declaration
|
/// Parse either infix expression or variable declaration
|
||||||
Result<Ast> parse_expression();
|
Result<Ast> parse_expression();
|
||||||
|
@ -14,8 +14,6 @@ struct Value;
|
|||||||
/// Lazy Array / Continuation / Closure type thingy
|
/// Lazy Array / Continuation / Closure type thingy
|
||||||
struct Block : Collection, Function
|
struct Block : Collection, Function
|
||||||
{
|
{
|
||||||
~Block() override = default;
|
|
||||||
|
|
||||||
/// Location of definition / creation
|
/// Location of definition / creation
|
||||||
Location location;
|
Location location;
|
||||||
|
|
||||||
@ -28,6 +26,8 @@ struct Block : Collection, Function
|
|||||||
/// Context from which block was created. Used for closures
|
/// Context from which block was created. Used for closures
|
||||||
std::shared_ptr<Env> context;
|
std::shared_ptr<Env> context;
|
||||||
|
|
||||||
|
~Block() override = default;
|
||||||
|
|
||||||
/// Calling block
|
/// Calling block
|
||||||
Result<Value> operator()(Interpreter &i, std::vector<Value> params) const override;
|
Result<Value> operator()(Interpreter &i, std::vector<Value> params) const override;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user