filebuff.cpp revision 1472:c18cbe5936b8
1/*
2 * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24
25// FILEBUFF.CPP - Routines for handling a parser file buffer
26#include "adlc.hpp"
27
28using namespace std;
29
30//------------------------------FileBuff---------------------------------------
31// Create a new parsing buffer
32FileBuff::FileBuff( BufferedFile *fptr, ArchDesc& archDesc) : _fp(fptr), _AD(archDesc) {
33  _err = fseek(_fp->_fp, 0, SEEK_END);  // Seek to end of file
34  if (_err) {
35    file_error(SEMERR, 0, "File seek error reading input file");
36    exit(1);                    // Exit on seek error
37  }
38  _filepos = ftell(_fp->_fp);   // Find offset of end of file
39  _bufferSize = _filepos + 5;   // Filepos points to last char, so add padding
40  _err = fseek(_fp->_fp, 0, SEEK_SET);  // Reset to beginning of file
41  if (_err) {
42    file_error(SEMERR, 0, "File seek error reading input file\n");
43    exit(1);                    // Exit on seek error
44  }
45  _filepos = ftell(_fp->_fp);      // Reset current file position
46  _linenum = 0;
47
48  _bigbuf = new char[_bufferSize]; // Create buffer to hold text for parser
49  if( !_bigbuf ) {
50    file_error(SEMERR, 0, "Buffer allocation failed\n");
51    exit(1);                    // Exit on allocation failure
52  }
53  *_bigbuf = '\n';               // Lead with a sentinel newline
54  _buf = _bigbuf+1;                     // Skip sentinel
55  _bufmax = _buf;               // Buffer is empty
56  _bufeol = _bigbuf;              // _bufeol points at sentinel
57  _filepos = -1;                 // filepos is in sync with _bufeol
58  _bufoff = _offset = 0L;       // Offset at file start
59
60  _bufmax += fread(_buf, 1, _bufferSize-2, _fp->_fp); // Fill buffer & set end value
61  if (_bufmax == _buf) {
62    file_error(SEMERR, 0, "File read error, no input read\n");
63    exit(1);                     // Exit on read error
64  }
65  *_bufmax = '\n';               // End with a sentinel new-line
66  *(_bufmax+1) = '\0';           // Then end with a sentinel NULL
67}
68
69//------------------------------~FileBuff--------------------------------------
70// Nuke the FileBuff
71FileBuff::~FileBuff() {
72  delete _bigbuf;
73}
74
75//------------------------------get_line----------------------------------------
76char *FileBuff::get_line(void) {
77  char *retval;
78
79  // Check for end of file & return NULL
80  if (_bufeol >= _bufmax) return NULL;
81
82  _linenum++;
83  retval = ++_bufeol;      // return character following end of previous line
84  if (*retval == '\0') return NULL; // Check for EOF sentinel
85  // Search for newline character which must end each line
86  for(_filepos++; *_bufeol != '\n'; _bufeol++)
87    _filepos++;                    // keep filepos in sync with _bufeol
88  // _bufeol & filepos point at end of current line, so return pointer to start
89  return retval;
90}
91
92//------------------------------FileBuffRegion---------------------------------
93// Create a new region in a FileBuff.
94FileBuffRegion::FileBuffRegion( FileBuff* bufr, int soln, int ln,
95                                int off, int len)
96: _bfr(bufr), _sol(soln), _line(ln), _offset(off), _length(len) {
97  _next = NULL;                 // No chained regions
98}
99
100//------------------------------~FileBuffRegion--------------------------------
101// Delete the entire linked list of buffer regions.
102FileBuffRegion::~FileBuffRegion() {
103  if( _next ) delete _next;
104}
105
106//------------------------------copy-------------------------------------------
107// Deep copy a FileBuffRegion
108FileBuffRegion *FileBuffRegion::copy() {
109  if( !this ) return NULL;      // The empty buffer region
110  FileBuffRegion *br = new FileBuffRegion(_bfr,_sol,_line,_offset,_length);
111  if( _next ) br->_next = _next->copy();
112  return br;
113}
114
115//------------------------------merge------------------------------------------
116// Merge another buffer region into this buffer region.  Make overlapping areas
117// become a single region.  Remove (delete) the input FileBuffRegion.
118// Since the buffer regions are sorted by file offset, this is a varient of a
119// "sorted-merge" running in linear time.
120FileBuffRegion *FileBuffRegion::merge( FileBuffRegion *br ) {
121  if( !br ) return this;        // Merging nothing
122  if( !this ) return br;        // Merging into nothing
123
124  assert( _bfr == br->_bfr, "" );     // Check for pointer-equivalent buffers
125
126  if( _offset < br->_offset ) { // "this" starts before "br"
127    if( _offset+_length < br->_offset ) { // "this" ends before "br"
128      if( _next ) _next->merge( br );    // Merge with remainder of list
129      else _next = br;                 // No more in this list; just append.
130    } else {                           // Regions overlap.
131      int l = br->_offset + br->_length - _offset;
132      if( l > _length ) _length = l;     // Pick larger region
133      FileBuffRegion *nr = br->_next;     // Get rest of region
134      br->_next = NULL;         // Remove indication of rest of region
135      delete br;                // Delete this region (it's been subsumed).
136      if( nr ) merge( nr );     // Merge with rest of region
137    }                           // End of if regions overlap or not.
138  } else {                      // "this" starts after "br"
139    if( br->_offset+br->_length < _offset ) {    // "br" ends before "this"
140      FileBuffRegion *nr = new FileBuffRegion(_bfr,_sol,_line,_offset,_length);
141      nr->_next = _next;                // Structure copy "this" guy to "nr"
142      *this = *br;              // Structure copy "br" over "this".
143      br->_next = NULL;         // Remove indication of rest of region
144      delete br;                // Delete this region (it's been copied)
145      merge( nr );              // Finish merging
146    } else {                    // Regions overlap.
147      int l = _offset + _length - br->_offset;
148      if( l > _length ) _length = l;    // Pick larger region
149      _offset = br->_offset;            // Start with earlier region
150      _sol = br->_sol;                  // Also use earlier line start
151      _line = br->_line;                        // Also use earlier line
152      FileBuffRegion *nr = br->_next;   // Get rest of region
153      br->_next = NULL;         // Remove indication of rest of region
154      delete br;                // Delete this region (it's been subsumed).
155      if( nr ) merge( nr );     // Merge with rest of region
156    }                           // End of if regions overlap or not.
157  }
158  return this;
159}
160
161//------------------------------expandtab--------------------------------------
162static int expandtab( ostream &os, int off, char c, char fill1, char fill2 ) {
163  if( c == '\t' ) {             // Tab?
164    do os << fill1;             // Expand the tab; Output space
165    while( (++off) & 7 );       // Expand to tab stop
166  } else {                      // Normal character
167    os << fill2;                // Display normal character
168    off++;                      // Increment "cursor" offset
169  }
170  return off;
171}
172
173//------------------------------printline--------------------------------------
174// Print and highlite a region of a line.  Return the amount of highliting left
175// to do (i.e. highlite length minus length of line).
176static int printline( ostream& os, const char *fname, int line,
177                        const char *_sol, int skip, int len ) {
178
179  // Display the entire tab-expanded line
180  os << fname << ":" << line << ": ";
181  const char *t = strchr(_sol,'\n')+1; // End of line
182  int off = 0;                  // Cursor offset for tab expansion
183  const char *s = _sol;         // Nice string pointer
184  while( t-s ) {                // Display whole line
185    char c = *s++;              // Get next character to display
186    off = expandtab(os,off,c,' ',c);
187  }
188
189  // Display the tab-expanded skippings before underlining.
190  os << fname << ":" << line << ": ";
191  off = 0;                      // Cursor offset for tab expansion
192  s = _sol;                     // Restart string pointer
193
194  // Start underlining.
195  if( skip != -1 ) {            // The no-start-indicating flag
196    const char *u = _sol+skip;  // Amount to skip
197    while( u-s )                // Display skipped part
198      off = expandtab(os,off,*s++,' ',' ');
199    os << '^';                  // Start region
200    off++;                      // Moved cursor
201    len--;                      // 1 less char to do
202    if( *s++ == '\t' )          // Starting character is a tab?
203      off = expandtab(os,off,'\t','-','^');
204  }
205
206  // Long region doesn't end on this line
207  int llen = (int)(t-s);        // Length of line, minus what's already done
208  if( len > llen ) {            // Doing entire rest of line?
209    while( t-s )                // Display rest of line
210      off = expandtab(os,off,*s++,'-','-');
211    os << '\n';                 // EOL
212    return len-llen;            // Return what's not yet done.
213  }
214
215  // Region does end on this line.  This code fails subtly if the region ends
216  // in a tab character.
217  int i;
218  for( i=1; i<len; i++ )        // Underline just what's needed
219    off = expandtab(os,off,*s++,'-','-');
220  if( i == len ) os << '^';     // Mark end of region
221  os << '\n';                   // End of marked line
222  return 0;                     // All done
223}
224
225//------------------------------print------------------------------------------
226//std::ostream& operator<< ( std::ostream& os, FileBuffRegion &br ) {
227ostream& operator<< ( ostream& os, FileBuffRegion &br ) {
228  if( &br == NULL ) return os;  // The empty buffer region
229  FileBuffRegion *brp = &br;    // Pointer to region
230  while( brp ) {                // While have chained regions
231    brp->print(os);             // Print region
232    brp = brp->_next;           // Chain to next
233  }
234  return os;                    // Return final stream
235}
236
237//------------------------------print------------------------------------------
238// Print the FileBuffRegion to a stream. FileBuffRegions are printed with the
239// filename and line number to the left, and complete text lines to the right.
240// Selected portions (portions of a line actually in the FileBuffRegion are
241// underlined.  Ellipses are used for long multi-line regions.
242//void FileBuffRegion::print( std::ostream& os ) {
243void FileBuffRegion::print( ostream& os ) {
244  if( !this ) return;           // Nothing to print
245  char *s = _bfr->get_line();
246  int skip = (int)(_offset - _sol);     // Amount to skip to start of data
247  int len = printline( os, _bfr->_fp->_name, _line, s, skip, _length );
248
249  if( !len ) return;                    // All done; exit
250
251  // Here we require at least 2 lines
252  int off1 = _length - len + skip;      // Length of line 1
253  int off2 = off1 + _sol;               // Offset to start of line 2
254  char *s2 = _bfr->get_line();           // Start of line 2
255  char *s3 = strchr( s2, '\n' )+1;      // Start of line 3 (unread)
256  if( len <= (s3-s2) ) {                // It all fits on the next line
257    printline( os, _bfr->_fp->_name, _line+1, s2, -1, len ); // Print&underline
258    return;
259  }
260
261  // Here we require at least 3 lines
262  int off3 = off2 + (int)(s3-s2);       // Offset to start of line 3
263  s3 = _bfr->get_line();                // Start of line 3 (read)
264  const char *s4 = strchr( s3, '\n' )+1;// Start of line 4 (unread)
265  if( len < (s4-s3) ) {                 // It all fits on the next 2 lines
266    s2 = _bfr->get_line();
267    len = printline( os, _bfr->_fp->_name, _line+1, s2, -1, len ); // Line 2
268    s3 = _bfr->get_line();
269    printline( os, _bfr->_fp->_name, _line+2, s3, -1, len );     // Line 3
270    return;
271  }
272
273  // Here we require at least 4 lines.
274  // Print only the 1st and last line, with ellipses in middle.
275  os << "...\n";                // The ellipses
276  int cline = _line+1;          // Skipped 2 lines
277  do {                          // Do until find last line
278    len -= (int)(s3-s2);        // Remove length of line
279    cline++;                    // Next line
280    s2 = _bfr->get_line();      // Get next line from end of this line
281    s3 = strchr( s2, '\n' ) + 1;// Get end of next line
282  } while( len > (s3-s2) );     // Repeat until last line
283  printline( os, _bfr->_fp->_name, cline, s2, -1, len ); // Print & underline
284}
285
286//------------------------------file_error-------------------------------------
287void FileBuff::file_error(int flag, int linenum, const char *fmt, ...)
288{
289  va_list args;
290
291  va_start(args, fmt);
292  switch (flag) {
293  case 0: _AD._warnings += _AD.emit_msg(0, flag, linenum, fmt, args);
294  case 1: _AD._syntax_errs += _AD.emit_msg(0, flag, linenum, fmt, args);
295  case 2: _AD._semantic_errs += _AD.emit_msg(0, flag, linenum, fmt, args);
296  default: assert(0, ""); break;
297  }
298  va_end(args);
299  _AD._no_output = 1;
300}
301