ADD: strings.
parent
f2c9a6248a
commit
25bd1f28f6
|
@ -4,6 +4,7 @@ EXPR ::=
|
|||
| int
|
||||
| ident
|
||||
| float
|
||||
| string
|
||||
| VARDECL
|
||||
| FUNDECL
|
||||
| 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);
|
||||
|
||||
extern "C" void lib_array(grino::Loader& loader)
|
||||
extern "C" void lib_collection(grino::Loader& loader)
|
||||
{
|
||||
loader.add_native("ref", [&loader](auto args){
|
||||
std::vector<size_t> idxs;
|
||||
|
@ -14,6 +14,12 @@ extern "C" void lib_array(grino::Loader& loader)
|
|||
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::shared_ptr<grino::Value>, std::vector<size_t>)>
|
||||
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){
|
||||
auto ref_val = args[0];
|
||||
size_t ref = ref_val->as_ref();
|
||||
auto val = args[0];
|
||||
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 = array_val->as_array();
|
||||
|
||||
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){
|
||||
auto ref_val = args[0];
|
||||
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);
|
||||
});
|
||||
|
||||
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)
|
||||
|
@ -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)
|
||||
{
|
||||
lib_assert(loader);
|
||||
|
@ -619,7 +667,9 @@ extern "C" void lib(grino::Loader& loader)
|
|||
lib_cmp(loader);
|
||||
lib_bool(loader);
|
||||
lib_flow_control(loader);
|
||||
lib_collection(loader);
|
||||
lib_array(loader);
|
||||
lib_string(loader);
|
||||
|
||||
loader.add_native("dump", [](auto args){
|
||||
std::string sep;
|
||||
|
|
|
@ -154,6 +154,16 @@ namespace grino
|
|||
program.push_constant(value));
|
||||
} 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: {
|
||||
std::string ident = node->child(0).lock()->repr();
|
||||
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_int, this));
|
||||
m_scanners.push_back(std::bind(&Lexer::scan_string, this));
|
||||
m_scanners.push_back(std::bind(&Lexer::scan_ident, this));
|
||||
}
|
||||
|
||||
|
@ -150,6 +151,50 @@ namespace grino
|
|||
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::string const& text,
|
||||
bool has_value)
|
||||
|
|
|
@ -49,6 +49,7 @@ namespace grino
|
|||
std::string const& text,
|
||||
bool has_value = false);
|
||||
|
||||
std::optional<ScanInfo> scan_string();
|
||||
std::optional<ScanInfo> scan_text(NodeType type,
|
||||
std::string const& text,
|
||||
bool has_value);
|
||||
|
|
|
@ -22,7 +22,8 @@
|
|||
G(NODE_CSQUARE), \
|
||||
G(NODE_BLOCK), \
|
||||
G(NODE_ARRAY), \
|
||||
G(NODE_FLOAT)
|
||||
G(NODE_FLOAT), \
|
||||
G(NODE_STRING)
|
||||
|
||||
namespace grino
|
||||
{
|
||||
|
|
|
@ -151,7 +151,8 @@ namespace grino
|
|||
if (type_is(NODE_IDENT)
|
||||
|| type_is(NODE_BOOL)
|
||||
|| type_is(NODE_INT)
|
||||
|| type_is(NODE_FLOAT))
|
||||
|| type_is(NODE_FLOAT)
|
||||
|| type_is(NODE_STRING))
|
||||
{
|
||||
return consume();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "Value.hpp"
|
||||
#include "Program.hpp"
|
||||
#include "src/types.hpp"
|
||||
|
||||
namespace grino
|
||||
{
|
||||
|
@ -104,6 +105,16 @@ namespace grino
|
|||
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
|
||||
{
|
||||
return m_program_val;
|
||||
|
@ -114,6 +125,7 @@ namespace grino
|
|||
switch (m_type)
|
||||
{
|
||||
case TYPE_NIL: return "<nil>";
|
||||
case TYPE_STRING: return *m_string_val;
|
||||
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";
|
||||
|
@ -149,6 +161,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_STRING: return *m_string_val == *other.m_string_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;
|
||||
|
|
|
@ -37,6 +37,9 @@ namespace grino
|
|||
static std::shared_ptr<Value> make_array(Loc const& loc,
|
||||
val_array_t val);
|
||||
|
||||
static std::shared_ptr<Value> make_string(Loc const& loc,
|
||||
std::string const& val);
|
||||
|
||||
explicit Value(Loc const& loc);
|
||||
virtual ~Value() = default;
|
||||
|
||||
|
@ -50,6 +53,7 @@ namespace grino
|
|||
std::shared_ptr<Program> as_program() const;
|
||||
val_array_t& as_array() { 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;
|
||||
bool equals(Value const& other) const;
|
||||
|
@ -64,6 +68,7 @@ namespace grino
|
|||
std::shared_ptr<Program> m_program_val;
|
||||
std::optional<size_t> m_ref_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_PROGRAM), \
|
||||
G(TYPE_FLOAT), \
|
||||
G(TYPE_ARRAY)
|
||||
G(TYPE_ARRAY), \
|
||||
G(TYPE_STRING)
|
||||
|
||||
|
||||
namespace grino
|
||||
|
|
|
@ -119,3 +119,13 @@ TEST_CASE_METHOD(LexerTest, "Lexer_float")
|
|||
test_next(lexer, "FLOAT[1.0]");
|
||||
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])))",
|
||||
"(: [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