New declaration syntax x := foo
instead of var x = foo
Additionaly moving into multiparameter binary operations
This commit is contained in:
parent
e57a60c36f
commit
ddf9cc8f8c
@ -1,8 +1,8 @@
|
|||||||
----------------------------------
|
----------------------------------
|
||||||
Simple variable assigment
|
Simple variable assigment
|
||||||
----------------------------------
|
----------------------------------
|
||||||
var x = 10;
|
x := 10;
|
||||||
var y = 20;
|
y := 20;
|
||||||
say x;
|
say x;
|
||||||
say y;
|
say y;
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ say y;
|
|||||||
----------------------------------
|
----------------------------------
|
||||||
Array updates
|
Array updates
|
||||||
----------------------------------
|
----------------------------------
|
||||||
var array = flat 1 2 3;
|
array := flat 1 2 3;
|
||||||
say array;
|
say array;
|
||||||
|
|
||||||
array = update array 1 10;
|
array = update array 1 10;
|
||||||
|
@ -1,34 +1,34 @@
|
|||||||
-- PAIR definition
|
-- PAIR definition
|
||||||
var pair = [x y | [z | z x y]];
|
pair := [x y | [z | z x y]];
|
||||||
var car = [p | p [x y | x]];
|
car := [pair | pair [x y | x]];
|
||||||
var cdr = [p | p [x y | y]];
|
cdr := [pair | pair [x y | y]];
|
||||||
|
|
||||||
var x = pair 100 200;
|
x := pair 100 200;
|
||||||
say (car x);
|
say (car x);
|
||||||
say (cdr x);
|
say (cdr x);
|
||||||
|
|
||||||
-- LIST definition
|
-- LIST definition
|
||||||
var null = pair true true;
|
null := pair true true;
|
||||||
var is_empty = car;
|
is_empty := car;
|
||||||
var head = [list | car (cdr list)];
|
head := [list | car (cdr list)];
|
||||||
var tail = [list | cdr (cdr list)];
|
tail := [list | cdr (cdr list)];
|
||||||
var cons = [head tail | pair false (pair head tail)];
|
cons := [head tail | pair false (pair head tail)];
|
||||||
|
|
||||||
var for_each = [list iterator |
|
for_each := [list iterator |
|
||||||
if (is_empty list) [ nil ] [
|
if (is_empty list) [ nil ] [
|
||||||
iterator (head list);
|
iterator (head list);
|
||||||
for_each (tail list) iterator ]];
|
for_each (tail list) iterator ]];
|
||||||
|
|
||||||
var map = [list iterator |
|
map := [list iterator |
|
||||||
if (is_empty list) [ null ] [|
|
if (is_empty list) [ null ] [|
|
||||||
cons (iterator (head list)) (map (tail list) iterator) ]];
|
cons (iterator (head list)) (map (tail list) iterator) ]];
|
||||||
|
|
||||||
var foldr = [list init folder |
|
foldr := [list init folder |
|
||||||
if (is_empty list)
|
if (is_empty list)
|
||||||
[ init ]
|
[ init ]
|
||||||
[ foldr (tail list) (folder (head list) init) folder ]];
|
[ foldr (tail list) (folder (head list) init) folder ]];
|
||||||
|
|
||||||
var range = [start stop | if (start >= stop) [cons start null] [cons start (range (start+1) stop)]];
|
range := [start stop | if (start >= stop) [cons start null] [cons start (range (start+1) stop)]];
|
||||||
|
|
||||||
var xs = range 1 5;
|
xs := range 1 5;
|
||||||
say (foldr xs 1 [x y|x*y]);
|
say (foldr xs 1 [x y|x*y]);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
var map = [fn array | var result = []; for array [v | result = result & [fn v] ]; result];
|
map := [fn array | var result = []; for array [v | result = result & [fn v] ]; result];
|
||||||
var drop = [n arr | arr.(range n (len arr))];
|
drop := [n arr | arr.(range n (len arr))];
|
||||||
var take = [n arr | arr.(up n)];
|
take := [n arr | arr.(up n)];
|
||||||
var filter = [predicate arr | arr.(map predicate arr)];
|
filter := [predicate arr | arr.(map predicate arr)];
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
var for = [ start stop iteration |
|
for := [ start stop iteration |
|
||||||
if (start > stop)
|
if (start > stop)
|
||||||
[nil]
|
[nil]
|
||||||
[iteration start; for (start + 1) stop iteration]
|
[iteration start; for (start + 1) stop iteration]
|
||||||
];
|
];
|
||||||
|
|
||||||
var factorial = [n | if (n <= 1) [1] [n * (factorial (n-1))]];
|
factorial := [n | if (n <= 1) [1] [n * (factorial (n-1))]];
|
||||||
for 1 10 [i | say (factorial i)];
|
for 1 10 [i | say (factorial i)];
|
||||||
|
|
||||||
var factorial_iterative = [n |
|
factorial_iterative := [n |
|
||||||
var x = 1;
|
x := 1;
|
||||||
for 1 n [i|x *= i];
|
for 1 n [i|x *= i];
|
||||||
x
|
x
|
||||||
];
|
];
|
||||||
|
@ -1 +1 @@
|
|||||||
var fib = [n | if (n <= 1) [ n ] [ fib (n-1) + fib (n-2) ] ];
|
fib := [n | if (n <= 1) [ n ] [ fib (n-1) + fib (n-2) ] ];
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
oct 5; bpm 72; len (1/16);
|
oct 5; bpm 72; len (1/16);
|
||||||
|
|
||||||
var subsection1 = [ n |
|
subsection1 := [ n |
|
||||||
sim (a 4 en) (a 2 e 3 a 3);
|
sim (a 4 en) (a 2 e 3 a 3);
|
||||||
play [oct 4; c e a];
|
play [oct 4; c e a];
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ var subsection1 = [ n |
|
|||||||
play (d 4 c 5 b 4);
|
play (d 4 c 5 b 4);
|
||||||
];
|
];
|
||||||
|
|
||||||
var section1 = [ n |
|
section1 := [ n |
|
||||||
play (e d#);
|
play (e d#);
|
||||||
play (e d# e b 4 d c);
|
play (e d# e b 4 d c);
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ var section1 = [ n |
|
|||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
var section2 = [ n |
|
section2 := [ n |
|
||||||
sim (e 5 den) (c 3 g 3 c 4);
|
sim (e 5 den) (c 3 g 3 c 4);
|
||||||
play (g 4 f e);
|
play (g 4 f e);
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
var C = chord (c 3 1) (g 3 1) (c 4 1);
|
C := chord (c 3 1) (g 3 1) (c 4 1);
|
||||||
var G = chord (g 3 1) (d 4 1) (g 4 1);
|
G := chord (g 3 1) (d 4 1) (g 4 1);
|
||||||
|
|
||||||
oct 5;
|
oct 5;
|
||||||
par C e e f g;
|
par C e e f g;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
var factorial = [n | if (n < 2) [1] [factorial (n-1) * n]];
|
factorial := [n | if (n < 2) [1] [factorial (n-1) * n]];
|
||||||
|
|
||||||
var for_all_permutations = [array fun |
|
for_all_permutations := [array fun |
|
||||||
var iter = [start stop x | if (start >= stop) [x] [iter (start+1) stop (fun x)]];
|
iter := [start stop x | if (start >= stop) [x] [iter (start+1) stop (fun x)]];
|
||||||
iter 0 (factorial (len array)) array
|
iter 0 (factorial (len array)) array
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -25,12 +25,12 @@ play c d c e (c & d) c d (c & e)
|
|||||||
|
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
|
|
||||||
var Length = 20;
|
Length := 20;
|
||||||
|
|
||||||
var cmajor = [c;d;e;f;g];
|
cmajor := [c;d;e;f;g];
|
||||||
var scale = reverse cmajor;
|
scale := reverse cmajor;
|
||||||
var primes = nprimes (len scale);
|
primes := nprimes (len scale);
|
||||||
var indicies = up (len scale);
|
indicies := up (len scale);
|
||||||
|
|
||||||
oct 3;
|
oct 3;
|
||||||
len (1/16);
|
len (1/16);
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
var x = 10;
|
x := 10;
|
||||||
say (x + 1)
|
say (x + 1)
|
||||||
|
@ -475,8 +475,8 @@ struct Token
|
|||||||
Location location;
|
Location location;
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr usize Keywords_Count = 6;
|
static constexpr usize Keywords_Count = 5;
|
||||||
static constexpr usize Operators_Count = 16;
|
static constexpr usize Operators_Count = 17;
|
||||||
|
|
||||||
std::string_view type_name(Token::Type type);
|
std::string_view type_name(Token::Type type);
|
||||||
|
|
||||||
@ -687,6 +687,9 @@ struct Parser
|
|||||||
|
|
||||||
/// Tests if current token has given type and source
|
/// Tests if current token has given type and source
|
||||||
bool expect(Token::Type type, std::string_view lexeme) const;
|
bool expect(Token::Type type, std::string_view lexeme) const;
|
||||||
|
|
||||||
|
// Tests if current token has given type and the next token has given type and source
|
||||||
|
bool expect(Token::Type t1, Token::Type t2, std::string_view lexeme_for_t2) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Number type supporting integer and fractional constants
|
/// Number type supporting integer and fractional constants
|
||||||
|
@ -136,6 +136,7 @@ namespace algo
|
|||||||
/// Fold that stops iteration on error value via Result type
|
/// Fold that stops iteration on error value via Result type
|
||||||
template<typename T>
|
template<typename T>
|
||||||
constexpr Result<T> fold(auto&& range, T init, auto &&reducer)
|
constexpr Result<T> fold(auto&& range, T init, auto &&reducer)
|
||||||
|
requires (is_template_v<Result, decltype(reducer(std::move(init), *range.begin()))>)
|
||||||
{
|
{
|
||||||
for (auto &&value : range) {
|
for (auto &&value : range) {
|
||||||
init = Try(reducer(std::move(init), value));
|
init = Try(reducer(std::move(init), value));
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
#include <musique.hh>
|
#include <musique.hh>
|
||||||
#include <musique_internal.hh>
|
#include <musique_internal.hh>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
/// Intrinsic implementation primitive to ease operation vectorization
|
/// Intrinsic implementation primitive to ease operation vectorization
|
||||||
Result<Value> vectorize(auto &&operation, Interpreter &interpreter, Value lhs, Value rhs)
|
static Result<Value> vectorize(auto &&operation, Interpreter &interpreter, Value lhs, Value rhs)
|
||||||
{
|
{
|
||||||
Array array;
|
|
||||||
|
|
||||||
if (is_indexable(lhs.type) && !is_indexable(rhs.type)) {
|
if (is_indexable(lhs.type) && !is_indexable(rhs.type)) {
|
||||||
Array array;
|
Array array;
|
||||||
@ -15,6 +15,7 @@ Result<Value> vectorize(auto &&operation, Interpreter &interpreter, Value lhs, V
|
|||||||
return Value::from(std::move(array));
|
return Value::from(std::move(array));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Array array;
|
||||||
for (auto i = 0u; i < rhs.size(); ++i) {
|
for (auto i = 0u; i < rhs.size(); ++i) {
|
||||||
array.elements.push_back(
|
array.elements.push_back(
|
||||||
Try(operation(interpreter, { lhs, Try(rhs.index(interpreter, i)) })));
|
Try(operation(interpreter, { lhs, Try(rhs.index(interpreter, i)) })));
|
||||||
@ -24,19 +25,39 @@ Result<Value> vectorize(auto &&operation, Interpreter &interpreter, Value lhs, V
|
|||||||
|
|
||||||
/// Intrinsic implementation primitive to ease operation vectorization
|
/// Intrinsic implementation primitive to ease operation vectorization
|
||||||
/// @invariant args.size() == 2
|
/// @invariant args.size() == 2
|
||||||
Result<Value> vectorize(auto &&operation, Interpreter &interpreter, std::vector<Value> args)
|
static Result<Value> vectorize(auto &&operation, Interpreter &interpreter, std::vector<Value> args)
|
||||||
{
|
{
|
||||||
assert(args.size() == 2, "Vectorization primitive only supports two arguments");
|
assert(args.size() == 2, "Vectorization primitive only supports two arguments");
|
||||||
return vectorize(std::move(operation), interpreter, std::move(args.front()), std::move(args.back()));
|
return vectorize(std::move(operation), interpreter, std::move(args.front()), std::move(args.back()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper simlifiing implementation of symetric binary operations.
|
||||||
std::optional<Value> symetric(Value::Type t1, Value::Type t2, Value &lhs, Value &rhs, auto binary_predicate)
|
///
|
||||||
|
/// Calls binary if values matches types any permutation of {t1, t2}, always in shape (t1, t2)
|
||||||
|
inline std::optional<Value> symetric(Value::Type t1, Value::Type t2, Value &lhs, Value &rhs, auto binary)
|
||||||
{
|
{
|
||||||
if (lhs.type == t1 && rhs.type == t2) {
|
if (lhs.type == t1 && rhs.type == t2) {
|
||||||
return binary_predicate(std::move(lhs), std::move(rhs));
|
return binary(std::move(lhs), std::move(rhs));
|
||||||
} else if (lhs.type == t2 && rhs.type == t1) {
|
} else if (lhs.type == t2 && rhs.type == t1) {
|
||||||
return binary_predicate(std::move(rhs), std::move(lhs));
|
return binary(std::move(rhs), std::move(lhs));
|
||||||
|
} else {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper simlifiing implementation of symetric binary operations.
|
||||||
|
///
|
||||||
|
/// Calls binary if values matches predicates in any permutation; always with shape (p1, p2)
|
||||||
|
inline auto symetric(
|
||||||
|
std::predicate<Value::Type> auto&& p1,
|
||||||
|
std::predicate<Value::Type> auto&& p2,
|
||||||
|
Value &lhs, Value &rhs,
|
||||||
|
auto binary) -> std::optional<decltype(binary(std::move(lhs), std::move(rhs)))>
|
||||||
|
{
|
||||||
|
if (p1(lhs.type) && p2(rhs.type)) {
|
||||||
|
return binary(std::move(lhs), std::move(rhs));
|
||||||
|
} else if (p2(lhs.type) && p1(rhs.type)) {
|
||||||
|
return binary(std::move(rhs), std::move(lhs));
|
||||||
} else {
|
} else {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@ -49,45 +70,33 @@ std::optional<Value> symetric(Value::Type t1, Value::Type t2, Value &lhs, Value
|
|||||||
template<typename Binary_Operation>
|
template<typename Binary_Operation>
|
||||||
static Result<Value> plus_minus_operator(Interpreter &interpreter, std::vector<Value> args)
|
static Result<Value> plus_minus_operator(Interpreter &interpreter, std::vector<Value> args)
|
||||||
{
|
{
|
||||||
static_assert(std::is_same_v<Binary_Operation, std::plus<>> || std::is_same_v<Binary_Operation, std::minus<>>,
|
if (args.empty()) {
|
||||||
"Error reporting depends on only one of this two types beeing provided");
|
return Value::from(Number(0));
|
||||||
|
|
||||||
using NN = Shape<Value::Type::Number, Value::Type::Number>;
|
|
||||||
using MN = Shape<Value::Type::Music, Value::Type::Number>;
|
|
||||||
using NM = Shape<Value::Type::Number, Value::Type::Music>;
|
|
||||||
|
|
||||||
if (NN::typecheck(args)) {
|
|
||||||
auto [a, b] = NN::move_from(args);
|
|
||||||
return Value::from(Binary_Operation{}(std::move(a), std::move(b)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MN::typecheck(args)) {
|
Value init = args.front();
|
||||||
auto [chord, offset] = MN::move_from(args);
|
return algo::fold(std::span(args).subspan(1), std::move(init), [&interpreter](Value lhs, Value &rhs) -> Result<Value> {
|
||||||
for (auto ¬e : chord.notes) {
|
if (lhs.type == Value::Type::Number && rhs.type == Value::Type::Number) {
|
||||||
|
return Value::from(Binary_Operation{}(std::move(lhs).n, std::move(rhs).n));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = symetric(Value::Type::Music, Value::Type::Number, lhs, rhs, [](Value lhs, Value rhs) {
|
||||||
|
for (auto ¬e : lhs.chord.notes) {
|
||||||
if (note.base) {
|
if (note.base) {
|
||||||
*note.base = Binary_Operation{}(*note.base, offset.as_int());
|
*note.base = Binary_Operation{}(*note.base, rhs.n.as_int());
|
||||||
note.simplify_inplace();
|
note.simplify_inplace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Value::from(std::move(chord));
|
return lhs;
|
||||||
|
});
|
||||||
|
if (result.has_value()) {
|
||||||
|
return *std::move(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NM::typecheck(args)) {
|
if (is_indexable(lhs.type) != is_indexable(rhs.type)) {
|
||||||
auto [offset, chord] = NM::move_from(args);
|
return vectorize(plus_minus_operator<Binary_Operation>, interpreter, std::move(lhs), std::move(rhs));
|
||||||
for (auto ¬e : chord.notes) {
|
|
||||||
if (note.base) {
|
|
||||||
*note.base = Binary_Operation{}(*note.base, offset.as_int());
|
|
||||||
note.simplify_inplace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Value::from(std::move(chord));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (may_be_vectorized(args)) {
|
|
||||||
return vectorize(plus_minus_operator<Binary_Operation>, interpreter, std::move(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Limit possibilities based on provided types
|
|
||||||
static_assert(std::is_same_v<std::plus<>, Binary_Operation> || std::is_same_v<std::minus<>, Binary_Operation>,
|
static_assert(std::is_same_v<std::plus<>, Binary_Operation> || std::is_same_v<std::minus<>, Binary_Operation>,
|
||||||
"Error message printing only supports operators given above");
|
"Error message printing only supports operators given above");
|
||||||
return Error {
|
return Error {
|
||||||
@ -101,9 +110,9 @@ static Result<Value> plus_minus_operator(Interpreter &interpreter, std::vector<V
|
|||||||
"(array, number|music) -> array",
|
"(array, number|music) -> array",
|
||||||
"(number|music, array) -> array",
|
"(number|music, array) -> array",
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
.location = {}, // TODO fill location
|
|
||||||
};
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Binary_Operation, char ...Chars>
|
template<typename Binary_Operation, char ...Chars>
|
||||||
@ -144,26 +153,24 @@ static Result<Value> comparison_operator(Interpreter &interpreter, std::vector<V
|
|||||||
return Value::from(algo::pairwise_all(std::move(args), Binary_Predicate{}));
|
return Value::from(algo::pairwise_all(std::move(args), Binary_Predicate{}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_indexable(args[1].type) && !is_indexable(args[0].type)) {
|
auto result = symetric(is_indexable, std::not_fn(is_indexable), args.front(), args.back(), [&interpreter](Value lhs, Value rhs) -> Result<Value> {
|
||||||
std::swap(args[1], args[0]);
|
|
||||||
goto vectorized;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_indexable(args[0].type) && !is_indexable(args[1].type)) {
|
|
||||||
vectorized:
|
|
||||||
std::vector<Value> result;
|
std::vector<Value> result;
|
||||||
result.reserve(args[0].size());
|
result.reserve(lhs.size());
|
||||||
for (auto i = 0u; i < args[0].size(); ++i) {
|
for (auto i = 0u; i < lhs.size(); ++i) {
|
||||||
result.push_back(
|
result.push_back(
|
||||||
Value::from(
|
Value::from(
|
||||||
Binary_Predicate{}(
|
Binary_Predicate{}(
|
||||||
Try(args[0].index(interpreter, i)),
|
Try(lhs.index(interpreter, i)),
|
||||||
args[1]
|
rhs
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Value::from(Array { std::move(result) });
|
return Value::from(Array { std::move(result) });
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.has_value()) {
|
||||||
|
return *std::move(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Value::from(Binary_Predicate{}(std::move(args.front()), std::move(args.back())));
|
return Value::from(Binary_Predicate{}(std::move(args.front()), std::move(args.back())));
|
||||||
@ -308,7 +315,7 @@ static constexpr auto Operators = std::array {
|
|||||||
// 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 - 2, "All operators handlers are defined here");
|
static_assert(Operators.size() == Operators_Count - 3, "All operators handlers are defined here");
|
||||||
|
|
||||||
void Interpreter::register_builtin_operators()
|
void Interpreter::register_builtin_operators()
|
||||||
{
|
{
|
||||||
|
@ -15,7 +15,6 @@ constexpr auto Keywords = std::array {
|
|||||||
"false"sv,
|
"false"sv,
|
||||||
"nil"sv,
|
"nil"sv,
|
||||||
"true"sv,
|
"true"sv,
|
||||||
"var"sv,
|
|
||||||
"and"sv,
|
"and"sv,
|
||||||
"or"sv
|
"or"sv
|
||||||
};
|
};
|
||||||
|
@ -26,14 +26,14 @@ constexpr auto Literal_Keywords = std::array {
|
|||||||
"true"sv,
|
"true"sv,
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(Keywords_Count == Literal_Keywords.size() + 3, "Ensure that all literal keywords are listed");
|
static_assert(Keywords_Count == Literal_Keywords.size() + 2, "Ensure that all literal keywords are listed");
|
||||||
|
|
||||||
constexpr auto Operator_Keywords = std::array {
|
constexpr auto Operator_Keywords = std::array {
|
||||||
"and"sv,
|
"and"sv,
|
||||||
"or"sv
|
"or"sv
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(Keywords_Count == Operator_Keywords.size() + 4, "Ensure that all keywords that are operators are listed here");
|
static_assert(Keywords_Count == Operator_Keywords.size() + 3, "Ensure that all keywords that are operators are listed here");
|
||||||
|
|
||||||
enum class At_Least : bool
|
enum class At_Least : bool
|
||||||
{
|
{
|
||||||
@ -67,13 +67,6 @@ 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();
|
||||||
|
|
||||||
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::Keyword, "var")) {
|
|
||||||
return Error {
|
|
||||||
.details = errors::Expected_Expression_Separator_Before { .what = "var" },
|
|
||||||
.location = parser.peek()->location
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parser.expect(Token::Type::Close_Paren) || parser.expect(Token::Type::Close_Block)) {
|
if (parser.expect(Token::Type::Close_Paren) || parser.expect(Token::Type::Close_Block)) {
|
||||||
auto const tok = parser.consume();
|
auto const tok = parser.consume();
|
||||||
return Error {
|
return Error {
|
||||||
@ -100,7 +93,7 @@ Result<Ast> Parser::parse_sequence()
|
|||||||
|
|
||||||
Result<Ast> Parser::parse_expression()
|
Result<Ast> Parser::parse_expression()
|
||||||
{
|
{
|
||||||
if (expect(Token::Type::Keyword, "var")) {
|
if (expect(Token::Type::Symbol, Token::Type::Operator, ":=")) {
|
||||||
return parse_variable_declaration();
|
return parse_variable_declaration();
|
||||||
}
|
}
|
||||||
return parse_infix_expression();
|
return parse_infix_expression();
|
||||||
@ -108,10 +101,7 @@ Result<Ast> Parser::parse_expression()
|
|||||||
|
|
||||||
Result<Ast> Parser::parse_variable_declaration()
|
Result<Ast> Parser::parse_variable_declaration()
|
||||||
{
|
{
|
||||||
assert(expect(Token::Type::Keyword, "var"), "Parser::parse_variable_declaration must be called only on expressions that starts with 'var'");
|
auto lvalue = parse_identifier();
|
||||||
auto var = consume();
|
|
||||||
|
|
||||||
auto lvalue = parse_many(*this, &Parser::parse_identifier, std::nullopt, At_Least::One);
|
|
||||||
if (not lvalue.has_value()) {
|
if (not lvalue.has_value()) {
|
||||||
auto details = lvalue.error().details;
|
auto details = lvalue.error().details;
|
||||||
if (auto ut = std::get_if<errors::internal::Unexpected_Token>(&details); ut) {
|
if (auto ut = std::get_if<errors::internal::Unexpected_Token>(&details); ut) {
|
||||||
@ -127,13 +117,9 @@ Result<Ast> Parser::parse_variable_declaration()
|
|||||||
return std::move(lvalue).error();
|
return std::move(lvalue).error();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(expect(Token::Type::Operator, ":="), "This function should always be called with valid sequence");
|
||||||
if (expect(Token::Type::Operator, "=")) {
|
|
||||||
consume();
|
consume();
|
||||||
return Ast::variable_declaration(var.location, *std::move(lvalue), Try(parse_expression()));
|
return Ast::variable_declaration(lvalue->location, { *std::move(lvalue) }, Try(parse_expression()));
|
||||||
}
|
|
||||||
|
|
||||||
return Ast::variable_declaration(var.location, *std::move(lvalue), std::nullopt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<Ast> Parser::parse_infix_expression()
|
Result<Ast> Parser::parse_infix_expression()
|
||||||
@ -436,6 +422,14 @@ bool Parser::expect(Token::Type type, std::string_view lexeme) const
|
|||||||
return token_id < tokens.size() && tokens[token_id].type == type && tokens[token_id].source == lexeme;
|
return token_id < tokens.size() && tokens[token_id].type == type && tokens[token_id].source == lexeme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Parser::expect(Token::Type t1, Token::Type t2, std::string_view lexeme_for_t2) const
|
||||||
|
{
|
||||||
|
return token_id+1 < tokens.size()
|
||||||
|
&& tokens[token_id].type == t1
|
||||||
|
&& tokens[token_id+1].type == t2
|
||||||
|
&& tokens[token_id+1].source == lexeme_for_t2;
|
||||||
|
}
|
||||||
|
|
||||||
// Don't know if it's a good idea to defer parsing of literal values up to value creation, which is current approach.
|
// Don't know if it's a good idea to defer parsing of literal values up to value creation, which is current approach.
|
||||||
// This may create unexpected performance degradation during program evaluation.
|
// This may create unexpected performance degradation during program evaluation.
|
||||||
Ast Ast::literal(Token token)
|
Ast Ast::literal(Token token)
|
||||||
@ -529,9 +523,10 @@ static usize precedense(std::string_view op)
|
|||||||
// '.' since it have own precedense rules and is not binary expression but its own kind of expression
|
// '.' since it have own precedense rules and is not binary expression but its own kind of expression
|
||||||
//
|
//
|
||||||
// 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 == 15, "Ensure that all operators have defined precedense below");
|
static_assert(Operators_Count - 1 == 16, "Ensure that all operators have defined precedense below");
|
||||||
|
|
||||||
if (one_of(op, "=")) return 0;
|
if (one_of(op, ":=")) return 0;
|
||||||
|
if (one_of(op, "=")) return 10;
|
||||||
if (one_of(op, "or")) return 100;
|
if (one_of(op, "or")) return 100;
|
||||||
if (one_of(op, "and")) return 150;
|
if (one_of(op, "and")) return 150;
|
||||||
if (one_of(op, "<", ">", "<=", ">=", "==", "!=")) return 200;
|
if (one_of(op, "<", ">", "<=", ">=", "==", "!=")) return 200;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include <musique.hh>
|
#include <musique.hh>
|
||||||
#include <musique_internal.hh>
|
#include <musique_internal.hh>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
|
||||||
/// Finds numeric value of note. This form is later used as in
|
/// Finds numeric value of note. This form is later used as in
|
||||||
@ -21,8 +22,6 @@ constexpr u8 note_index(u8 note)
|
|||||||
unreachable();
|
unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
constexpr std::string_view note_index_to_string(int note_index)
|
constexpr std::string_view note_index_to_string(int note_index)
|
||||||
{
|
{
|
||||||
note_index %= 12;
|
note_index %= 12;
|
||||||
|
Loading…
Reference in New Issue
Block a user