source.sa


Generated by gen_html_sa_files from ICSI. Contact gomes@icsi.berkeley.edu for details
 
------------------------->  GNU Sather - sourcefile  <-------------------------
-- Copyright (C) 2000 by K Hopper, University of Waikato, New Zealand        --
-- This file is part of the GNU Sather library. It is free software; you may --
-- redistribute  and/or modify it under the terms of the GNU Library General --
-- Public  License (LGPL)  as published  by the  Free  Software  Foundation; --
-- either version 2 of the license, or (at your option) any later version.   --
-- This  library  is distributed  in the  hope that it will  be  useful, but --
-- WITHOUT ANY WARRANTY without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. See Doc/LGPL for more details.       --
-- The license text is also available from:  Free Software Foundation, Inc., --
-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA                     --
-------------->  Please email comments to <bug-sather@gnu.org>  <--------------


immutable class CONDITIONALS < $ENUMS{CONDITIONALS}

immutable class CONDITIONALS < $ENUMS{CONDITIONALS} is -- This immutable class is the enumeration over the conditional source -- reading keywords. -- -- The corresponding names in the culture-independent resources file -- are prescribed by the ISO/IEC standard 14652. The required names are -- -- -- def, ifdef, ifdef, ifndef, elsif, else, endif -- Version 1.0 Feb 98. Copyright K Hopper, U of Waikato -- Development History -- ------------------- -- Date Who By Detail -- ---- ------ ------ -- 19 Feb 98 kh Original from ISO/IEC 9945-2 include EXACT_ENUM{CONDITIONALS} ; private const val_count : CARD := 7 ; -- The following constant 'routines' specify the enumeration values. Define : SAME is return enum(1) end ; Undefine : SAME is return enum(2) end ; If_Defined : SAME is return enum(3) end ; If_Not_Defined : SAME is return enum(4) end ; Else_If : SAME is return enum(5) end ; Else : SAME is return enum(6) end ; End_If : SAME is return enum(7) end ; end ; -- CONDITIONALS

class SOURCE

class SOURCE is -- This class defines the abstraction of a text source from which lines -- can be taken in sequence. -- -- A source is created from a text file whose name is provided to the -- creation routine. The major part of the class is the pre-processed and -- raw iters for returning lines (as string cursors) to the using class. -- -- -- Version 1.1 Oct 98. Copyright K Hopper, U of Waikato -- Development History -- ------------------- -- Date Who By Detail -- ---- ------ ------ -- 9 Jun 98 kh Original from boot cultural compiler. -- 26 Oct 98 kh Revised , added pre/post conditions private attr escape_char : CHAR ; readonly attr file_name : FILE_PATH ; private attr fyle_cursor : STR_CURSOR ; readonly attr line_no : CARD ; private attr stack : FLIST{BOOL} ; private attr else_seen : FLIST{BOOL} ; private attr nesting_valid : BOOL ; private attr toggles : FSET{STR} ; private shared report : REPORTER ; private const File_Path, File_Access, Bad_Nesting, Missing_Toggle, Dup_Name, Unknown ; private const Msg_Count : CARD := Unknown + 1 ; private shared Messages : ARRAY{STR} ; private init(me : SAME) pre ~void(me) post ~void(Messages) and ~void(report) is -- This private routine initialises those components which are shared -- value objects -- if necessary. if void(report) then -- sets up shared once only! loc_cult : CULTURE := CULTURE::default ; Messages := loc_cult.resources.read(SYS::rune_name(me),Msg_Count) ; report := REPORTER::create(Messages,loc_cult.sather_lib) end end ; create(fname : STR) : SAME pre (fname.size > 0) post true is -- This routine attempts to open the file with the given name for -- reading and, if successful, reads its contents, closes the file and then -- creates the fyle_cursor for use when yielding individual lines! If the -- file is not found or may not be read then void is returned after issuing -- an error message. -- -- NOTE The default comment start - ' ' - is established. fyle : TEXT_FILE ; me : SAME := new ; init(me) ; loc_path : FILE_PATH := FILE_PATH::create(fname) ; if loc_path.leaf = fname then --a relative name! loc_path := FILE_PATH::create( DIRECTORY::current.str).append(loc_path) end ; if void(loc_path) then report.error(File_Path,fname) ; return void else me.file_name := loc_path ; fyle := TEXT_FILE::open_for_read(loc_path.str) ; if void(fyle) or fyle.error then -- couldn't find the file? report.error(File_Access,fname) ; return void end end ; me.file_name := loc_path ; me.fyle_cursor := fyle.fstr.str.cursor ; fyle.close ; -- The next three lines set up for the pre-processing iter. me.nesting_valid := true ; -- in case first line is not cond! me.stack := FLIST{BOOL}::create ; me.else_seen := FLIST{BOOL}::create ; me.toggles := FSET{STR}::create ; me.escape_char := fname.index_lib.Null.char ; -- ie there isn't one! me.line_no := 1 ; return me end ; create( fpath : FILE_PATH ) : SAME pre ~void(fpath) post true is -- This routine attempts to open the file with the given path for -- reading and, if successful, reads its contents, closes the file and then -- creates the fyle_cursor for use when yielding individual lines! If the -- file is not found or may not be read then void is returned. me : SAME := new ; init(me) ; fyle : TEXT_FILE := TEXT_FILE::open_for_read(fpath.str) ; if void(fyle) or fyle.error then -- couldn't find the file? report.error(File_Access,fpath.str) ; return void end ; me.file_name := fpath ; me.fyle_cursor := fyle.fstr.str.cursor ; fyle.close ; -- The next three lines set up for the pre-processing iter. me.nesting_valid := true ; -- in case first line is not cond! me.stack := FLIST{BOOL}::create ; me.else_seen := FLIST{BOOL}::create ; me.toggles := FSET{STR}::create ; me.escape_char := void ; -- ie there isn't one! me.line_no := 1 ; return me end ; create( fyle : TEXT_FILE, name : FILE_PATH ) : SAME pre ~void(name) post true is -- This routine is provided for use where no pre-processing is to be -- done and checking/opening of file has external requirements. The file -- is closed before exit! me : SAME := new ; -- The next three lines set up for the pre-processing iter. me.file_name := name ; me.fyle_cursor := fyle.fstr.str.cursor ; fyle.close ; me.nesting_valid := true ; -- in case first line is not cond! me.stack := FLIST{BOOL}::create ; me.else_seen := FLIST{BOOL}::create ; me.toggles := FSET{STR}::create ; me.escape_char := void ; -- ie there isn't one! me.line_no := 1 ; return me end ; comment : STR is -- This returns the start of comment string which is currently being -- used. return fyle_cursor.comment_start end ; comment( chars : STR ) pre (chars.size > 0) post true is -- This routine establishes the first two characters of chars as the -- current comment characters for this source text. if chars.size = 1 then -- only a single character fyle_cursor.set_comment_start(chars[0]) else fyle_cursor.set_comment_start(chars) end end ; escape : CHAR pre ~void(self) post result = escape_char is -- This routine returns the current escape character for this source. return escape_char end ; escape( ch : CHAR ) pre ~void(self) post escape_char = ch is -- This routine establishes ch as the escape character for the source -- until a further change takes place. escape_char := ch end ; finished : BOOL is -- This predicate returns true if and only if the file cursor has -- reached the end of the file. return void(self) -- which means it 'has' or fyle_cursor.is_done end ; private line_escaped( str : FSTR ) : BOOL is -- This predicate returns true if and only if the (possible) sequence of -- escape characters at the end of the line has an odd count - meaning that -- the end of the line is escaped. cnt : CARD := 0 ; loop index : CARD := (str.loc - 1).downto!(0) ; if str[index] = escape then cnt := cnt + 1 else break! end end ; return cnt.is_odd end ; line! : STR_CURSOR pre ~void(self) post ~void(result) -- or has quit! is -- This iter assembles one or more source lines into a logical line -- without conditional pre-processing, omitting comment lines and stripping -- unwanted line marks at the end as necessary. text : FSTR := FSTR::create ; escaped : BOOL := false ; loop if fyle_cursor.is_done then if escaped and (text.size > 0) then -- escaped at end? res : STR_CURSOR := text.str.cursor ; res.set_comment_start(fyle_cursor.comment_start) ; yield res else quit end ; quit end ; tmp_str : STR := fyle_cursor.get_str.strip ; if tmp_str.size > 0 then loc_cursor : STR_CURSOR := tmp_str.cursor ; loc_cursor.set_comment_start(fyle_cursor.comment_start) ; loc_cursor.skip_space ; -- and comments too! if ~loc_cursor.is_done then -- something there! loc_str : FSTR := FSTR::create(loc_cursor.get_str) ; if ~escaped then if fyle_cursor.is_done then line_no := fyle_cursor.line_no else line_no := fyle_cursor.line_no - 1 end end ; if (loc_str[loc_str.loc - 1] = escape) then -- need to check if line escaped! escaped := line_escaped(loc_str) ; if escaped then loc_str.loc := loc_str.loc - 1 end else escaped := false end ; text := text + loc_str ; if ~escaped then res : STR_CURSOR := text.str.cursor ; res.set_comment_start(fyle_cursor.comment_start) ; -- an inheritance! yield res ; text := FSTR::create end end end end end ; pp_line! : STR_CURSOR pre ~void(self) post ~void(result) -- or has quit! is -- This iter loops through source lines until one which has data is -- found. Any end of line mark is then stripped. If the last character is -- an escape character then the 'line' is continued on the next source line. -- Leading spaces are then omitted in yielding the resulting valid text line! valid_line : FSTR ; escaped : BOOL := false ; toggle_name : STR ; text : FSTR := FSTR::create ; loop if fyle_cursor.is_done then if ~(stack.size = 0) then report.line_error(Bad_Nesting,(fyle_cursor.line_no - 1)) end ; if escaped then -- escaped at end? res : STR_CURSOR := text.str.cursor ; res.set_comment_start(fyle_cursor.comment_start) ; yield res end ; quit end ; valid_line := FSTR::create(fyle_cursor.get_str) ; if ((void(valid_line) or (valid_line.size = 0)) and fyle_cursor.is_done) then quit end ; #OUT+"source.sa pp_line! valid_line:"+valid_line.str+"\n"; loc_cursor : STR_CURSOR := valid_line.str.cursor ; loc_cursor.set_comment_start(fyle_cursor.comment_start) ; loc_cursor.skip_space ; key_word : STR; if loc_cursor.is_done then #OUT+"source.sa pp_line! loc_cursor.is_done\n"; else key_word := loc_cursor.get_word ; loc_cursor.skip_space ; if ~loc_cursor.is_done then toggle_name := loc_cursor.get_word ; else toggle_name := void end ; key : CONDITIONALS := CONDITIONALS::create(key_word) ; case key when CONDITIONALS::Define then if void(toggle_name) then report.line_error(Missing_Toggle,(fyle_cursor.line_no - 1)) elsif toggles.test(toggle_name) then report.line_error(Dup_Name,(fyle_cursor.line_no - 1), toggle_name) else toggles := toggles.insert(toggle_name) end when CONDITIONALS::Undefine then if void(toggle_name) then report.line_error(Missing_Toggle,(fyle_cursor.line_no - 1)) elsif toggles.test(toggle_name) then toggles := toggles.delete(toggle_name) else report.line_error(Unknown, (fyle_cursor.line_no - 1),toggle_name) end when CONDITIONALS::If_Defined then stack := stack.push(nesting_valid) ; if nesting_valid then if void(toggle_name) then report.line_error(Missing_Toggle, (fyle_cursor.line_no - 1)) else nesting_valid := toggles.test(toggle_name) end ; stack := stack.push(~nesting_valid) else stack := stack.push(false) end ; else_seen := else_seen.push(false) ; when CONDITIONALS::If_Not_Defined then stack := stack.push(nesting_valid) ; if nesting_valid then if void(toggle_name) then report.line_error(Missing_Toggle, (fyle_cursor.line_no - 1)) else nesting_valid := ~toggles.test(toggle_name) end ; stack := stack.push(~nesting_valid) else stack := stack.push(false) end ; else_seen := else_seen.push(false) ; when CONDITIONALS::Else_If then if (stack.size = 0) then report.line_error(Bad_Nesting,(fyle_cursor.line_no - 1)) else nesting_valid := stack.pop ; if nesting_valid then if void(toggle_name) then report.line_error(Missing_Toggle, (fyle_cursor.line_no - 1)) else nesting_valid := toggles.test(toggle_name) end ; stack := stack.push(~nesting_valid) else stack := stack.push(false) end end when CONDITIONALS::Else then if (stack.size = 0) then report.line_error(Bad_Nesting,(fyle_cursor.line_no - 1)) else nesting_valid := stack.pop ; else_seen := else_seen.push(~else_seen.pop) end when CONDITIONALS::End_If then if (stack.size = 0) then report.line_error(Bad_Nesting,(fyle_cursor.line_no - 1)) else if ~else_seen.top then -- need to get off the else value! nesting_valid := stack.pop end ; dummy : BOOL := else_seen.pop ; nesting_valid := stack.pop end else -- NOT a pre-processing directive if nesting_valid then if ~loc_cursor.skip_comment then -- not a comment! valid_line := valid_line.strip ; -- line marks if valid_line.size > 0 then -- something there! if ~escaped then if fyle_cursor.is_done then line_no := fyle_cursor.line_no else line_no := fyle_cursor.line_no - 1 end end ; if (valid_line[valid_line.loc - 1] = escape) then -- need to check if line escaped! escaped := line_escaped(valid_line) ; if escaped then valid_line.loc := valid_line.loc - 1 end else escaped := false end ; text := text + valid_line ; if ~escaped then res : STR_CURSOR := text.str.cursor ; res.set_comment_start( fyle_cursor.comment_start) ; -- an inheritance! yield res ; text := FSTR::create end end end end end end end ; end; pp_line : STR_CURSOR pre ~void(self) post true is -- This is merely a single line version of the iter for 'prefix type' -- testing, etc. loop return pp_line! end ; return void end ; line : STR_CURSOR pre ~void(self) post true is -- This is merely a single line version of the iter for 'prefix type' -- testing, etc. loop return line! end ; return void end ; end ; -- SOURCE