switched index operation from 'a.n' to 'a[n]'
This commit is contained in:
parent
0e515c727b
commit
bfbc82d6a5
@ -1,5 +1,5 @@
|
|||||||
map := (fn array | result := (), for array (v | result = result & (| fn v) ), result),
|
map := (fn array | result := (), for array (v | result = result & (| fn v) ), result),
|
||||||
drop := (n arr | arr.(range n (len arr))),
|
drop := (n arr | arr[range n (len arr)]),
|
||||||
take := (n arr | arr.(up n)),
|
take := (n arr | arr[up n]),
|
||||||
filter := (predicate arr | arr.(map predicate arr)),
|
filter := (predicate arr | arr[map predicate arr]),
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
for (up 10) (n |
|
for (up 10) (n |
|
||||||
snd := c + (shuffle (up 12)).0,
|
snd := c + (pick (up 12)),
|
||||||
o := if (n % 2 == 0) 3 4,
|
o := if (n % 2 == 0) 3 4,
|
||||||
say snd o,
|
say snd o,
|
||||||
play (oct o, snd qn),
|
play (oct o, snd qn),
|
||||||
|
@ -36,5 +36,5 @@ oct 3,
|
|||||||
len (1/16),
|
len (1/16),
|
||||||
|
|
||||||
for (2 + up Length) ( i |
|
for (2 + up Length) ( i |
|
||||||
play (chord scale.(indicies.(i % primes == 0))),
|
play (chord scale[indicies[i % primes == 0]]),
|
||||||
),
|
),
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
#ifndef MUSIQUE_ERRORS_HH
|
#ifndef MUSIQUE_ERRORS_HH
|
||||||
#define MUSIQUE_ERRORS_HH
|
#define MUSIQUE_ERRORS_HH
|
||||||
|
|
||||||
#if defined(__cpp_lib_source_location)
|
|
||||||
#include <source_location>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
@ -306,11 +306,11 @@ static constexpr auto Operators = std::array {
|
|||||||
Operator_Entry { ">", builtin_operator_compare<std::greater<>> },
|
Operator_Entry { ">", builtin_operator_compare<std::greater<>> },
|
||||||
Operator_Entry { ">=", builtin_operator_compare<std::greater_equal<>> },
|
Operator_Entry { ">=", builtin_operator_compare<std::greater_equal<>> },
|
||||||
|
|
||||||
Operator_Entry { ".", builtin_operator_index },
|
Operator_Entry { "[", builtin_operator_index },
|
||||||
Operator_Entry { "&", builtin_operator_join },
|
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
|
// and are need unevaluated expressions for their proper evaluation. Exclusion of them is marked
|
||||||
// as subtraction of total excluded operators from expected constant
|
// as subtraction of total excluded operators from expected constant
|
||||||
static_assert(Operators.size() == Operators_Count - 3, "All operators handlers are defined here");
|
static_assert(Operators.size() == Operators_Count - 3, "All operators handlers are defined here");
|
||||||
|
@ -90,6 +90,8 @@ 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_Block, finish(), token_location };
|
||||||
case ')': consume(); return Token { Token::Type::Close_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 ',': consume(); return Token { Token::Type::Expression_Separator, finish(), token_location };
|
||||||
|
|
||||||
case '|':
|
case '|':
|
||||||
@ -263,6 +265,8 @@ std::ostream& operator<<(std::ostream& os, Token::Type type)
|
|||||||
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();
|
||||||
}
|
}
|
||||||
@ -272,10 +276,12 @@ 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_Block: return ")";
|
||||||
|
case Token::Type::Close_Index: 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_Block: return "(";
|
||||||
|
case Token::Type::Open_Index: 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";
|
||||||
|
@ -13,12 +13,14 @@ struct Token
|
|||||||
Symbol, ///< like repeat or choose or chord
|
Symbol, ///< like repeat or choose or chord
|
||||||
Keyword, ///< like true, false, nil
|
Keyword, ///< like true, false, nil
|
||||||
Operator, ///< like "+", "-", "++", "<"
|
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)
|
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, ///< "[" delimits anonymous block of code (potentially a function)
|
Open_Block, ///< "(" starts anonymous block of code (potentially a function)
|
||||||
Close_Block, ///< "]" delimits 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
|
/// Type of token
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
#ifndef MUSIQUE_LOCATION_HH
|
#ifndef MUSIQUE_LOCATION_HH
|
||||||
#define MUSIQUE_LOCATION_HH
|
#define MUSIQUE_LOCATION_HH
|
||||||
|
|
||||||
|
#if defined(__cpp_lib_source_location)
|
||||||
|
#include <source_location>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <musique/common.hh>
|
#include <musique/common.hh>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
|
||||||
|
@ -131,7 +131,6 @@ Result<Ast> Parser::parse_infix_expression()
|
|||||||
|| expect(Token::Type::Keyword, "or");
|
|| expect(Token::Type::Keyword, "or");
|
||||||
|
|
||||||
if (next_is_operator) {
|
if (next_is_operator) {
|
||||||
ensure(not expect(Token::Type::Operator, "."), "This should be handled by parse_index_expression");
|
|
||||||
auto op = consume();
|
auto op = consume();
|
||||||
|
|
||||||
Ast ast;
|
Ast ast;
|
||||||
@ -159,7 +158,6 @@ Result<Ast> Parser::parse_rhs_of_infix_expression(Ast lhs)
|
|||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure(not expect(Token::Type::Operator, "."), "This should be handled by parse_index_expression");
|
|
||||||
auto op = consume();
|
auto op = consume();
|
||||||
|
|
||||||
if (precedense(lhs.token.source) >= precedense(op.source)) {
|
if (precedense(lhs.token.source) >= precedense(op.source)) {
|
||||||
@ -181,18 +179,61 @@ Result<Ast> Parser::parse_rhs_of_infix_expression(Ast lhs)
|
|||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<Ast> parse_sequence_inside(
|
||||||
|
Parser &parser,
|
||||||
|
Token::Type closing_token,
|
||||||
|
Location start_location,
|
||||||
|
bool is_lambda,
|
||||||
|
std::vector<Ast> &¶meters,
|
||||||
|
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()
|
Result<Ast> Parser::parse_index_expression()
|
||||||
{
|
{
|
||||||
auto result = Try(parse_atomic_expression());
|
auto result = Try(parse_atomic_expression());
|
||||||
|
|
||||||
while (expect(Token::Type::Operator, ".")) {
|
while (expect(Token::Type::Open_Index)) {
|
||||||
auto op = consume();
|
auto op = consume();
|
||||||
if (auto maybe_index = parse_atomic_expression(); maybe_index.has_value()) {
|
auto start_location = op.location;
|
||||||
result = Ast::binary(std::move(op), std::move(result), *std::move(maybe_index));
|
result = Ast::binary(
|
||||||
} else {
|
std::move(op),
|
||||||
// TODO Report that error occured during parsing of index expression
|
std::move(result),
|
||||||
return std::move(maybe_index).error();
|
Try(parse_sequence_inside(
|
||||||
}
|
*this,
|
||||||
|
Token::Type::Close_Index,
|
||||||
|
start_location,
|
||||||
|
false,
|
||||||
|
{},
|
||||||
|
[]() -> std::optional<Error> {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -243,55 +284,45 @@ Result<Ast> Parser::parse_atomic_expression()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return parse_sequence().and_then([&](Ast &&ast) -> Result<Ast> {
|
return parse_sequence_inside(
|
||||||
if (not expect(Token::Type::Close_Block)) {
|
*this,
|
||||||
if (expect(Token::Type::Parameter_Separator)) {
|
Token::Type::Close_Block,
|
||||||
if (is_lambda) {
|
opening.location,
|
||||||
ensure(false, "There should be error message that you cannot put multiple parameter separators in one block");
|
is_lambda,
|
||||||
} else {
|
std::move(parameters),
|
||||||
// This may be a result of user trying to specify parameters from things that cannot be parameters (like chord literals)
|
[&]() -> std::optional<Error> {
|
||||||
// or accidential hit of "|" on the keyboard. We can detect first case by ensuring that all tokens between this place
|
if (!expect(Token::Type::Parameter_Separator)) {
|
||||||
// and beggining of a block are identifier looking: keywords, symbols, literal chord declarations, boolean literals etc
|
return std::nullopt;
|
||||||
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 (is_lambda) {
|
||||||
if (!invalid_token && token.type != Token::Type::Symbol) {
|
ensure(false, "There should be error message that you cannot put multiple parameter separators in one block");
|
||||||
// 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) {
|
// This may be a result of user trying to specify parameters from things that cannot be parameters (like chord literals)
|
||||||
return Error {
|
// or accidential hit of "|" on the keyboard. We can detect first case by ensuring that all tokens between this place
|
||||||
.details = errors::Literal_As_Identifier {
|
// and beggining of a block are identifier looking: keywords, symbols, literal chord declarations, boolean literals etc
|
||||||
.type_name = type_name(invalid_token->type),
|
std::optional<Token> invalid_token = std::nullopt;
|
||||||
.source = invalid_token->source,
|
auto const success = std::all_of(tokens.begin() + start, tokens.begin() + (token_id - start + 1), [&](Token const& token) {
|
||||||
.context = "block parameter list"
|
if (!invalid_token && token.type != Token::Type::Symbol) {
|
||||||
},
|
// TODO Maybe gather all tokens to provide all the help needed in the one iteration
|
||||||
.location = invalid_token->location
|
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 {
|
return std::nullopt;
|
||||||
.details = errors::Unexpected_Empty_Source {
|
|
||||||
.reason = errors::Unexpected_Empty_Source::Block_Without_Closing_Bracket,
|
|
||||||
.start = opening.location
|
|
||||||
},
|
|
||||||
.location = tokens.back().location
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
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:
|
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)
|
static usize precedense(std::string_view op)
|
||||||
{
|
{
|
||||||
// Operators that are not included below are
|
// 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.
|
// 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");
|
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 400;
|
||||||
if (one_of(op, "**")) return 500;
|
if (one_of(op, "**")) return 500;
|
||||||
|
|
||||||
|
|
||||||
std::cerr << op << std::endl;
|
|
||||||
|
|
||||||
unreachable();
|
unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user