diff --git a/examples/arrays.mq b/examples/arrays.mq new file mode 100644 index 0000000..c7231e8 --- /dev/null +++ b/examples/arrays.mq @@ -0,0 +1,11 @@ +var numbers = [1;2;3;4;5]; + +var for = [array iteration | + var iter = [i | if (i < (len array)) [ + iteration (numbers.i); + iter (i+1) + ]]; + iter 0 +]; + +for numbers say; diff --git a/src/interpreter.cc b/src/interpreter.cc index 438c6d8..644ac29 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -95,6 +95,16 @@ Interpreter::Interpreter(std::ostream& out) return {}; }); + global.force_define("len", +[](Interpreter &, std::vector args) -> Result { + assert(args.size() == 1, "len only accepts one argument"); + assert(args.front().type == Value::Type::Block, "Only blocks can be measure"); + if (args.front().blk.body.type != Ast::Type::Sequence) { + return Value::number(Number(1)); + } else { + return Value::number(Number(args.front().blk.body.arguments.size())); + } + }); + operators["+"] = binary_operator>(); operators["-"] = binary_operator>(); operators["*"] = binary_operator>(); @@ -107,6 +117,13 @@ Interpreter::Interpreter(std::ostream& out) operators["=="] = equality_operator>(); operators["!="] = equality_operator>(); + + operators["."] = +[](Interpreter &i, std::vector args) -> Result { + assert(args.size() == 2, "Operator . requires two arguments"); // TODO(assert) + assert(args.front().type == Value::Type::Block, "Only blocks can be indexed"); // TODO(assert) + assert(args.back().type == Value::Type::Number, "Only numbers can be used for indexing"); // TODO(assert) + return std::move(args.front()).blk.index(i, std::move(args.back()).n.as_int()); + }; } Result Interpreter::eval(Ast &&ast) diff --git a/src/lexer.cc b/src/lexer.cc index 75f6d76..bbb51fb 100644 --- a/src/lexer.cc +++ b/src/lexer.cc @@ -5,8 +5,9 @@ constexpr std::string_view Notes_Symbols = "abcedefgh"; constexpr std::string_view Valid_Operator_Chars = "+-*/:%" // arithmetic - "|&^" // logic & bit operations - "<>=!" // comparisons + "|&^" // logic & bit operations + "<>=!" // comparisons + "." // indexing ; constexpr auto Keywords = std::array { @@ -84,14 +85,6 @@ auto Lexer::next_token() -> Result return { Token::Type::Parameter_Separator, finish(), token_location }; } - // Number literals like .75 - if (peek() == '.') { - consume(); - while (consume_if(unicode::is_digit)) {} - if (token_length != 1) - return { Token::Type::Numeric, finish(), token_location }; - } - if (consume_if(unicode::is_digit)) { while (consume_if(unicode::is_digit)) {} if (peek() == '.') { diff --git a/src/musique.hh b/src/musique.hh index 7a0c7e8..a262ba5 100644 --- a/src/musique.hh +++ b/src/musique.hh @@ -444,6 +444,7 @@ struct Block std::shared_ptr context; Result operator()(Interpreter &i, std::vector params); + Result index(Interpreter &i, unsigned position); }; template @@ -530,8 +531,13 @@ private: struct Interpreter { + /// Output of IO builtins like `say` std::ostream &out; + + /// Operators defined for language std::unordered_map operators; + + /// Current environment (current scope) std::shared_ptr env; Interpreter(); diff --git a/src/tests/lex.cc b/src/tests/lex.cc index e66a6e0..5178d42 100644 --- a/src/tests/lex.cc +++ b/src/tests/lex.cc @@ -114,7 +114,6 @@ suite lexer_test = [] { "Numeric tokens"_test = [] { expect_token_type_and_value(Token::Type::Numeric, "0"); expect_token_type_and_value(Token::Type::Numeric, "123456789"); - expect_token_type_and_value(Token::Type::Numeric, ".75"); expect_token_type_and_value(Token::Type::Numeric, "0.75"); expect_token_type_and_value(Token::Type::Numeric, "123456789.123456789"); expect_token_type_and_value(Token::Type::Numeric, "123.", "123"); diff --git a/src/value.cc b/src/value.cc index 344212b..878e765 100644 --- a/src/value.cc +++ b/src/value.cc @@ -152,3 +152,16 @@ Result Block::operator()(Interpreter &i, std::vector arguments) i.env = old_scope; return result; } + +// TODO Add memoization +Result Block::index(Interpreter &i, unsigned position) +{ + assert(parameters.size() == 0, "cannot index into block with parameters (for now)"); + if (body.type != Ast::Type::Sequence) { + assert(position == 0, "Out of range"); // TODO(assert) + return i.eval((Ast)body); + } + + assert(position < body.arguments.size(), "Out of range"); // TODO(assert) + return i.eval((Ast)body.arguments[position]); +}