diff --git a/doc/grammar.bnf b/doc/grammar.bnf index 3c3a2be..138f3e4 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -3,6 +3,7 @@ EXPR ::= bool | int | ident +| float | VARDECL | FUNDECL | FUNCALL diff --git a/examples/float.gri b/examples/float.gri new file mode 100644 index 0000000..0e2ab31 --- /dev/null +++ b/examples/float.gri @@ -0,0 +1,26 @@ +(assert= 3.2 3.2) +(assert= false (eq? 3.7 7.2)) +(assert= true (ne? 3.7 7.2)) + +(assert= 3.1 (+. 1.0 2.1)) +(assert= -1.1 (-. 1.0 2.1)) +(assert= 7.8 (*. 5.2 1.5)) +(assert= 2.5 (/. 5.0 2.0)) +(assert= 1.25 (%. 10.0 1.75)) +(assert= 8.0 (^. 2.0 3.0)) + +(assert= true (< 1.0 2.0)) +(assert= false (< 2.0 2.0)) +(assert= false (< 3.0 2.0)) + +(assert= true (<= 1.0 2.0)) +(assert= true (<= 2.0 2.0)) +(assert= false (<= 3.0 2.0)) + +(assert= true (> 18.0 5.3)) +(assert= false (> 18.0 18.0)) +(assert= false (> 18.0 115.3)) + +(assert= true (>= 18.0 5.3)) +(assert= true (>= 18.0 18.0)) +(assert= false (>= 18.0 115.3)) \ No newline at end of file diff --git a/lib/core.cpp b/lib/core.cpp index c3a6e7d..08368e4 100644 --- a/lib/core.cpp +++ b/lib/core.cpp @@ -267,41 +267,236 @@ extern "C" void lib_int(grino::Loader& loader) }); } +extern "C" void lib_float(grino::Loader& loader) +{ + loader.add_native("+.", [](auto args){ + float result = 0; + + for (auto value: args) + { + result += value->as_float(); + } + + grino::Loc loc {"???", 0}; + + if (args.empty() == false) + { + loc = args.front()->loc(); + } + + return grino::Value::make_float(loc, result); + }); + + loader.add_native("*.", [](auto args){ + float result = 1; + + for (auto value: args) + { + result *= value->as_float(); + } + + grino::Loc loc {"???", 0}; + + if (args.empty() == false) + { + loc = args.front()->loc(); + } + + return grino::Value::make_float(loc, result); + }); + + loader.add_native("-.", [](auto args){ + grino::Loc loc {"???", 0}; + + if (args.empty() == false) + { + loc = args.front()->loc(); + } + else + { + return grino::Value::make_float(loc, 0); + } + + float result = args[0]->as_float(); + + if (args.size() == 1) + { + return grino::Value::make_float(loc, -result); + } + + for (size_t i=1; ias_float(); + } + + return grino::Value::make_float(loc, result); + }); + + loader.add_native("/.", [](auto args){ + grino::Loc loc {"???", 0}; + + if (args.empty() == false) + { + loc = args.front()->loc(); + } + else + { + return grino::Value::make_float(loc, 1.0f); + } + + float result = args[0]->as_float(); + + if (args.size() == 1) + { + return grino::Value::make_float(loc, result); + } + + for (size_t i=1; ias_float(); + } + + return grino::Value::make_float(loc, result); + }); + + loader.add_native("%.", [](auto args){ + grino::Loc loc {"???", 0}; + + if (args.empty() == false) + { + loc = args.front()->loc(); + } + else + { + return grino::Value::make_float(loc, 1); + } + + float result = args[0]->as_float(); + + if (args.size() == 1) + { + return grino::Value::make_float(loc, result); + } + + for (size_t i=1; ias_float()); + } + + return grino::Value::make_float(loc, result); + }); + + loader.add_native("^.", [](auto args){ + grino::Loc loc {"???", 0}; + + if (args.empty() == false) + { + loc = args.front()->loc(); + } + else + { + return grino::Value::make_float(loc, 1); + } + + float result = args[0]->as_float(); + + if (args.size() == 1) + { + return grino::Value::make_int(loc, result); + } + + for (size_t i=1; ias_float()); + } + + return grino::Value::make_float(loc, result); + }); +} + extern "C" void lib_cmp(grino::Loader& loader) { loader.add_native("<", [](auto args){ - int lhs = args[0]->as_int(); - int rhs = args[1]->as_int(); + if (args[0]->type() == grino::TYPE_INT) + { + int lhs = args[0]->as_int(); + int rhs = args[1]->as_int(); - return grino::Value::make_bool(args[0]->loc(), lhs < rhs); + return grino::Value::make_bool(args[0]->loc(), lhs < rhs); + } + else if (args[0]->type() == grino::TYPE_FLOAT) + { + float lhs = args[0]->as_float(); + float rhs = args[1]->as_float(); + + return grino::Value::make_bool(args[0]->loc(), lhs < rhs); + } + + assert(0); }); loader.add_native("<=", [](auto args){ - int lhs = args[0]->as_int(); - int rhs = args[1]->as_int(); + if (args[0]->type() == grino::TYPE_INT) + { + int lhs = args[0]->as_int(); + int rhs = args[1]->as_int(); - return grino::Value::make_bool(args[0]->loc(), lhs <= rhs); + return grino::Value::make_bool(args[0]->loc(), lhs <= rhs); + } + else if (args[0]->type() == grino::TYPE_FLOAT) + { + float lhs = args[0]->as_float(); + float rhs = args[1]->as_float(); + + return grino::Value::make_bool(args[0]->loc(), lhs <= rhs); + } + + assert(0); }); loader.add_native(">", [](auto args){ - int lhs = args[0]->as_int(); - int rhs = args[1]->as_int(); + if (args[0]->type() == grino::TYPE_INT) + { + int lhs = args[0]->as_int(); + int rhs = args[1]->as_int(); - return grino::Value::make_bool(args[0]->loc(), lhs > rhs); + return grino::Value::make_bool(args[0]->loc(), lhs > rhs); + } + else if (args[0]->type() == grino::TYPE_FLOAT) + { + float lhs = args[0]->as_float(); + float rhs = args[1]->as_float(); + + return grino::Value::make_bool(args[0]->loc(), lhs > rhs); + } + + assert(0); }); loader.add_native(">=", [](auto args){ - int lhs = args[0]->as_int(); - int rhs = args[1]->as_int(); + if (args[0]->type() == grino::TYPE_INT) + { + int lhs = args[0]->as_int(); + int rhs = args[1]->as_int(); - return grino::Value::make_bool(args[0]->loc(), lhs >= rhs); + return grino::Value::make_bool(args[0]->loc(), lhs >= rhs); + } + else if (args[0]->type() == grino::TYPE_FLOAT) + { + float lhs = args[0]->as_float(); + float rhs = args[1]->as_float(); + + return grino::Value::make_bool(args[0]->loc(), lhs >= rhs); + } + + assert(0); }); loader.add_native("ne?", [](auto args){ - int lhs = args[0]->as_int(); - int rhs = args[1]->as_int(); + auto lhs = args[0]; + auto rhs = args[1]; - return grino::Value::make_bool(args[0]->loc(), lhs != rhs); + return grino::Value::make_bool(args[0]->loc(), !lhs->equals(*rhs)); }); } @@ -420,6 +615,7 @@ extern "C" void lib(grino::Loader& loader) { lib_assert(loader); lib_int(loader); + lib_float(loader); lib_cmp(loader); lib_bool(loader); lib_flow_control(loader); diff --git a/src/Compiler.cpp b/src/Compiler.cpp index fd851f0..f6ab0e4 100644 --- a/src/Compiler.cpp +++ b/src/Compiler.cpp @@ -145,6 +145,15 @@ namespace grino program.push_constant(value)); } break; + case NODE_FLOAT: { + std::string repr = node->repr(); + + auto value = Value::make_float(node->loc(), std::stof(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 59f8e69..428be8b 100644 --- a/src/Lexer.cpp +++ b/src/Lexer.cpp @@ -18,6 +18,7 @@ namespace grino add_keyword(NODE_BOOL, "true", true); add_keyword(NODE_BOOL, "false", true); + m_scanners.push_back(std::bind(&Lexer::scan_float, this)); m_scanners.push_back(std::bind(&Lexer::scan_int, this)); m_scanners.push_back(std::bind(&Lexer::scan_ident, this)); } @@ -258,4 +259,51 @@ namespace grino repr }; } + + std::optional Lexer::scan_float() + { + 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 (!has_more(cursor) || at(cursor) != '.') + { + return std::nullopt; + } + + repr += at(cursor); + cursor++; + + while (has_more(cursor) + && std::isdigit(at(cursor))) + { + repr += at(cursor); + cursor++; + } + + if (repr.empty() + || repr.back() == '-' + || repr.back() == '.') + { + return std::nullopt; + } + + return ScanInfo { + cursor, + NODE_FLOAT, + repr + }; + } } diff --git a/src/Lexer.hpp b/src/Lexer.hpp index 6ccebeb..cf02857 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_float(); std::optional scan_int(); }; } diff --git a/src/Node.hpp b/src/Node.hpp index c960a49..e3d2e85 100644 --- a/src/Node.hpp +++ b/src/Node.hpp @@ -21,7 +21,8 @@ G(NODE_OSQUARE), \ G(NODE_CSQUARE), \ G(NODE_BLOCK), \ - G(NODE_ARRAY) + G(NODE_ARRAY), \ + G(NODE_FLOAT) namespace grino { diff --git a/src/Parser.cpp b/src/Parser.cpp index e899506..cc9839b 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -150,7 +150,8 @@ namespace grino if (type_is(NODE_IDENT) || type_is(NODE_BOOL) - || type_is(NODE_INT)) + || type_is(NODE_INT) + || type_is(NODE_FLOAT)) { return consume(); } diff --git a/src/Value.cpp b/src/Value.cpp index 3deb0d8..b9c8a78 100644 --- a/src/Value.cpp +++ b/src/Value.cpp @@ -26,6 +26,14 @@ namespace grino return value; } + /*static*/ std::shared_ptr Value::make_float(Loc const& loc, float val) + { + auto value = std::make_shared(loc); + value->m_type = TYPE_FLOAT; + value->m_float_val = val; + return value; + } + /*static*/ std::shared_ptr Value::make_native_function(Loc const& loc, native_t val) @@ -107,6 +115,7 @@ namespace grino { case TYPE_NIL: return ""; case TYPE_INT: return std::to_string(*m_int_val); + case TYPE_FLOAT: return std::to_string(*m_float_val); case TYPE_BOOL: return *m_bool_val ? "true" : "false"; case TYPE_FUNCTION: return ""; case TYPE_REF: return "&" + std::to_string(*m_ref_val); @@ -140,6 +149,8 @@ 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_FLOAT: + return std::fabs(*m_float_val - *other.m_float_val) < FLOAT_APPROX; case TYPE_REF: return *m_ref_val == *other.m_ref_val; case TYPE_PROGRAM: return false; diff --git a/src/Value.hpp b/src/Value.hpp index 4b5662e..b013901 100644 --- a/src/Value.hpp +++ b/src/Value.hpp @@ -18,6 +18,7 @@ namespace grino 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_float(Loc const& loc, float val); static std::shared_ptr make_native_function(Loc const& loc, native_t val); static std::shared_ptr make_function(Loc const& loc, @@ -42,6 +43,7 @@ namespace grino Loc loc() const { return m_loc; } TypeType type() const { return m_type; } bool as_bool() const { return *m_bool_val; } + float as_float() const { return *m_float_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; } @@ -57,6 +59,7 @@ namespace grino TypeType m_type = TYPE_NIL; std::optional m_bool_val; std::optional m_int_val; + std::optional m_float_val; std::shared_ptr m_function_val; std::shared_ptr m_program_val; std::optional m_ref_val; diff --git a/src/commons.hpp b/src/commons.hpp index 403478b..2127973 100644 --- a/src/commons.hpp +++ b/src/commons.hpp @@ -16,4 +16,6 @@ #include "config.hpp" #include "mutils.hpp" +#define FLOAT_APPROX 0.001f + #endif diff --git a/src/types.hpp b/src/types.hpp index a91dc00..de637cf 100644 --- a/src/types.hpp +++ b/src/types.hpp @@ -10,6 +10,7 @@ G(TYPE_FUNCTION), \ G(TYPE_REF), \ G(TYPE_PROGRAM), \ + G(TYPE_FLOAT), \ G(TYPE_ARRAY) diff --git a/tests/Lexer.cpp b/tests/Lexer.cpp index fe8f788..5f9d7ee 100644 --- a/tests/Lexer.cpp +++ b/tests/Lexer.cpp @@ -109,12 +109,13 @@ TEST_CASE_METHOD(LexerTest, "Lexer_block") test_end(lexer); } -TEST_CASE_METHOD(LexerTest, "Lexer_array") +TEST_CASE_METHOD(LexerTest, "Lexer_float") { grino::Lexer lexer {m_logger, "tests/lexer"}; - lexer.scan(" [] "); - test_next(lexer, "OSQUARE"); - test_next(lexer, "CSQUARE"); + lexer.scan(" 3.12 -0.7 1.0"); + test_next(lexer, "FLOAT[3.12]"); + test_next(lexer, "FLOAT[-0.7]"); + test_next(lexer, "FLOAT[1.0]"); test_end(lexer); } diff --git a/tests/Parser.cpp b/tests/Parser.cpp index 5136bf4..8cca787 100644 --- a/tests/Parser.cpp +++ b/tests/Parser.cpp @@ -93,3 +93,9 @@ TEST_CASE_METHOD(ParserTest, "Parser_array") test_parse("MODULE(BLOCK(ARRAY(INT[1],BOOL[true],IDENT[salut])))", "(: [1 true salut] )"); } + +TEST_CASE_METHOD(ParserTest, "Parser_float") +{ + test_parse("MODULE(BLOCK(ARRAY(INT[1],FLOAT[28.5],IDENT[salut])))", + "(: [1 28.5 salut] )"); +}