1 /*
2 This file is part of GNU APL, a free implementation of the
3 ISO/IEC Standard 13751, "Programming Language APL, Extended"
4
5 Copyright (C) 2008-2016 Dr. Jürgen Sauermann
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <vector>
22
23 #include "Avec.hh"
24 #include "UCS_string_vector.hh"
25 #include "Value.hh"
26 #include "Workspace.hh"
27
28
29 //----------------------------------------------------------------------------
UCS_string_vector(const Value & val,bool surrogate)30 UCS_string_vector::UCS_string_vector(const Value & val, bool surrogate)
31 {
32 const ShapeItem var_count = val.get_rows();
33 const ShapeItem name_len = val.get_cols();
34
35 loop(v, var_count)
36 {
37 ShapeItem nidx = v*name_len;
38 const ShapeItem end = nidx + name_len;
39 UCS_string name;
40 loop(n, name_len)
41 {
42 const Unicode uni = val.get_ravel(nidx++).get_char_value();
43
44 if (n == 0 && Avec::is_quad(uni)) // leading ⎕
45 {
46 name.append(uni);
47 continue;
48 }
49
50 if (Avec::is_symbol_char(uni)) // valid symbol char
51 {
52 name.append(uni);
53 continue;
54 }
55 // end of (first) name reached. At this point we expect either
56 // spaces until 'end' or some spaces and another name.
57 //
58 if (uni != UNI_ASCII_SPACE)
59 {
60 name.clear();
61 break;
62 }
63
64 // we have reached the end of the first name. At this point
65 // there could be:
66 //
67 // 1. spaces until 'end' (= one name), or
68 // 2. a second name (alias)
69
70 // skip spaces from nidx and subsequent spaces
71 //
72 while (nidx < end &&
73 val.get_ravel(nidx).get_char_value() == UNI_ASCII_SPACE)
74 ++nidx;
75
76
77 if (nidx == end) break; // only spaces (no second name)
78
79 // at this point we maybe have the start of a second name, which
80 // is an error (if last is false) or not. In both cases the first
81 // name can be ignored.
82 //
83 name.clear();
84 if (!surrogate) break; // error
85
86 // 'last' is true thus to_varnames() was called from ⎕SVO and
87 // the line may contains two variable names.
88 // Return the second (i.e. surrogate name)
89 //
90 surrogate = false;
91 while (nidx < end)
92 {
93 const Unicode uni = val.get_ravel(nidx++).get_char_value();
94 if (Avec::is_symbol_char(uni)) // valid symbol char
95 {
96 name.append(uni);
97 }
98 else if (uni == UNI_ASCII_SPACE)
99 {
100 break;
101 }
102 else
103 {
104 name.clear(); // error
105 break;
106 }
107 }
108 break;
109 }
110 push_back(name);
111 }
112 }
113 //----------------------------------------------------------------------------
114 void
compute_column_width(int tab_size,std::vector<int> & result)115 UCS_string_vector::compute_column_width(int tab_size, std::vector<int> & result)
116 {
117 const int quad_PW = Workspace::get_PW();
118
119 result.clear();
120
121 if (size() < 2)
122 {
123 if (size()) result.push_back(front().size());
124 else result.push_back(quad_PW);
125 return;
126 }
127
128 // compute block counts (one block having tab_size characters)
129 //
130 const int max_blocks = (quad_PW + 1) / tab_size;
131 std::vector<int> name_blocks;
132 loop(n, size()) name_blocks.push_back(1 + (1 + at(n).size()) / tab_size);
133
134 // compute max number of column blocks based on first line blocks
135 //
136 int max_col = -1;
137 {
138 int blocks = 0;
139 loop(n, size())
140 {
141 if ((blocks + name_blocks[n]) < max_blocks) // name_blocks[n] fits
142 blocks += name_blocks[n];
143 else // max_blocks exceeded
144 {
145 max_col = n - 1;
146 break;
147 }
148 }
149
150 if (max_col == -1) // all blocks fit
151 {
152 result.clear();
153 loop(n, size()) result.push_back(name_blocks[n]);
154 return;
155 }
156 }
157
158 // decrease max_col until all names fit...
159 //
160 for (;max_col > 1; --max_col)
161 {
162 result.clear();
163 int free_blocks = max_blocks;
164 loop(n, size()) // try to fit blocks into result
165 {
166 const int col_n = n % max_col;
167 const int bn = name_blocks[n];
168 if (n < max_col) // first row: append column
169 {
170 free_blocks -= bn;
171 if (free_blocks < 0) break;
172 result.push_back(bn);
173 }
174 else if (bn > result[col_n])
175 {
176 free_blocks -= bn - result[col_n];
177 if (free_blocks < 0) break;
178 result[col_n] = bn;
179 }
180 }
181
182 if (free_blocks >= 0) return; // success
183 }
184
185 // single colums
186 //
187 result.clear();
188 int max_nb = 0;
189 loop(n, size()) if (max_nb < name_blocks[n]) max_nb = name_blocks[n];
190 result.push_back(max_nb);
191 }
192 //----------------------------------------------------------------------------
193