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-2015  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 "Avec.hh"
22 #include "Common.hh"
23 #include "DiffOut.hh"
24 #include "InputFile.hh"
25 #include "IO_Files.hh"
26 #include "Output.hh"
27 #include "Performance.hh"
28 #include "UTF8_string.hh"
29 
30 //-----------------------------------------------------------------------------
31 int
overflow(int c)32 DiffOut::overflow(int c)
33 {
34 PERFORMANCE_START(cout_perf)
35    Output::set_color_mode(errout ? Output::COLM_UERROR : Output::COLM_OUTPUT);
36 
37    // expand LF to CRLF if desired
38    //
39    if (expand_LF && c == '\n')   cout << "\r";
40 
41    cout << char(c);
42 
43    if (!InputFile::is_validating())
44       {
45         PERFORMANCE_END(fs_COUT_B, cout_perf, 1)
46         return 0;
47       }
48 
49    if (c != '\n')   // not end of line
50       {
51         aplout += c;
52         PERFORMANCE_END(fs_COUT_B, cout_perf, 1)
53         return 0;
54       }
55 
56    // complete line received
57    //
58 ofstream & rep = IO_Files::get_current_testreport();
59    Assert(rep.is_open());
60 
61 const char * apl = aplout.c_str();
62 UTF8_string ref;
63 bool eof = false;
64    IO_Files::read_file_line(ref, eof);
65    if (eof)   // nothing in current_testfile
66       {
67         rep << "extra: " << apl << endl;
68       }
69    else if (different(utf8P(apl), utf8P(ref.c_str())))
70       {
71         IO_Files::diff_error();
72         rep << "apl: ⋅⋅⋅" << apl << "⋅⋅⋅" << endl
73             << "ref: ⋅⋅⋅" << ref.c_str() << "⋅⋅⋅" << endl;
74       }
75    else                    // same
76       {
77         rep << "== " << apl << endl;
78       }
79 
80    aplout.clear();
81    PERFORMANCE_END(fs_COUT_B, cout_perf, 1)
82    return 0;
83 }
84 //-----------------------------------------------------------------------------
85 bool
different(const UTF8 * apl,const UTF8 * ref)86 DiffOut::different(const UTF8 * apl, const UTF8 * ref)
87 {
88    for (;;)
89        {
90          int len_apl, len_ref;
91 
92          const Unicode a = UTF8_string::toUni(apl, len_apl, true);
93          const Unicode r = UTF8_string::toUni(ref, len_ref, true);
94          apl += len_apl;
95          ref += len_ref;
96 
97          if (a == r)   // same char
98             {
99               if (a == 0)   return false;   // not different
100               continue;
101             }
102 
103          // different chars: try special matches (⁰, ¹, ², or ⁿ)...
104 
105          if (r == UNI_PAD_U0)   // ⁰: match one or more digits
106             {
107               if (!Avec::is_digit(a))   return true;   // different
108 
109               // skip trailing digits
110               while (Avec::is_digit(Unicode(*apl)))   ++apl;
111               continue;
112             }
113 
114          if (r == UNI_PAD_U1)   // ¹: match zero or more spaces
115             {
116               if (a != UNI_ASCII_SPACE)   return true;   // different
117 
118               // skip trailing digits
119               while (*apl == ' ')   ++apl;
120               continue;
121             }
122 
123          if (r == UNI_PAD_U2)   // ²: match floating point number
124             {
125               if (!Avec::is_digit(a) &&
126                   a != UNI_OVERBAR   &&
127                   a != UNI_ASCII_E   &&
128                   a != UNI_ASCII_J   &&
129                   a != UNI_ASCII_FULLSTOP)   return true;   // different
130 
131               // skip trailing digits
132               //
133               for (;;)
134                   {
135                     if (Avec::is_digit(Unicode(*apl)) ||
136                         *apl == UNI_ASCII_E           ||
137                         *apl == UNI_ASCII_J           ||
138                         *apl == UNI_ASCII_FULLSTOP)   ++apl;
139                    else
140                       {
141                          int len;
142                          if (UTF8_string::toUni(apl, len, true) == UNI_OVERBAR)
143                             {
144                               apl += len;
145                               continue;
146                             }
147                          else break;
148                       }
149                   }
150 
151               continue;
152             }
153 
154          if (r == UNI_PAD_U3)   // ³: match anything
155             return false;   // not different
156 
157          if (r == UNI_PAD_U4)   // ⁴: match optional ¯
158             {
159               if (a != UNI_OVERBAR)   apl -= len_apl;   // restore apl
160               continue;
161             }
162 
163          if (r == UNI_PAD_U5)   // ⁵: match + or -
164             {
165               if (a != '+' && a != '-')   return true;   // different
166               continue;
167             }
168 
169          if (r == UNI_PAD_U6)   // ⁶: match 28 ⎕CR 42
170             {
171 #ifdef RATIONAL_NUMBERS_WANTED
172               if (a != '1')   return true;   // different
173 #else
174               if (a != '0')   return true;   // different
175 #endif
176               continue;
177             }
178          if (r == UNI_PAD_Un)   // ⁿ: optional unit multiplier
179             {
180               // ⁿ shall match an optional  unit multiplier, ie.
181               // m, n, u, or μ
182               //
183               if (a == 'm')       continue;
184               if (a == 'n')       continue;
185               if (a == 'u')       continue;
186               if (a == UNI_MUE)   continue;
187 
188               // no unit matches as well
189               apl -= len_apl;
190               continue;
191             }
192 
193          return true;   // different
194        }
195 
196    return false;   // not different
197 }
198 //-----------------------------------------------------------------------------
199