127 lines
5.3 KiB
Text
127 lines
5.3 KiB
Text
# S-Expressions #
|
|
CHAR nl = REPR 10;
|
|
# mode representing an S-expression #
|
|
MODE SEXPR = STRUCT( UNION( VOID, STRING, REF SEXPR ) element, REF SEXPR next );
|
|
# creates an initialises an SEXPR #
|
|
PROC new s expr = REF SEXPR: HEAP SEXPR := ( EMPTY, NIL );
|
|
# reports an error #
|
|
PROC error = ( STRING msg )VOID: print( ( "**** ", msg, newline ) );
|
|
# S-expression reader - reads and returns an S-expression from the string s #
|
|
PROC s reader = ( STRING s )REF SEXPR:
|
|
BEGIN
|
|
PROC at end = BOOL: s pos > UPB s;
|
|
PROC curr = CHAR: IF at end THEN REPR 0 ELSE s[ s pos ] FI;
|
|
PROC skip spaces = VOID: WHILE NOT at end AND ( curr = " " OR curr = nl ) DO s pos +:= 1 OD;
|
|
PROC end of list = BOOL: at end OR curr = ")";
|
|
INT s pos := LWB s;
|
|
INT t pos;
|
|
[ ( UPB s - LWB s ) + 1 ]CHAR token; # token text - large enough to hold the whole string if necessary #
|
|
# adds the current character to the token #
|
|
PROC add curr = VOID: token[ t pos +:= 1 ] := curr;
|
|
# get an s expression element from s #
|
|
PROC get element = REF SEXPR:
|
|
BEGIN
|
|
REF SEXPR result = new s expr;
|
|
skip spaces;
|
|
# get token text #
|
|
IF at end THEN
|
|
# no element #
|
|
element OF result := EMPTY
|
|
ELIF curr = "(" THEN
|
|
s pos +:= 1;
|
|
skip spaces;
|
|
IF NOT end of list
|
|
THEN
|
|
REF SEXPR nested expression = get element;
|
|
REF SEXPR element pos := nested expression;
|
|
element OF result := nested expression;
|
|
skip spaces;
|
|
WHILE NOT end of list
|
|
DO
|
|
element pos := next OF element pos := get element;
|
|
skip spaces
|
|
OD
|
|
FI;
|
|
IF curr = ")" THEN
|
|
s pos +:= 1
|
|
ELSE
|
|
error( "Missing "")""" )
|
|
FI
|
|
ELIF curr = ")" THEN
|
|
s pos +:= 1;
|
|
error( "Unexpected "")""" );
|
|
element OF result := EMPTY
|
|
ELSE
|
|
# quoted or unquoted string #
|
|
t pos := LWB token - 1;
|
|
IF curr /= """" THEN
|
|
# unquoted string #
|
|
WHILE add curr;
|
|
s pos +:= 1;
|
|
NOT at end AND curr /= " " AND curr /= "("
|
|
AND curr /= ")" AND curr /= """"
|
|
AND curr /= nl
|
|
DO SKIP OD
|
|
ELSE
|
|
# quoted string #
|
|
WHILE add curr;
|
|
s pos +:= 1;
|
|
NOT at end AND curr /= """"
|
|
DO SKIP OD;
|
|
IF curr /= """" THEN
|
|
# missing string quote #
|
|
error( "Unterminated string: <<" + token[ : t pos ] + ">>" )
|
|
ELSE
|
|
# have the closing quote #
|
|
add curr;
|
|
s pos +:= 1
|
|
FI
|
|
FI;
|
|
element OF result := token[ : t pos ]
|
|
FI;
|
|
result
|
|
END # get element # ;
|
|
|
|
REF SEXPR s expr = get element;
|
|
skip spaces;
|
|
IF NOT at end THEN
|
|
# extraneuos text after the expression #
|
|
error( "Unexpected text at end of expression: " + s[ s pos : ] )
|
|
FI;
|
|
|
|
s expr
|
|
END # s reader # ;
|
|
# prints an S expression #
|
|
PROC s writer = ( REF SEXPR s expr )VOID:
|
|
BEGIN
|
|
# prints an S expression with a suitable indent #
|
|
PROC print indented s expression = ( REF SEXPR s expr, INT indent )VOID:
|
|
BEGIN
|
|
REF SEXPR s pos := s expr;
|
|
WHILE REF SEXPR( s pos ) ISNT REF SEXPR( NIL ) DO
|
|
FOR i TO indent DO print( ( " " ) ) OD;
|
|
CASE element OF s pos
|
|
IN (VOID ): print( ( "()", newline ) )
|
|
, (STRING s): print( ( s, newline ) )
|
|
, (REF SEXPR e): BEGIN
|
|
print( ( "(", newline ) );
|
|
print indented s expression( e, indent + 4 );
|
|
FOR i TO indent DO print( ( " " ) ) OD;
|
|
print( ( ")", newline ) )
|
|
END
|
|
OUT
|
|
error( "Unexpected S expression element" )
|
|
ESAC;
|
|
s pos := next OF s pos
|
|
OD
|
|
END # print indented s expression # ;
|
|
|
|
print indented s expression( s expr, 0 )
|
|
END # s writer # ;
|
|
# test the eader and writer with the example from the task #
|
|
s writer( s reader( "((data ""quoted data"" 123 4.5)"
|
|
+ nl
|
|
+ " (data (!@# (4.5) ""(more"" ""data)"")))"
|
|
+ nl
|
|
)
|
|
)
|