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), 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]),

View File

@ -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),

View File

@ -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]]),
), ),

View File

@ -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>

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<>> },
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");

View File

@ -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";

View File

@ -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

View File

@ -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>

View File

@ -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> &&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() 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();
} }