ADD: strings.
parent
f2c9a6248a
commit
25bd1f28f6
|
@ -4,6 +4,7 @@ EXPR ::=
|
||||||
| int
|
| int
|
||||||
| ident
|
| ident
|
||||||
| float
|
| float
|
||||||
|
| string
|
||||||
| VARDECL
|
| VARDECL
|
||||||
| FUNDECL
|
| FUNDECL
|
||||||
| FUNCALL
|
| FUNCALL
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
(assert= 'hello' 'hello')
|
||||||
|
(assert (not (eq? 'bim' 'bam')))
|
||||||
|
|
||||||
|
(assert= false (empty? 'coucou'))
|
||||||
|
(assert= true (empty? ''))
|
||||||
|
|
||||||
|
(assert= 'e' (ref 'hello' 1))
|
||||||
|
(assert= 5 (len 'hello'))
|
||||||
|
|
||||||
|
(assert= 'aaa' (dup 'a' 3))
|
||||||
|
(assert= 'abcdefg' (cat 'abc' 'def' 'g'))
|
74
lib/core.cpp
74
lib/core.cpp
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
GRINO_ERROR(assertion_error);
|
GRINO_ERROR(assertion_error);
|
||||||
|
|
||||||
extern "C" void lib_array(grino::Loader& loader)
|
extern "C" void lib_collection(grino::Loader& loader)
|
||||||
{
|
{
|
||||||
loader.add_native("ref", [&loader](auto args){
|
loader.add_native("ref", [&loader](auto args){
|
||||||
std::vector<size_t> idxs;
|
std::vector<size_t> idxs;
|
||||||
|
@ -14,6 +14,12 @@ extern "C" void lib_array(grino::Loader& loader)
|
||||||
idxs.push_back(args[i]->as_int());
|
idxs.push_back(args[i]->as_int());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (args[0]->type() == grino::TYPE_STRING)
|
||||||
|
{
|
||||||
|
auto char_val = std::string(1, args[0]->as_string()[args[1]->as_int()]);
|
||||||
|
return grino::Value::make_string(args[0]->loc(), char_val);
|
||||||
|
}
|
||||||
|
|
||||||
std::function<std::shared_ptr<grino::Value>
|
std::function<std::shared_ptr<grino::Value>
|
||||||
(std::shared_ptr<grino::Value>, std::vector<size_t>)>
|
(std::shared_ptr<grino::Value>, std::vector<size_t>)>
|
||||||
f = [&](std::shared_ptr<grino::Value> val, std::vector<size_t> indexes){
|
f = [&](std::shared_ptr<grino::Value> val, std::vector<size_t> indexes){
|
||||||
|
@ -37,14 +43,38 @@ extern "C" void lib_array(grino::Loader& loader)
|
||||||
});
|
});
|
||||||
|
|
||||||
loader.add_native("empty?", [&loader](auto args){
|
loader.add_native("empty?", [&loader](auto args){
|
||||||
auto ref_val = args[0];
|
auto val = args[0];
|
||||||
size_t ref = ref_val->as_ref();
|
if (val->type() == grino::TYPE_STRING)
|
||||||
|
{
|
||||||
|
std::string str = val->as_string();
|
||||||
|
return grino::Value::make_bool(val->loc(), str.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ref = val->as_ref();
|
||||||
auto array_val = loader.vm().heap(ref);
|
auto array_val = loader.vm().heap(ref);
|
||||||
auto& array = array_val->as_array();
|
auto& array = array_val->as_array();
|
||||||
|
|
||||||
return grino::Value::make_bool(array_val->loc(), array.empty());
|
return grino::Value::make_bool(array_val->loc(), array.empty());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
loader.add_native("len", [&loader](auto args){
|
||||||
|
auto val = args[0];
|
||||||
|
|
||||||
|
if (val->type() == grino::TYPE_STRING)
|
||||||
|
{
|
||||||
|
return grino::Value::make_int(val->loc(), val->as_string().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ref = val->as_ref();
|
||||||
|
auto array_val = loader.vm().heap(ref);
|
||||||
|
auto& array = array_val->as_array();
|
||||||
|
|
||||||
|
return grino::Value::make_int(array_val->loc(), array.size());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void lib_array(grino::Loader& loader)
|
||||||
|
{
|
||||||
loader.add_native("head", [&loader](auto args){
|
loader.add_native("head", [&loader](auto args){
|
||||||
auto ref_val = args[0];
|
auto ref_val = args[0];
|
||||||
size_t ref = ref_val->as_ref();
|
size_t ref = ref_val->as_ref();
|
||||||
|
@ -87,15 +117,6 @@ extern "C" void lib_array(grino::Loader& loader)
|
||||||
|
|
||||||
return grino::Value::make_ref(array_val->loc(), addr);
|
return grino::Value::make_ref(array_val->loc(), addr);
|
||||||
});
|
});
|
||||||
|
|
||||||
loader.add_native("len", [&loader](auto args){
|
|
||||||
auto ref_val = args[0];
|
|
||||||
size_t ref = ref_val->as_ref();
|
|
||||||
auto array_val = loader.vm().heap(ref);
|
|
||||||
auto& array = array_val->as_array();
|
|
||||||
|
|
||||||
return grino::Value::make_int(array_val->loc(), array.size());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void lib_flow_control(grino::Loader& loader)
|
extern "C" void lib_flow_control(grino::Loader& loader)
|
||||||
|
@ -611,6 +632,33 @@ extern "C" void lib_assert(grino::Loader& loader)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" void lib_string(grino::Loader& loader)
|
||||||
|
{
|
||||||
|
loader.add_native("dup", [](auto args) {
|
||||||
|
std::string value = args[0]->as_string();
|
||||||
|
size_t count = args[1]->as_int();
|
||||||
|
std::string result;
|
||||||
|
|
||||||
|
for (size_t i=0; i<count; i++)
|
||||||
|
{
|
||||||
|
result += value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return grino::Value::make_string(args[0]->loc(), result);
|
||||||
|
});
|
||||||
|
|
||||||
|
loader.add_native("cat", [](auto args) {
|
||||||
|
std::string result;
|
||||||
|
|
||||||
|
for (auto arg: args)
|
||||||
|
{
|
||||||
|
result += arg->as_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
return grino::Value::make_string(args[0]->loc(), result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" void lib(grino::Loader& loader)
|
extern "C" void lib(grino::Loader& loader)
|
||||||
{
|
{
|
||||||
lib_assert(loader);
|
lib_assert(loader);
|
||||||
|
@ -619,7 +667,9 @@ extern "C" void lib(grino::Loader& loader)
|
||||||
lib_cmp(loader);
|
lib_cmp(loader);
|
||||||
lib_bool(loader);
|
lib_bool(loader);
|
||||||
lib_flow_control(loader);
|
lib_flow_control(loader);
|
||||||
|
lib_collection(loader);
|
||||||
lib_array(loader);
|
lib_array(loader);
|
||||||
|
lib_string(loader);
|
||||||
|
|
||||||
loader.add_native("dump", [](auto args){
|
loader.add_native("dump", [](auto args){
|
||||||
std::string sep;
|
std::string sep;
|
||||||
|
|
|
@ -154,6 +154,16 @@ namespace grino
|
||||||
program.push_constant(value));
|
program.push_constant(value));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case NODE_STRING: {
|
||||||
|
std::string repr = node->repr();
|
||||||
|
std::string val = repr.substr(1, repr.size() - 2);
|
||||||
|
|
||||||
|
auto value = Value::make_string(node->loc(), val);
|
||||||
|
|
||||||
|
program.push_instr(OPCODE_LOAD_CONST,
|
||||||
|
program.push_constant(value));
|
||||||
|
} break;
|
||||||
|
|
||||||
case NODE_VARDECL: {
|
case NODE_VARDECL: {
|
||||||
std::string ident = node->child(0).lock()->repr();
|
std::string ident = node->child(0).lock()->repr();
|
||||||
auto expr = node->child(1).lock();
|
auto expr = node->child(1).lock();
|
||||||
|
|
|
@ -20,6 +20,7 @@ namespace grino
|
||||||
|
|
||||||
m_scanners.push_back(std::bind(&Lexer::scan_float, this));
|
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_int, this));
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_string, this));
|
||||||
m_scanners.push_back(std::bind(&Lexer::scan_ident, this));
|
m_scanners.push_back(std::bind(&Lexer::scan_ident, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,6 +151,50 @@ namespace grino
|
||||||
text, has_value));
|
text, has_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<ScanInfo> Lexer::scan_string()
|
||||||
|
{
|
||||||
|
size_t cursor = m_cursor;
|
||||||
|
std::string repr = "'";
|
||||||
|
|
||||||
|
auto has_delim = [&](size_t idx){
|
||||||
|
if (idx >= m_source.size()) { return false; }
|
||||||
|
bool is_escaped = idx > 0 && m_source[idx - 1] == '\\';
|
||||||
|
return m_source[idx] == '\'' && !is_escaped;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!has_delim(cursor))
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor++;
|
||||||
|
|
||||||
|
while (has_more(cursor)
|
||||||
|
&& !has_delim(cursor))
|
||||||
|
{
|
||||||
|
if (at(cursor) != '\\')
|
||||||
|
{
|
||||||
|
repr += at(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_delim(cursor))
|
||||||
|
{
|
||||||
|
repr += "'";
|
||||||
|
cursor++;
|
||||||
|
|
||||||
|
return ScanInfo {
|
||||||
|
cursor,
|
||||||
|
NODE_STRING,
|
||||||
|
repr
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<ScanInfo> Lexer::scan_text(NodeType type,
|
std::optional<ScanInfo> Lexer::scan_text(NodeType type,
|
||||||
std::string const& text,
|
std::string const& text,
|
||||||
bool has_value)
|
bool has_value)
|
||||||
|
|
|
@ -49,6 +49,7 @@ namespace grino
|
||||||
std::string const& text,
|
std::string const& text,
|
||||||
bool has_value = false);
|
bool has_value = false);
|
||||||
|
|
||||||
|
std::optional<ScanInfo> scan_string();
|
||||||
std::optional<ScanInfo> scan_text(NodeType type,
|
std::optional<ScanInfo> scan_text(NodeType type,
|
||||||
std::string const& text,
|
std::string const& text,
|
||||||
bool has_value);
|
bool has_value);
|
||||||
|
|
|
@ -22,7 +22,8 @@
|
||||||
G(NODE_CSQUARE), \
|
G(NODE_CSQUARE), \
|
||||||
G(NODE_BLOCK), \
|
G(NODE_BLOCK), \
|
||||||
G(NODE_ARRAY), \
|
G(NODE_ARRAY), \
|
||||||
G(NODE_FLOAT)
|
G(NODE_FLOAT), \
|
||||||
|
G(NODE_STRING)
|
||||||
|
|
||||||
namespace grino
|
namespace grino
|
||||||
{
|
{
|
||||||
|
|
|
@ -151,7 +151,8 @@ namespace grino
|
||||||
if (type_is(NODE_IDENT)
|
if (type_is(NODE_IDENT)
|
||||||
|| type_is(NODE_BOOL)
|
|| type_is(NODE_BOOL)
|
||||||
|| type_is(NODE_INT)
|
|| type_is(NODE_INT)
|
||||||
|| type_is(NODE_FLOAT))
|
|| type_is(NODE_FLOAT)
|
||||||
|
|| type_is(NODE_STRING))
|
||||||
{
|
{
|
||||||
return consume();
|
return consume();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "Value.hpp"
|
#include "Value.hpp"
|
||||||
#include "Program.hpp"
|
#include "Program.hpp"
|
||||||
|
#include "src/types.hpp"
|
||||||
|
|
||||||
namespace grino
|
namespace grino
|
||||||
{
|
{
|
||||||
|
@ -104,6 +105,16 @@ namespace grino
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*static*/
|
||||||
|
std::shared_ptr<Value> Value::make_string(Loc const& loc,
|
||||||
|
std::string const& val)
|
||||||
|
{
|
||||||
|
auto value = std::make_shared<Value>(loc);
|
||||||
|
value->m_type = TYPE_STRING;
|
||||||
|
value->m_string_val = val;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<Program> Value::as_program() const
|
std::shared_ptr<Program> Value::as_program() const
|
||||||
{
|
{
|
||||||
return m_program_val;
|
return m_program_val;
|
||||||
|
@ -114,6 +125,7 @@ namespace grino
|
||||||
switch (m_type)
|
switch (m_type)
|
||||||
{
|
{
|
||||||
case TYPE_NIL: return "<nil>";
|
case TYPE_NIL: return "<nil>";
|
||||||
|
case TYPE_STRING: return *m_string_val;
|
||||||
case TYPE_INT: return std::to_string(*m_int_val);
|
case TYPE_INT: return std::to_string(*m_int_val);
|
||||||
case TYPE_FLOAT: return std::to_string(*m_float_val);
|
case TYPE_FLOAT: return std::to_string(*m_float_val);
|
||||||
case TYPE_BOOL: return *m_bool_val ? "true" : "false";
|
case TYPE_BOOL: return *m_bool_val ? "true" : "false";
|
||||||
|
@ -149,6 +161,7 @@ namespace grino
|
||||||
case TYPE_NIL: return true;
|
case TYPE_NIL: return true;
|
||||||
case TYPE_BOOL: return *m_bool_val == *other.m_bool_val;
|
case TYPE_BOOL: return *m_bool_val == *other.m_bool_val;
|
||||||
case TYPE_INT: return *m_int_val == *other.m_int_val;
|
case TYPE_INT: return *m_int_val == *other.m_int_val;
|
||||||
|
case TYPE_STRING: return *m_string_val == *other.m_string_val;
|
||||||
case TYPE_FLOAT:
|
case TYPE_FLOAT:
|
||||||
return std::fabs(*m_float_val - *other.m_float_val) < FLOAT_APPROX;
|
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_REF: return *m_ref_val == *other.m_ref_val;
|
||||||
|
|
|
@ -37,6 +37,9 @@ namespace grino
|
||||||
static std::shared_ptr<Value> make_array(Loc const& loc,
|
static std::shared_ptr<Value> make_array(Loc const& loc,
|
||||||
val_array_t val);
|
val_array_t val);
|
||||||
|
|
||||||
|
static std::shared_ptr<Value> make_string(Loc const& loc,
|
||||||
|
std::string const& val);
|
||||||
|
|
||||||
explicit Value(Loc const& loc);
|
explicit Value(Loc const& loc);
|
||||||
virtual ~Value() = default;
|
virtual ~Value() = default;
|
||||||
|
|
||||||
|
@ -50,6 +53,7 @@ namespace grino
|
||||||
std::shared_ptr<Program> as_program() const;
|
std::shared_ptr<Program> as_program() const;
|
||||||
val_array_t& as_array() { return *m_array_val; }
|
val_array_t& as_array() { return *m_array_val; }
|
||||||
val_array_t const& as_array() const { return *m_array_val; }
|
val_array_t const& as_array() const { return *m_array_val; }
|
||||||
|
std::string const& as_string() const { return *m_string_val; }
|
||||||
|
|
||||||
std::string string() const;
|
std::string string() const;
|
||||||
bool equals(Value const& other) const;
|
bool equals(Value const& other) const;
|
||||||
|
@ -64,6 +68,7 @@ namespace grino
|
||||||
std::shared_ptr<Program> m_program_val;
|
std::shared_ptr<Program> m_program_val;
|
||||||
std::optional<size_t> m_ref_val;
|
std::optional<size_t> m_ref_val;
|
||||||
std::optional<val_array_t> m_array_val;
|
std::optional<val_array_t> m_array_val;
|
||||||
|
std::optional<std::string> m_string_val;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
G(TYPE_REF), \
|
G(TYPE_REF), \
|
||||||
G(TYPE_PROGRAM), \
|
G(TYPE_PROGRAM), \
|
||||||
G(TYPE_FLOAT), \
|
G(TYPE_FLOAT), \
|
||||||
G(TYPE_ARRAY)
|
G(TYPE_ARRAY), \
|
||||||
|
G(TYPE_STRING)
|
||||||
|
|
||||||
|
|
||||||
namespace grino
|
namespace grino
|
||||||
|
|
|
@ -119,3 +119,13 @@ TEST_CASE_METHOD(LexerTest, "Lexer_float")
|
||||||
test_next(lexer, "FLOAT[1.0]");
|
test_next(lexer, "FLOAT[1.0]");
|
||||||
test_end(lexer);
|
test_end(lexer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(LexerTest, "Lexer_strings")
|
||||||
|
{
|
||||||
|
grino::Lexer lexer {m_logger, "tests/lexer"};
|
||||||
|
|
||||||
|
lexer.scan(" ' hello ' '\\'bim\\'' ");
|
||||||
|
test_next(lexer, "STRING[' hello ']");
|
||||||
|
test_next(lexer, "STRING[''bim'']");
|
||||||
|
test_end(lexer);
|
||||||
|
}
|
||||||
|
|
|
@ -99,3 +99,9 @@ TEST_CASE_METHOD(ParserTest, "Parser_float")
|
||||||
test_parse("MODULE(BLOCK(ARRAY(INT[1],FLOAT[28.5],IDENT[salut])))",
|
test_parse("MODULE(BLOCK(ARRAY(INT[1],FLOAT[28.5],IDENT[salut])))",
|
||||||
"(: [1 28.5 salut] )");
|
"(: [1 28.5 salut] )");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(ParserTest, "Parser_string")
|
||||||
|
{
|
||||||
|
test_parse("MODULE(BLOCK(ARRAY(STRING['bim !'],FLOAT[28.5],IDENT[salut])))",
|
||||||
|
"(: ['bim !' 28.5 salut] )");
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue