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