switched index operation from 'a.n' to 'a[n]'

This commit is contained in:
Robert Bendun 2022-10-31 00:31:47 +01:00
parent 0e515c727b
commit bfbc82d6a5
9 changed files with 110 additions and 73 deletions

View File

@ -1,5 +1,5 @@
map := (fn array | result := (), for array (v | result = result & (| fn v) ), result),
drop := (n arr | arr.(range n (len arr))),
take := (n arr | arr.(up n)),
filter := (predicate arr | arr.(map predicate arr)),
drop := (n arr | arr[range n (len arr)]),
take := (n arr | arr[up n]),
filter := (predicate arr | arr[map predicate arr]),

View File

@ -1,5 +1,5 @@
for (up 10) (n |
snd := c + (shuffle (up 12)).0,
snd := c + (pick (up 12)),
o := if (n % 2 == 0) 3 4,
say snd o,
play (oct o, snd qn),

View File

@ -36,5 +36,5 @@ oct 3,
len (1/16),
for (2 + up Length) ( i |
play (chord scale.(indicies.(i % primes == 0))),
play (chord scale[indicies[i % primes == 0]]),
),

View File

@ -1,10 +1,6 @@
#ifndef MUSIQUE_ERRORS_HH
#define MUSIQUE_ERRORS_HH
#if defined(__cpp_lib_source_location)
#include <source_location>
#endif
#include <optional>
#include <system_error>
#include <variant>

View File

@ -306,11 +306,11 @@ static constexpr auto Operators = std::array {
Operator_Entry { ">", builtin_operator_compare<std::greater<>> },
Operator_Entry { ">=", builtin_operator_compare<std::greater_equal<>> },
Operator_Entry { ".", builtin_operator_index },
Operator_Entry { "[", builtin_operator_index },
Operator_Entry { "&", builtin_operator_join },
};
// All operators should be defined here except 'and' and 'or' which handle evaluation differently
// All operators should be defined here except '=', 'and' and 'or' which handle evaluation differently
// and are need unevaluated expressions for their proper evaluation. Exclusion of them is marked
// as subtraction of total excluded operators from expected constant
static_assert(Operators.size() == Operators_Count - 3, "All operators handlers are defined here");

View File

@ -90,6 +90,8 @@ auto Lexer::next_token() -> Result<std::variant<Token, End_Of_File>>
switch (peek()) {
case '(': consume(); return Token { Token::Type::Open_Block, finish(), token_location };
case ')': consume(); return Token { Token::Type::Close_Block, 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::Expression_Separator, finish(), token_location };
case '|':
@ -263,6 +265,8 @@ std::ostream& operator<<(std::ostream& os, Token::Type type)
case Token::Type::Operator: return os << "OPERATOR";
case Token::Type::Parameter_Separator: return os << "PARAMETER SEPARATOR";
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();
}
@ -272,10 +276,12 @@ std::string_view type_name(Token::Type type)
switch (type) {
case Token::Type::Chord: return "chord";
case Token::Type::Close_Block: return ")";
case Token::Type::Close_Index: return "]";
case Token::Type::Expression_Separator: return "|";
case Token::Type::Keyword: return "keyword";
case Token::Type::Numeric: return "numeric";
case Token::Type::Open_Block: return "(";
case Token::Type::Open_Index: return "[";
case Token::Type::Operator: return "operator";
case Token::Type::Parameter_Separator: return "parameter separator";
case Token::Type::Symbol: return "symbol";

View File

@ -13,12 +13,14 @@ struct Token
Symbol, ///< like repeat or choose or chord
Keyword, ///< like true, false, nil
Operator, ///< like "+", "-", "++", "<"
Chord, ///< chord or single note literal, like "c125"
Chord, ///< chord or single note literal, like "c4"
Numeric, ///< numeric literal (floating point or integer)
Parameter_Separator, ///< "|" separaters arguments from block body
Expression_Separator, ///< ";" separates expressions. Used mainly to separate calls, like `foo 1 2; bar 3 4`
Open_Block, ///< "[" delimits anonymous block of code (potentially a function)
Close_Block, ///< "]" delimits anonymous block of code (potentially a function)
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
Close_Index ///< "]" ends index section of index expression
};
/// Type of token

View File

@ -1,6 +1,10 @@
#ifndef MUSIQUE_LOCATION_HH
#define MUSIQUE_LOCATION_HH
#if defined(__cpp_lib_source_location)
#include <source_location>
#endif
#include <musique/common.hh>
#include <ostream>

View File

@ -131,7 +131,6 @@ Result<Ast> Parser::parse_infix_expression()
|| expect(Token::Type::Keyword, "or");
if (next_is_operator) {
ensure(not expect(Token::Type::Operator, "."), "This should be handled by parse_index_expression");
auto op = consume();
Ast ast;
@ -159,7 +158,6 @@ Result<Ast> Parser::parse_rhs_of_infix_expression(Ast lhs)
return lhs;
}
ensure(not expect(Token::Type::Operator, "."), "This should be handled by parse_index_expression");
auto op = consume();
if (precedense(lhs.token.source) >= precedense(op.source)) {
@ -181,18 +179,61 @@ Result<Ast> Parser::parse_rhs_of_infix_expression(Ast lhs)
return lhs;
}
Result<Ast> parse_sequence_inside(
Parser &parser,
Token::Type closing_token,
Location start_location,
bool is_lambda,
std::vector<Ast> &&parameters,
auto &&dont_arrived_at_closing_token
)
{
auto ast = Try(parser.parse_sequence());
if (not parser.expect(closing_token)) {
Try(dont_arrived_at_closing_token());
return Error {
.details = errors::Unexpected_Empty_Source {
.reason = errors::Unexpected_Empty_Source::Block_Without_Closing_Bracket,
.start = start_location
},
.location = parser.tokens.back().location
};
}
parser.consume();
if (is_lambda) {
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");
if (ast.arguments.size() == 1) {
return std::move(ast.arguments.front());
}
return Ast::block(start_location, std::move(ast));
}
Result<Ast> Parser::parse_index_expression()
{
auto result = Try(parse_atomic_expression());
while (expect(Token::Type::Operator, ".")) {
while (expect(Token::Type::Open_Index)) {
auto op = consume();
if (auto maybe_index = parse_atomic_expression(); maybe_index.has_value()) {
result = Ast::binary(std::move(op), std::move(result), *std::move(maybe_index));
} else {
// TODO Report that error occured during parsing of index expression
return std::move(maybe_index).error();
}
auto start_location = op.location;
result = Ast::binary(
std::move(op),
std::move(result),
Try(parse_sequence_inside(
*this,
Token::Type::Close_Index,
start_location,
false,
{},
[]() -> std::optional<Error> {
return std::nullopt;
}
))
);
}
return result;
@ -243,55 +284,45 @@ Result<Ast> Parser::parse_atomic_expression()
}
}
return parse_sequence().and_then([&](Ast &&ast) -> Result<Ast> {
if (not expect(Token::Type::Close_Block)) {
if (expect(Token::Type::Parameter_Separator)) {
if (is_lambda) {
ensure(false, "There should be error message that you cannot put multiple parameter separators in one block");
} else {
// This may be a result of user trying to specify parameters from things that cannot be parameters (like chord literals)
// or accidential hit of "|" on the keyboard. We can detect first case by ensuring that all tokens between this place
// and beggining of a block are identifier looking: keywords, symbols, literal chord declarations, boolean literals etc
std::optional<Token> invalid_token = std::nullopt;
auto const success = std::all_of(tokens.begin() + start, tokens.begin() + (token_id - start + 1), [&](Token const& token) {
if (!invalid_token && token.type != Token::Type::Symbol) {
// TODO Maybe gather all tokens to provide all the help needed in the one iteration
invalid_token = token;
}
return is_identifier_looking(token.type);
});
return parse_sequence_inside(
*this,
Token::Type::Close_Block,
opening.location,
is_lambda,
std::move(parameters),
[&]() -> std::optional<Error> {
if (!expect(Token::Type::Parameter_Separator)) {
return std::nullopt;
}
if (is_lambda) {
ensure(false, "There should be error message that you cannot put multiple parameter separators in one block");
}
if (success && invalid_token) {
return Error {
.details = errors::Literal_As_Identifier {
.type_name = type_name(invalid_token->type),
.source = invalid_token->source,
.context = "block parameter list"
},
.location = invalid_token->location
};
}
// This may be a result of user trying to specify parameters from things that cannot be parameters (like chord literals)
// or accidential hit of "|" on the keyboard. We can detect first case by ensuring that all tokens between this place
// and beggining of a block are identifier looking: keywords, symbols, literal chord declarations, boolean literals etc
std::optional<Token> invalid_token = std::nullopt;
auto const success = std::all_of(tokens.begin() + start, tokens.begin() + (token_id - start + 1), [&](Token const& token) {
if (!invalid_token && token.type != Token::Type::Symbol) {
// TODO Maybe gather all tokens to provide all the help needed in the one iteration
invalid_token = token;
}
return is_identifier_looking(token.type);
});
if (success && invalid_token) {
return Error {
.details = errors::Literal_As_Identifier {
.type_name = type_name(invalid_token->type),
.source = invalid_token->source,
.context = "block parameter list"
},
.location = invalid_token->location
};
}
return Error {
.details = errors::Unexpected_Empty_Source {
.reason = errors::Unexpected_Empty_Source::Block_Without_Closing_Bracket,
.start = opening.location
},
.location = tokens.back().location
};
return std::nullopt;
}
consume();
if (is_lambda) {
return Ast::lambda(opening.location, std::move(ast), std::move(parameters));
} else {
ensure(ast.type == Ast::Type::Sequence, "I dunno if this is a valid assumption tbh");
if (ast.arguments.size() == 1) {
return std::move(ast.arguments.front());
}
return Ast::block(opening.location, std::move(ast));
}
});
);
}
break; case Token::Type::Operator:
@ -516,7 +547,8 @@ constexpr bool one_of(std::string_view id, auto const& ...args)
static usize precedense(std::string_view op)
{
// Operators that are not included below are
// '.' since it have own precedense rules and is not binary expression but its own kind of expression
// postfix index operator [] since it have own precedense rules and is not binary expression but its own kind of expression
// in terms of our parsing function hierarchy
//
// Exclusion of them is marked by subtracting total number of excluded operators.
static_assert(Operators_Count - 1 == 16, "Ensure that all operators have defined precedense below");
@ -530,9 +562,6 @@ static usize precedense(std::string_view op)
if (one_of(op, "*", "/", "%", "&")) return 400;
if (one_of(op, "**")) return 500;
std::cerr << op << std::endl;
unreachable();
}