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