for review: new module for handling file offsets

Zoltan Somogyi zs at cs.mu.OZ.AU
Mon Mar 8 20:41:14 AEDT 1999


For review by anyone. The long-term intention is to replace the line numbers
in term__contexts with file offsets, which are the same size but carry more
information. This extra information can be useful to the debugger, as well
as for better error messages.

library/file_offset.m:
	A new module to handle conversions from offsets into files into
	line number, column number pairs.

library/std_util.m:
	Add two new types:

	:- type maybe_error ---> ok ; error(string).
	:- type maybe_error(T) ---> ok(T) ; error(string).

	They are used in file_offset.m, but they can be used in many other
	places as well. We should think about replacing several types in
	io.m with instances of these.

library/library.m:
	Include file_offset.m in the library.

Zoltan.

cvs diff: Diffing .
Index: file_offset.m
===================================================================
RCS file: file_offset.m
diff -N file_offset.m
--- /dev/null	Mon Mar  8 20:05:01 1999
+++ file_offset.m	Mon Mar  8 18:02:15 1999
@@ -0,0 +1,257 @@
+%---------------------------------------------------------------------------%
+% Copyright (C) 1999 University of Melbourne.
+% This file may only be copied under the terms of the GNU Library General
+% Public License - see the file COPYING.LIB in the Mercury distribution.
+%---------------------------------------------------------------------------%
+
+% File: file_offset.m.
+% Main author: zs.
+% Stability: low.
+
+% This module handles conversions from offsets into files to line number,
+% column number pairs. This obviously requires reading the file concerned.
+% Since programs that need to perform such conversions often need to
+% perform several conversions of the same file, this module works with
+% a database that maintains offset information about files it has processed
+% before.
+%
+% The module has predicates to initialize the database, and to load or reload
+% files into the database. (Reloading a file into the database if the contents
+% of the file may have changed since the last time it was loaded.) It also has
+% predicates to perform the conversions. Some of these require the file
+% concerned to have been loaded before; others load it if it has not been
+% loaded before.
+
+%-----------------------------------------------------------------------------%
+
+:- module file_offset.
+:- interface.
+
+:- import_module io, std_util.
+
+:- type file_offset_db.
+
+:- type line_column --->
+	line_column(
+		int,	% line number, counted starting from 1
+		int	% column number, counted starting from 1
+	).
+
+	% Initialize the file offset database.
+:- pred file_offset__init_db(file_offset_db::out) is det.
+
+	% Load the file with the given name into the file offset database.
+	% If the file could be successfully read, return the new offset
+	% database; if it couldn't, return the old database and
+	% an error message.
+:- pred file_offset__load_file_into_db(file_offset_db::in, string::in,
+	file_offset_db::out, maybe_error::out,
+	io__state::di, io__state::uo) is det.
+
+	% Load the file with the given name into the file offset database.
+	% If the file could be successfully read, return the new offset
+	% database; if it couldn't, abort with an error message.
+:- pred file_offset__load_file_into_db_det(file_offset_db::in, string::in,
+	file_offset_db::out, io__state::di, io__state::uo) is det.
+
+	% Look up the given filename and offset in the file offset database,
+	% and convert it to a line number and a column number. Return an error
+	% if the named file is not in the file offset database, or if the
+	% given offset is out of range for that file.
+:- pred file_offset__lookup_db(file_offset_db::in, string::in, int::in,
+	maybe_error(line_column)::out) is det.
+
+	% Look up the given filename and offset in the file offset database,
+	% and convert it to a line number and a column number. Abort
+	% if the named file is not in the file offset database, or if the
+	% given offset is out of range for that file.
+:- pred file_offset__lookup_db_det(file_offset_db::in, string::in, int::in,
+	line_column::out) is det.
+
+	% Look up the given filename and offset in the file offset database,
+	% and convert it to a line number and a column number. If the named
+	% named file is not in the file offset database, read it in.
+	% Return an error if the file cannot be read, or if the given offset
+	% is out of range for that file.
+:- pred file_offset__lookup_db_maybe_load_file(file_offset_db::in,
+	string::in, int::in,
+	file_offset_db::out, maybe_error(line_column)::out,
+	io__state::di, io__state::uo) is det.
+
+	% Look up the given filename and offset in the file offset database,
+	% and convert it to a line number and a column number. If the named
+	% named file is not in the file offset database, read it in.
+	% Abort an error if the file cannot be read, or if the given offset
+	% is out of range for that file.
+:- pred file_offset__lookup_db_maybe_load_file_det(file_offset_db::in,
+	string::in, int::in, file_offset_db::out, line_column::out,
+	io__state::di, io__state::uo) is det.
+
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+:- import_module int, string, map, require.
+
+:- type file_info --->
+	file_info(
+		int,
+			% The size of the file in bytes.
+		file_offset_map
+			% There is an entry mapping O to L if the first char
+			% of line L is at offset O within the file.
+	).
+
+:- type file_offset_map == map(int, int).
+
+	% The file offset database maps filenames to the line offset map
+	% within the file.
+:- type file_offset_db == map(string, file_info).
+
+file_offset__init_db(FileOffsetDb) :-
+	map__init(FileOffsetDb).
+
+file_offset__load_file_into_db_det(FileOffsetDb0, FileName, FileOffsetDb,
+		IO0, IO) :-
+	file_offset__load_file_into_db(FileOffsetDb0, FileName, FileOffsetDb,
+		Result, IO0, IO),
+	( 
+		Result = ok
+	;
+		Result = error(Msg),
+		error(Msg)
+	).
+
+file_offset__load_file_into_db(FileOffsetDb0, FileName, FileOffsetDb, Result,
+		IO0, IO) :-
+	file_offset__load_file_into_db_return_filemap(FileOffsetDb0, FileName,
+		FileOffsetDb, LoadResult, IO0, IO),
+	( 
+		LoadResult = ok(_),
+		Result = ok
+	;
+		LoadResult = error(Msg),
+		Result = error(Msg)
+	).
+
+:- pred file_offset__load_file_into_db_return_filemap(file_offset_db::in,
+	string::in, file_offset_db::out, maybe_error(file_info)::out,
+	io__state::di, io__state::uo) is det.
+
+file_offset__load_file_into_db_return_filemap(FileOffsetDb0, FileName,
+		FileOffsetDb, Result) -->
+	io__open_input(FileName, OpenResult),
+	(
+		{ OpenResult = ok(FileStream) },
+		io__read_file_as_string(FileStream, ReadResult, FileStr),
+		{
+			ReadResult = ok,
+			map__init(FileMap0),
+			map__det_insert(FileMap0, 0, 1, FileMap1),
+			file_offset__find_offsets(FileStr, 0, 1, FileSize,
+				FileMap1, FileMap),
+			FileInfo = file_info(FileSize, FileMap),
+			map__set(FileOffsetDb0, FileName, FileInfo,
+				FileOffsetDb),
+			Result = ok(FileInfo)
+		;
+			ReadResult = error(Msg),
+			FileOffsetDb = FileOffsetDb0,
+			io__error_message(Msg, DecodedMsg),
+			Result = error(DecodedMsg)
+		},
+		io__close_input(FileStream)
+	;
+		{ OpenResult = error(Msg) },
+		{ FileOffsetDb = FileOffsetDb0 },
+		{ io__error_message(Msg, DecodedMsg) },
+		{ Result = error(DecodedMsg) }
+	).
+
+:- pred file_offset__find_offsets(string::in, int::in, int::in, int::out,
+	file_offset_map::in, file_offset_map::out) is det.
+
+file_offset__find_offsets(FileStr, CurOffset, CurLine, FileSize, Map0, Map) :-
+	( string__index(FileStr, CurOffset, CurChar) ->
+		NextOffset is CurOffset + 1,
+		( CurChar = '\n' ->
+			NextLine is CurLine + 1,
+			map__det_insert(Map0, NextOffset, NextLine, Map1)
+		;
+			NextLine = CurLine,
+			Map1 = Map0
+		),
+		file_offset__find_offsets(FileStr, NextOffset, NextLine,
+			FileSize, Map1, Map)
+	;
+		FileSize = CurOffset,
+		Map = Map0
+	).
+
+file_offset__lookup_db_maybe_load_file_det(FileOffsetDb0, FileName, Offset,
+		FileOffsetDb, LineColumn, IO0, IO) :-
+	file_offset__lookup_db_maybe_load_file(FileOffsetDb0, FileName, Offset,
+		FileOffsetDb, Result, IO0, IO),
+	( 
+		Result = ok(LineColumn)
+	;
+		Result = error(Msg),
+		error(Msg)
+	).
+
+file_offset__lookup_db_maybe_load_file(FileOffsetDb0, FileName, Offset,
+		FileOffsetDb, Result, IO0, IO) :-
+	( map__search(FileOffsetDb0, FileName, OldFileInfo) ->
+		MaybeFileInfo = ok(OldFileInfo),
+		FileOffsetDb = FileOffsetDb0,
+		IO = IO0
+	;
+		file_offset__load_file_into_db_return_filemap(FileOffsetDb0,
+			FileName, FileOffsetDb, LoadResult, IO0, IO),
+		(
+			LoadResult = ok(NewFileInfo),
+			MaybeFileInfo = ok(NewFileInfo)
+		;
+			LoadResult = error(Msg),
+			MaybeFileInfo = error(Msg)
+		)
+	),
+	(
+		MaybeFileInfo = ok(FileInfo),
+		file_offset__lookup_file_info(FileInfo, Offset, Result)
+	;
+		MaybeFileInfo = error(ErrorMsg),
+		Result = error(ErrorMsg)
+	).
+
+file_offset__lookup_db_det(FileOffsetDb0, FileName, Offset, LineColumn) :-
+	file_offset__lookup_db(FileOffsetDb0, FileName, Offset, Result),
+	(
+		Result = ok(LineColumn)
+	;
+		Result = error(Msg),
+		error(Msg)
+	).
+
+file_offset__lookup_db(FileOffsetDb0, FileName, Offset, Result) :-
+	( map__search(FileOffsetDb0, FileName, OldFileInfo) ->
+		file_offset__lookup_file_info(OldFileInfo, Offset, Result)
+	;
+		Result = error("file not in offset database")
+	).
+
+:- pred file_offset__lookup_file_info(file_info::in, int::in,
+	maybe_error(line_column)::out) is det.
+
+file_offset__lookup_file_info(FileInfo, Offset, Result) :-
+	FileInfo = file_info(FileSize, FileMap),
+	(
+		0 =< Offset,
+		Offset < FileSize
+	->
+		map__lower_bound_lookup(FileMap, Offset,
+			LineStartOffset, LineNumber),
+		ColumnNumber is Offset - LineStartOffset + 1,
+		Result = ok(line_column(LineNumber, ColumnNumber))
+	;
+		Result = error("offset out of range")
+	).
Index: library.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/library.m,v
retrieving revision 1.43
diff -u -b -u -r1.43 library.m
--- library.m	1998/09/29 05:10:45	1.43
+++ library.m	1999/03/08 02:24:18
@@ -1,5 +1,5 @@
 %---------------------------------------------------------------------------%
-% Copyright (C) 1993-1998 The University of Melbourne.
+% Copyright (C) 1993-1999 The University of Melbourne.
 % This file may only be copied under the terms of the GNU Library General
 % Public License - see the file COPYING.LIB in the Mercury distribution.
 %---------------------------------------------------------------------------%
@@ -33,6 +33,7 @@
 :- import_module store, rbtree, parser, lexer, ops.
 :- import_module prolog.
 :- import_module integer, rational.
+:- import_module file_offset.
 
 % library__version must be implemented using pragma c_code,
 % so we can get at the MR_VERSION and MR_FULLARCH configuration
Index: std_util.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/std_util.m,v
retrieving revision 1.141
diff -u -b -u -r1.141 std_util.m
--- std_util.m	1999/02/07 14:07:24	1.141
+++ std_util.m	1999/03/08 01:58:20
@@ -92,6 +92,9 @@
 
 :- type maybe(T) ---> no ; yes(T).
 
+:- type maybe_error ---> ok ; error(string).
+:- type maybe_error(T) ---> ok(T) ; error(string).
+
 %-----------------------------------------------------------------------------%
 
 % The "unit" type - stores no information at all.



More information about the developers mailing list