1 // Copyright 2016-2021 Doug Moen
2 // Licensed under the Apache License, version 2.0
3 // See accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0
4 
5 #include <libcurv/string.h>
6 #include <libcurv/list.h>
7 #include <libcurv/exception.h>
8 
9 namespace curv {
10 
is_string(Value val)11 bool is_string(Value val)
12 {
13     if (val.maybe<String>() != nullptr) return true;
14     if (auto list = val.maybe<List>()) {
15         for (auto c : *list)
16             if (!c.is_char()) return false;
17         return true;
18     }
19     return false;
20 }
21 
22 Shared<const String>
value_to_string(Value val,Fail fl,const Context & cx)23 value_to_string(Value val, Fail fl, const Context& cx)
24 {
25     if (auto str = val.maybe<const String>())
26         return str;
27     if (auto list = val.maybe<const List>()) {
28         auto str = make_uninitialized_string(list->size());
29         for (unsigned i = 0; i < list->size(); ++i) {
30             Value c = list->at(i);
31             if (c.is_char())
32                 str->at(i) = c.to_char_unsafe();
33             else goto error;
34         }
35         return str;
36     }
37   error:
38     FAIL(fl, nullptr, cx, stringify(val," is not a string"));
39 }
40 
41 const char String_Base::name[] = "string";
42 
43 Shared<String>
make_uninitialized_string(size_t len)44 make_uninitialized_string(size_t len)
45 {
46     return String::make(len);
47 }
48 
49 Shared<String>
make_string(const char * str,size_t len)50 make_string(const char* str, size_t len)
51 {
52     return String::make(str, len);
53 }
54 
55 Shared<const String>
to_print_string(Value val)56 to_print_string(Value val)
57 {
58     auto s = val.maybe<const String>();
59     if (s) return s;
60     String_Builder sb;
61     val.print_string(sb);
62     return sb.get_string();
63 }
64 
65 Shared<String>
get_string()66 String_Builder::get_string()
67 {
68     auto s = str(); // copies the data
69     return make_string(s.data(), s.size()); // copies the data again
70 }
71 Value
get_value()72 String_Builder::get_value()
73 {
74     auto s = str(); // copies the data
75     if (s.empty()) return {List::make(0)};
76     return {make_string(s.data(), s.size())}; // copies the data again
77 }
78 
79 void
print_repr(std::ostream & out) const80 String_Base::print_repr(std::ostream& out) const
81 {
82     write_curv_string(data_, 0, out);
83 }
84 
85 void
write_curv_string(const char * s,unsigned indent,std::ostream & out)86 write_curv_string(const char* s, unsigned indent, std::ostream& out)
87 {
88     // Normally, a String should not be empty, but be robust and handle anyway.
89     if (*s == '\0') {
90         out << "[]";
91         return;
92     }
93     out << '"';
94     for (; *s != '\0'; ++s) {
95         char c = *s;
96         if (c == '$') {
97             out << c;
98             if (is_dollar_next_char(s[1]))
99                 out << '_';
100         }
101         else if (c == '"')
102             out << "\"_";
103         else if (c == '\n') {
104             out << "\n";
105             for (unsigned i = 0; i < indent; ++i)
106                 out << " ";
107             if (s[1] != '\0')
108                 out << "|";
109         } else if (c == '\t')
110             out << c;
111         else if (c < ' ' || c > '~')
112             out << "$[" << unsigned(c) << "]";
113         else
114             out << c;
115     }
116     out << '"';
117 }
118 
119 void
write_curv_char(char c,char next,unsigned indent,std::ostream & out)120 write_curv_char(char c, char next, unsigned indent, std::ostream& out)
121 {
122     if (c == '$') {
123         out << c;
124         if (is_dollar_next_char(next))
125             out << '_';
126     }
127     else if (c == '"')
128         out << "\"_";
129     else if (c == '\n') {
130         out << "\n";
131         for (unsigned i = 0; i < indent; ++i)
132             out << " ";
133         if (next != '\0')
134             out << "|";
135     } else if (c == '\t')
136         out << c;
137     else if (c < ' ' || c > '~')
138         out << "$[" << unsigned(c) << "]";
139     else
140         out << c;
141 }
142 
143 } // namespace curv
144