/* Part of XPCE This example code is in the public domain */ :- module(pce_bibtex, []). :- use_module(library(pce)). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This library file defines a parser for BibTeX database files. It is intended as an example for using XPCE's text-manipulation and data representation techniques and may be used as a starting point for implementing a BibTeX database manager program. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /******************************* * DATABASE * *******************************/ :- pce_begin_class(bibtex_db, hash_table, "BibTeX DataBase"). variable(strings, sheet, get, "Table of strings"). variable(file, file*, get, "File it was loaded from"). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - A BibTeX database is represented by a hash_table mapping keys onto bibtex_entry objects. A bibtex_entry is subclass of class sheet. The database as a whole also stores a sheet for the defined string-constants as well as a reference to the file it was loaded from (if any). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ initialise(DB, File:[file]*) :-> "Create BibTeX database from file":: send(DB, send_super, initialise), send(DB, slot, strings, new(sheet)), ( File \== @default -> send(DB, slot, file, File), send(DB, load, File) ). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The database is parsed using a text_buffer. This allows us to easily exploit the regular-expression capabilities of XPCE implemented by class regex. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ load(DB, File:file) :-> "Load entries from file":: new(TB, text_buffer), send(TB, insert_file, 0, File), send(DB, parse, TB), free(TB). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Toplevel loop to parse a BibTeX file. First of all, the syntax-table of the text_buffer is modified to treat the newline as white_space, allowing the '\s ' operator in regular expressions to include newlines. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ parse(DB, TB:text_buffer) :-> "Parse all entries from given text-buffer":: new(Here, number(0)), get(TB, size, Size), send(TB, syntax, new(S, syntax_table)), send(S, add_syntax, '\n', white_space), repeat, send(DB, parse_entry, TB, Here), get(Here, value, Size), !. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Regular expression constants for the various fields of a BibTeX file. Note that GNU-regular expression define `syntax categories': '\s ' means `any white character (as defined by the syntax table)', '\w' means `any word character'. '\S ' and '\W' are negations. See class regex for a full description. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ :- pce_global(@bibtex_head_regex, new(regex('@\\(\\w+\\){'))). :- pce_global(@bibtex_key_regex, new(regex('\\s *\\(\\S +\\)\\s *,'))). :- pce_global(@bibtex_att_regex, new(regex('\\s *\\(\\w+\\)\\s *=\\s *\\("\\(\\\\"\\|[^"]\\)*"\\|\\w+\\|[^,]+\\)\s *,?'))). :- pce_global(@bibtex_end_attr_regex, new(regex('\\s *}\\s *'))). parse_entry(DB, TB:text_buffer, Here:number) :-> "Parse 1 entry":: send(@bibtex_head_regex, search, TB, Here), !, get(@bibtex_head_regex, register_value, TB, 1, name, T0), send(Here, value, @bibtex_head_regex?register_end), get(T0, downcase, Type), ( Type == string -> ( send(@bibtex_att_regex, match, TB, Here) -> get(@bibtex_att_regex, register_value, TB, 1, name, SName), get(@bibtex_att_regex, register_value, TB, 2, SValue), send(DB?strings, value, SName, SValue), send(Here, value, @bibtex_att_regex?register_end) ; send(TB, report, error, 'Illegal string at %d', Here), fail ) ; send(@bibtex_key_regex, match, TB, Here), get(@bibtex_key_regex, register_value, TB, 1, name, Key), send(DB, append, Key, new(E, bibtex_entry(Type, Key))), send(Here, value, @bibtex_key_regex?register_end), repeat, ( send(E, parse_attribute, TB, Here) -> ( send(@bibtex_end_attr_regex, match, TB, Here) -> !, send(Here, value, @bibtex_end_attr_regex?register_end) ; fail ) ; !, send(TB, report, error, 'Cannot parse attribute at %d', Here), fail ) ). :- pce_end_class. /******************************* * ENTRY * *******************************/ :- pce_begin_class(bibtex_entry, sheet). initialise(E, Type:name, Key:name) :-> send(E, send_super, initialise), send(E, value, type, Type), send(E, value, key, Key). parse_attribute(E, TB:text_buffer, Here:number) :-> "Parse attribute-value pair":: send(@bibtex_att_regex, match, TB, Here), get(@bibtex_att_regex, register_value, TB, 1, name, AName), get(@bibtex_att_regex, register_value, TB, 2, AValue), send(E, value, AName, AValue), send(Here, value, @bibtex_att_regex?register_end). :- pce_end_class.