diff --git a/doc/grammar.bnf b/doc/grammar.bnf index 044280f..cb7e123 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -1,6 +1,7 @@ MODULE ::= EXPR* EXPR ::= bool +| int | ident | VARDECL | FUNCALL diff --git a/examples/int.gri b/examples/int.gri new file mode 100644 index 0000000..521c083 --- /dev/null +++ b/examples/int.gri @@ -0,0 +1,28 @@ +(assert-eq? 7 7) + +(assert-eq? 0 (+)) +(assert-eq? 3 (+ 3)) +(assert-eq? 5 (+ 2 3)) + +(assert-eq? 0 (-)) +(assert-eq? -3 (- 3)) +(assert-eq? -1 (- 2 3)) +(assert-eq? 5 (- 8 3)) + +(assert-eq? 1 (*)) +(assert-eq? 6 (* 3 2)) +(assert-eq? 120 (* 1 2 3 4 5)) + +(assert-eq? 1 (/)) +(assert-eq? 6 (/ 6)) +(assert-eq? 3 (/ 6 2)) +(assert-eq? 2 (/ 12 2 3)) + +(assert-eq? 1 (%)) +(assert-eq? 3 (% 7 4)) +(assert-eq? 2 (% 13 7 4)) + +(assert-eq? 1 (^)) +(assert-eq? 2 (^ 2)) +(assert-eq? 8 (^ 2 3)) +(assert-eq? 64 (^ 2 3 2)) diff --git a/lib/core.cpp b/lib/core.cpp index 632ba61..bc16b21 100644 --- a/lib/core.cpp +++ b/lib/core.cpp @@ -3,9 +3,156 @@ GRINO_ERROR(assertion_error); +extern "C" void lib_int(grino::Loader& loader) +{ + loader.add_native("+", [](auto args){ + int result = 0; + + for (auto value: args) + { + result += value->as_int(); + } + + grino::Loc loc {"???", 0}; + + if (args.empty() == false) + { + loc = args.front()->loc(); + } + + return grino::Value::make_int(loc, result); + }); + + loader.add_native("*", [](auto args){ + int result = 1; + + for (auto value: args) + { + result *= value->as_int(); + } + + grino::Loc loc {"???", 0}; + + if (args.empty() == false) + { + loc = args.front()->loc(); + } + + return grino::Value::make_int(loc, result); + }); + + loader.add_native("-", [](auto args){ + grino::Loc loc {"???", 0}; + + if (args.empty() == false) + { + loc = args.front()->loc(); + } + else + { + return grino::Value::make_int(loc, 0); + } + + int result = args[0]->as_int(); + + if (args.size() == 1) + { + return grino::Value::make_int(loc, -result); + } + + for (size_t i=1; ias_int(); + } + + return grino::Value::make_int(loc, result); + }); + + loader.add_native("/", [](auto args){ + grino::Loc loc {"???", 0}; + + if (args.empty() == false) + { + loc = args.front()->loc(); + } + else + { + return grino::Value::make_int(loc, 1); + } + + int result = args[0]->as_int(); + + if (args.size() == 1) + { + return grino::Value::make_int(loc, result); + } + + for (size_t i=1; ias_int(); + } + + return grino::Value::make_int(loc, result); + }); + + loader.add_native("%", [](auto args){ + grino::Loc loc {"???", 0}; + + if (args.empty() == false) + { + loc = args.front()->loc(); + } + else + { + return grino::Value::make_int(loc, 1); + } + + int result = args[0]->as_int(); + + if (args.size() == 1) + { + return grino::Value::make_int(loc, result); + } + + for (size_t i=1; ias_int(); + } + + return grino::Value::make_int(loc, result); + }); + + loader.add_native("^", [](auto args){ + grino::Loc loc {"???", 0}; + + if (args.empty() == false) + { + loc = args.front()->loc(); + } + else + { + return grino::Value::make_int(loc, 1); + } + + int result = args[0]->as_int(); + + if (args.size() == 1) + { + return grino::Value::make_int(loc, result); + } + + for (size_t i=1; ias_int()); + } + + return grino::Value::make_int(loc, result); + }); +} + extern "C" void lib_assert(grino::Loader& loader) { - loader.add_native("assert", [](auto args){ + loader.add_native("assert", [](auto args){ for (auto value: args) { @@ -21,7 +168,7 @@ extern "C" void lib_assert(grino::Loader& loader) return grino::Value::make_bool(args.front()->loc(), true); }); - loader.add_native("assert-eq?", [](auto args){ + loader.add_native("assert-eq?", [](auto args){ auto lhs = args.front(); for (size_t i=1; irepr(); + auto value = Value::make_int(node->loc(), std::stoi(repr)); + + program.push_instr(OPCODE_LOAD_CONST, + program.push_constant(value)); + } break; + case NODE_VARDECL: { std::string ident = node->child(0).lock()->repr(); auto expr = node->child(1).lock(); diff --git a/src/Lexer.cpp b/src/Lexer.cpp index f028f40..3db6e15 100644 --- a/src/Lexer.cpp +++ b/src/Lexer.cpp @@ -14,6 +14,7 @@ namespace grino add_keyword(NODE_BOOL, "true", true); add_keyword(NODE_BOOL, "false", true); + m_scanners.push_back(std::bind(&Lexer::scan_int, this)); m_scanners.push_back(std::bind(&Lexer::scan_ident, this)); } @@ -222,4 +223,35 @@ namespace grino return std::nullopt; } + + std::optional Lexer::scan_int() + { + size_t cursor = m_cursor; + std::string repr; + + if (has_more(cursor) && at(cursor) == '-') + { + repr += "-"; + cursor++; + } + + while (has_more(cursor) + && std::isdigit(at(cursor))) + { + repr += at(cursor); + cursor++; + } + + if (repr.empty() + || repr.back() == '-') + { + return std::nullopt; + } + + return ScanInfo { + cursor, + NODE_INT, + repr + }; + } } diff --git a/src/Lexer.hpp b/src/Lexer.hpp index 5f82807..6ccebeb 100644 --- a/src/Lexer.hpp +++ b/src/Lexer.hpp @@ -58,6 +58,7 @@ namespace grino bool has_value); std::optional scan_ident(); + std::optional scan_int(); }; } diff --git a/src/Node.hpp b/src/Node.hpp index 30d2bfa..513bfc3 100644 --- a/src/Node.hpp +++ b/src/Node.hpp @@ -7,6 +7,7 @@ #define NODE_TYPE(G) \ G(NODE_MODULE), \ G(NODE_BOOL), \ + G(NODE_INT), \ G(NODE_VARDECL), \ G(NODE_FUNCALL), \ G(NODE_IDENT), \ diff --git a/src/Parser.cpp b/src/Parser.cpp index d088c99..7728655 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -128,7 +128,8 @@ namespace grino } if (type_is(NODE_IDENT) - || type_is(NODE_BOOL)) + || type_is(NODE_BOOL) + || type_is(NODE_INT)) { return consume(); } diff --git a/src/Value.cpp b/src/Value.cpp index 9259bdc..55e0c6e 100644 --- a/src/Value.cpp +++ b/src/Value.cpp @@ -17,6 +17,14 @@ namespace grino return value; } + /*static*/ std::shared_ptr Value::make_int(Loc const& loc, int val) + { + auto value = std::make_shared(loc); + value->m_type = TYPE_INT; + value->m_int_val = val; + return value; + } + /*static*/ std::shared_ptr Value::make_native_function(Loc const& loc, native_t val) @@ -40,6 +48,7 @@ namespace grino switch (m_type) { case TYPE_NIL: return ""; + case TYPE_INT: return std::to_string(*m_int_val); case TYPE_BOOL: return *m_bool_val ? "true" : "false"; case TYPE_FUNCTION: return ""; case TYPE_REF: return "&" + std::to_string(*m_ref_val); @@ -58,6 +67,7 @@ namespace grino { case TYPE_NIL: return true; case TYPE_BOOL: return *m_bool_val == *other.m_bool_val; + case TYPE_INT: return *m_int_val == *other.m_int_val; case TYPE_REF: return *m_ref_val == *other.m_ref_val; default: diff --git a/src/Value.hpp b/src/Value.hpp index 7b212a7..6d5f8bd 100644 --- a/src/Value.hpp +++ b/src/Value.hpp @@ -13,6 +13,7 @@ namespace grino public: static std::shared_ptr make_nil(Loc const& loc); static std::shared_ptr make_bool(Loc const& loc, bool val); + static std::shared_ptr make_int(Loc const& loc, int val); static std::shared_ptr make_native_function(Loc const& loc, native_t val); static std::shared_ptr make_ref(Loc const& loc, @@ -24,6 +25,7 @@ namespace grino Loc loc() const { return m_loc; } TypeType type() const { return m_type; } bool as_bool() const { return *m_bool_val; } + int as_int() const { return *m_int_val; } std::shared_ptr as_function() const { return m_function_val; } size_t as_ref() const { return *m_ref_val; } @@ -34,6 +36,7 @@ namespace grino Loc m_loc; TypeType m_type = TYPE_NIL; std::optional m_bool_val; + std::optional m_int_val; std::shared_ptr m_function_val; std::optional m_ref_val; }; diff --git a/src/commons.hpp b/src/commons.hpp index 36354ec..403478b 100644 --- a/src/commons.hpp +++ b/src/commons.hpp @@ -2,6 +2,7 @@ #define grino_COMMONS_HPP #include +#include #include #include #include diff --git a/src/types.hpp b/src/types.hpp index d989f6d..8a0cf5f 100644 --- a/src/types.hpp +++ b/src/types.hpp @@ -6,6 +6,7 @@ #define TYPES(G) \ G(TYPE_NIL), \ G(TYPE_BOOL), \ + G(TYPE_INT), \ G(TYPE_FUNCTION), \ G(TYPE_REF) diff --git a/tests/Lexer.cpp b/tests/Lexer.cpp index 642f3c0..ccab9c0 100644 --- a/tests/Lexer.cpp +++ b/tests/Lexer.cpp @@ -77,3 +77,16 @@ TEST_CASE_METHOD(LexerTest, "Lexer_no_end_space") test_next(lexer, "CPAR"); test_end(lexer); } + +TEST_CASE_METHOD(LexerTest, "Lexer_int") +{ + grino::Lexer lexer {m_logger, "tests/lexer"}; + + lexer.scan("4 39 256 2 -7"); + test_next(lexer, "INT[4]"); + test_next(lexer, "INT[39]"); + test_next(lexer, "INT[256]"); + test_next(lexer, "INT[2]"); + test_next(lexer, "INT[-7]"); + test_end(lexer); +} diff --git a/tests/Parser.cpp b/tests/Parser.cpp index 72ac39e..f01921c 100644 --- a/tests/Parser.cpp +++ b/tests/Parser.cpp @@ -48,3 +48,9 @@ TEST_CASE_METHOD(ParserTest, "Parser_funcall") test_parse("MODULE(FUNCALL(IDENT[f],BOOL[false],BOOL[true]))", "(f false true)"); } + +TEST_CASE_METHOD(ParserTest, "Parser_integer") +{ + test_parse("MODULE(FUNCALL(IDENT[hello],INT[2],INT[34]))", + "(hello 2 34)"); +}