#!/usr/bin/env perl # *************************************************************************** #.MODULE: sexagesimal.pl #.PURPOSE: Parser for a single sexagesimal number #.AUTHOR: A.Micol, ST-ECF #.DATE: A boring Saturday night. #.DEFINITIONS: # # 1.- A (one) sexagesimal number is a sequence of up to 3 (minimum 1) # groups of numbers separated (followed) by an indefinite number of # whatever characters except 0-9 + - # Only one group, ***the last one***, can be a float. # If none of the groups is a float, the sequence must contain 3 groups. # Only one group, the first one, can have a sign (+-). # The second and third groups are base-60. # # 2.- A pair of sexagesimal numbers is a sequence of two sexagesimal numbers. # # *************************************************************************** my( @group, $rest, $is_float, $is_integer ); my( $sexagesimal ) = $ARGV[0]; print "Input string: [$sexagesimal]\n"; # # Copy input into destroyable $trynum # my( $trynum ) = $sexagesimal; # # Try the three groups, but stop as soon as float is encountered. # foreach( $i=0; $i < 3; $i++) { # Get first available numeric group in trynum ( $is_float, $float, $sep, $rest ) = &isFloat( $trynum ); if ( $is_float ) { &checkCompliance( $i, $float ); $group[ $i ] = $float; last; } else { ( $is_integer, $integer, $sep, $rest ) = &isInteger( $trynum ); if ( ! $is_integer ) { &exception( $trynum ); } &checkCompliance( $i, $float ); $group[ $i ] = $integer; } if ( $sep =~ /[+-]/ ) { &exception( "Found sign in wrong position\n" ); } $trynum = $rest; if ( $trynum eq "" ) { last; } } # # Last group can be integer if and only if it is the third one! # if ( $#group + 1 < 3 && ! $is_float ) { &exception( "Found only two integer groups" ); } # # Final check: # No remaining part of the input string can contains other digits! # if ( $sep =~ /.*\d.*/ ) { &exception( "Found extra digits ($sep)" ); } if ( $rest =~ /.*\d.*/ ) { &exception( "Found extra digits ($rest)" ); } # # Success! # print "Found a ", $#group + 1, "-group sexagesimal number.\n"; # # Return the sexagesimal number in a "canonical" format (e.g. 12:00:23.71). # $canonical = ""; foreach( $i=0; $i < $#group+1; $i++ ) { $canonical .= "$group[$i]:"; } chop( $canonical ); print "Canonical form: [$canonical]\n"; #print "Successfully parsed sexagesimal\n"; # # --- END OF MAIN --- # # # The second or third group shall not be negative, nor .ge. 60. sub checkCompliance { my( $group_index, $number ) = @_; if ( $group_index > 0 && $number < 0.0 ) { &exception( "Only first group can be negative" ); } if ( $group_index > 0 && $number >= 60.0 ) { &exception( "Only first group can be greater (or equal) than 60." ); } } sub isInteger { my( $num ) = $_[0]; my( $is_integer ) = 0; my( $integer, $rest ); if ( $num =~ /^([+-]){0,1}(\d+)(\D+)(.*)/ || $num =~ /^(\d+)$/ ) { $is_integer = 1; $sign = $1; $integer = $2; $separator = $3; $rest = $4; } return( $is_integer, $sign.$integer, $separator, $rest ); } sub isFloat { my( $num ) = $_[0]; my( $is_float ) = 0; my( $float, $rest ); if ( $num =~ /^([+-]){0,1}(\d+\.\d*)(\D+.*)/ || $num =~ /^([+-]){0,1}(\d+\.\d*)$/ ) { $is_float = 1; $sign = $1; $float = $2; $separator = $3; $rest = $4; } return( $is_float, $sign.$float, $separator, $rest ); } sub exception { my( $msg ) = $_[0]; print "*** ERROR $msg \n"; exit(1); }