cpsa-2.0.2: src/sexpr.pl
% -*- mode: prolog -*-
% Converts lists of character codes to Prolog style S-Expressions.
% Known to work in SWI-Prolog, but not with GNU Prolog.
:- module(sexpr,
[codes_sexpr/2, codes_sexpr_list/2, sexpr_term/2,
read_sexpr_list/2, read_sexpr_term_list/2]).
% codes_sexpr(+Codes, -Sexpr)
% Convert a list of character codes to a Prolog style S-Expression.
codes_sexpr(Codes, Sexpr) :-
phrase(sexpr_top(Sexpr), Codes).
sexpr_top(S) -->
sexpr(S),
sexpr_white_spaces.
% codes_sexpr_list(+Codes, -Sexprs)
% Convert a list of character codes to a Prolog style list of
% S-Expressions.
codes_sexpr_list(Codes, Sexprs) :-
phrase(sexpr_list_top(Sexprs), Codes).
sexpr_list_top(S) -->
sexpr_list(S),
sexpr_white_spaces.
% Supporting predicates follow.
% Parse an S-Expression.
sexpr(S) -->
sexpr_white_space,
!,
sexpr(S).
sexpr(S) -->
sexpr_open,
!,
sexpr_list(S),
sexpr_close.
sexpr(_) -->
sexpr_close,
!,
{ fail }.
sexpr(S) -->
sexpr_digit(C),
!,
sexpr_number(C, S).
sexpr(S) -->
sexpr_string_start,
!,
sexpr_string(S).
sexpr(S) -->
sexpr_symbol(S).
% Parse a list minus the opening parenthesis.
sexpr_list(L) -->
sexpr_white_space,
!,
sexpr_list(L).
sexpr_list([S|L]) -->
sexpr(S),
!,
sexpr_list(L).
sexpr_list([]) -->
[].
% Parse a number.
sexpr_number(C, S) -->
sexpr_digits(L),
{ number_codes(S, [C|L]) }.
sexpr_digits([]) -->
sexpr_atom_end,
!.
sexpr_digits([C|L]) -->
sexpr_digit(C),
sexpr_digits(L).
% Parse a string minus the opening quote.
sexpr_string(S) -->
sexpr_string_rest(L),
{ string_to_list(S, L) }.
sexpr_string_rest(L) -->
sexpr_string_start,
!,
{ L = [] }.
sexpr_string_rest([C|L]) -->
[C],
sexpr_string_rest(L).
sexpr_string_start -->
sexpr_quote.
% Symbols
sexpr_symbol(S) -->
sexpr_symbol_code(C),
sexpr_symbol_codes(L),
{ atom_codes(S, [C|L]) }.
sexpr_symbol_codes([C|L]) -->
sexpr_symbol_code(C),
!,
sexpr_symbol_codes(L).
sexpr_symbol_codes([]) -->
sexpr_atom_end,
!.
sexpr_symbol_code(C) -->
sexpr_other(C).
% Character recognizers.
sexpr_white_space -->
[C],
{ sexpr_token(C, space) }.
sexpr_white_space -->
sexpr_semicolon,
!,
sexpr_comment.
sexpr_semicolon -->
[C],
{ sexpr_token(C, semicolon) }.
sexpr_comment -->
sexpr_comment_char,
!,
sexpr_comment.
sexpr_comment -->
[].
sexpr_comment_char -->
[C],
{ \+ code_type(C, newline) }.
sexpr_open -->
[C],
{ sexpr_token(C, open) }.
sexpr_close -->
[C],
{ sexpr_token(C, close) }.
sexpr_digit(C) -->
[C],
{ code_type(C, digit) }.
sexpr_quote -->
[C],
{ sexpr_token(C, quote) }.
sexpr_other(C) -->
[C],
{ sexpr_token_other(C) }.
sexpr_token_other(C) :-
sexpr_token(C, _),
!,
fail.
sexpr_token_other(_).
% Atomic S-Expressions end with EOF, white space, or a parenthesis.
sexpr_atom_end([], []) :-
!.
sexpr_atom_end([C|L], [C|L]) :-
sexpr_token(C, space),
!.
sexpr_atom_end([C|L], [C|L]) :-
sexpr_token(C, open),
!.
sexpr_atom_end([C|L], [C|L]) :-
sexpr_token(C, close).
% S-Expression character classes.
sexpr_token(Code, space) :-
code_type(Code, space),
!.
sexpr_token(Code, open) :-
char_code('(', Code),
!.
sexpr_token(Code, close) :-
char_code(')', Code),
!.
sexpr_token(Code, semicolon) :-
char_code(';', Code),
!.
sexpr_token(Code, quote) :-
char_code('"', Code).
% Check for trailing white space in a list of character codes.
sexpr_white_spaces -->
sexpr_white_space,
!,
sexpr_white_spaces.
sexpr_white_spaces -->
[].
% Other predicates used in conjunction with the parser.
% sexpr_term(+Sexpr, -Term)
% Convert S-Expression into a more convenient form of term. When the
% first element of a list is an atom, convert it to a function symbol
% applied to the remaining terms.
sexpr_term(A, A) :-
atomic(A),
!.
sexpr_term([F|L], T) :-
atom(F),
!,
sexpr_term_list(L, S),
T =.. [F|S].
sexpr_term(L, T) :-
sexpr_term_list(L, T).
% sexpr_term_list(+Sexprs, -Terms)
sexpr_term_list(L, T) :-
maplist(sexpr_term, L, T).
% --------------I/O--------------
% read_file(+Name, -Codes)
read_file(Name, Codes) :-
open(Name, read, Stream),
get_codes(Stream, Codes).
% Get characters from a stream and return them as a list.
get_codes(Stream, []) :-
at_end_of_stream(Stream),
!,
close(Stream).
get_codes(Stream, [Code|Codes]) :-
get_code(Stream, Code),
get_codes(Stream, Codes).
% read_sexpr_list(+Name, -Sexprs)
% Intrepret the character codes from a file as a list of S-Expressions.
read_sexpr_list(Name, Sexprs) :-
read_file(Name, Codes),
codes_sexpr_list(Codes, Sexprs).
% read_sexpr_term_list(+Name, -Terms)
% Intrepret the character codes from a file as a list of S-Expressions
% in which lists that start with an atom have been converted to a
% function application.
read_sexpr_term_list(Name, Terms) :-
read_sexpr_list(Name, Sexprs),
sexpr_term_list(Sexprs, Terms).