166 lines
4.4 KiB
Plaintext
166 lines
4.4 KiB
Plaintext
MODE YIELDINT = PROC(INT)VOID;
|
|
|
|
MODE RANGE = STRUCT(INT lwb, upb);
|
|
MODE RANGEINT = UNION(RANGE, INT);
|
|
|
|
OP SIZEOF = ([]RANGEINT list)INT: (
|
|
# determine the length of the output array #
|
|
INT upb := LWB list - 1;
|
|
FOR key FROM LWB list TO UPB list DO
|
|
CASE list[key] IN
|
|
(RANGE value): upb +:= upb OF value - lwb OF value + 1,
|
|
(INT): upb +:= 1
|
|
ESAC
|
|
OD;
|
|
upb
|
|
);
|
|
|
|
PROC gen range expand = ([]RANGEINT list, YIELDINT yield)VOID:
|
|
FOR key FROM LWB list TO UPB list DO
|
|
CASE list[key] IN
|
|
(RANGE range): FOR value FROM lwb OF range TO upb OF range DO yield(value) OD,
|
|
(INT int): yield(int)
|
|
ESAC
|
|
OD;
|
|
|
|
PROC range expand = ([]RANGEINT list)[]INT: (
|
|
[LWB list: LWB list + SIZEOF list - 1]INT out;
|
|
INT upb := LWB out - 1;
|
|
# FOR INT value IN # gen range expand(list, # ) DO #
|
|
## (INT value)VOID:
|
|
out[upb +:= 1] := value
|
|
# OD #);
|
|
out
|
|
);
|
|
|
|
#
|
|
test:(
|
|
[]RANGEINT list = (-6, RANGE(-3, -1), RANGE(3, 5), RANGE(7, 11), 14, 15, RANGE(17, 20));
|
|
print((range expand(list), new line))
|
|
)
|
|
#
|
|
|
|
|
|
# converts string containing a comma-separated list of ranges and values to a []RANGEINT #
|
|
OP TORANGE = ( STRING s )[]RANGEINT:
|
|
BEGIN
|
|
|
|
# counts the number of elements - one more than the number of commas #
|
|
# and so assumes there is always at least one element #
|
|
PROC count elements = INT:
|
|
BEGIN
|
|
|
|
INT elements := 1;
|
|
|
|
FOR pos FROM LWB s TO UPB s
|
|
DO
|
|
IF s[ pos ] = ","
|
|
THEN
|
|
elements +:= 1
|
|
FI
|
|
OD;
|
|
|
|
# RESULT #
|
|
elements
|
|
END; # count elements #
|
|
|
|
REF[]RANGEINT result = HEAP [ 1 : count elements ]RANGEINT;
|
|
|
|
# does the actual parsing - assumes the string is syntatically valid and doesn't check for errors #
|
|
# - in particular, a string with no elements will cause problems, as will space characters in the string #
|
|
PROC parse range string = []RANGEINT:
|
|
BEGIN
|
|
|
|
INT element := 0;
|
|
INT str pos := 1;
|
|
|
|
PROC next = VOID: str pos +:= 1;
|
|
PROC curr char = CHAR: IF str pos > UPB s THEN "?" ELSE s[ str pos ] FI;
|
|
PROC have minus = BOOL: curr char = "-";
|
|
PROC have digit = BOOL: curr char >= "0" AND curr char <= "9";
|
|
|
|
|
|
# parses a number out of the string #
|
|
# the number must be a sequence of digits with an optional leading minus sign #
|
|
PROC get number = INT:
|
|
BEGIN
|
|
|
|
INT number := 0;
|
|
|
|
INT sign multiplier = IF have minus
|
|
THEN
|
|
# negaive number #
|
|
# skip the sign #
|
|
next;
|
|
-1
|
|
ELSE
|
|
# positive number #
|
|
1
|
|
FI;
|
|
|
|
WHILE curr char >= "0" AND curr char <= "9"
|
|
DO
|
|
number *:= 10;
|
|
number +:= ( ABS curr char - ABS "0" );
|
|
next
|
|
OD;
|
|
|
|
# RESULT #
|
|
number * sign multiplier
|
|
END; # get number #
|
|
|
|
|
|
# main parsing #
|
|
WHILE str pos <= UPB s
|
|
DO
|
|
IF have minus OR have digit
|
|
THEN
|
|
# have the start of a number #
|
|
INT from value = get number;
|
|
element +:= 1;
|
|
IF NOT have minus
|
|
THEN
|
|
# not a range #
|
|
result[ element ] := from value
|
|
ELSE
|
|
# have a range #
|
|
next;
|
|
INT to value = get number;
|
|
result[ element ] := RANGE( from value, to value )
|
|
FI
|
|
ELSE
|
|
# should be a comma #
|
|
next
|
|
FI
|
|
OD;
|
|
# RESULT #
|
|
result
|
|
END; # parse range string #
|
|
|
|
|
|
# RESULT #
|
|
parse range string
|
|
END; # TORANGE #
|
|
|
|
|
|
# converts a []INT to a comma separated string of the elements #
|
|
OP TOSTRING = ( []INT values )STRING:
|
|
BEGIN
|
|
|
|
STRING result := "";
|
|
STRING separator := "";
|
|
|
|
FOR pos FROM LWB values TO UPB values
|
|
DO
|
|
result +:= ( separator + whole( values[ pos ], 0 ) );
|
|
separator := ","
|
|
OD;
|
|
|
|
# RESULT #
|
|
result
|
|
END; # TOSTRING #
|
|
|
|
|
|
# test #
|
|
print( ( TOSTRING range expand( TORANGE "-6,-3--1,3-5,7-11,14,15,17-20" ), newline ) )
|