• Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

World: r4wp

[Rebol School] REBOL School

GrahamC
24-Apr-2012
[102x3]
has anyone written parse rules for phone numbers?   I need to setup 
a set of rules for international numbers, local numbers, cell numbers, 
1800 etc for use in asterisk and prevent users from making toll calls 
unless authorise
To make it easier I will strip out alll non-digits first
I guess i should just make a rule for each type of call and then 
combine them.
Gregg
24-Apr-2012
[105x4]
Here's something for US phone numbers I did a long time ago
set 'parse-phone-num func [
        num [string!]
        /local  digit digits sep _ext_  ch nums pin ext
    ] [
        digit:  charset "0123456798"
        digits: [some digit]
        sep:    charset "()-._"
        _ext_:  ["ext" opt "." | "x"]

        nums: copy ""
        rules: [
            any [
                some [sep | copy ch digit (append nums ch)]
                | _ext_ copy ext digits
                | "pin" copy pin digits
            ]
            end
        ]
        either parse trim num rules 
            [reduce ['num nums 'ext ext 'pin pin]]
            [none]
    ]

    set 'well-formed-phone-number? func [num /local data] [
        either none? data: parse-phone-num num [false] [
            any [
                found? find [7 10] length? data/num
                all [11 = length? data/num  data/num/1 = #"1"]
            ]
        ]
    ]
And a formatter that goes the other way:
set 'format-phone-number func [
        num [string! object!] "String or object with /text value"
        /def-area-code area-code [string! integer!]
        /local left right mid obj res
    ] [
        left:  func [s len][copy/part s len]
        right: func [s len] [copy skip tail s negate len]
        mid:   func [s start len][copy/part at s start len]

        if object? num [obj: num  num: obj/text]

        res: either data: parse-phone-num num [
            ; discard leader if it's there.
            if all [11 = length? data/num  data/num/1 = #"1"] [
                data/num: right data/num 10
            ]
            rejoin [
                rejoin switch/default length? data/num [
                    7  [ compose [

                        (either area-code [rejoin ["(" area-code ") "]][])
                        left data/num 3 "-" right data/num 4
                    ]]
                    10 [[
                        "(" left data/num 3 ") "
                        mid data/num 4 3 "-" right data/num 4
                    ]]
                ][[data/num]]

                reduce either data/ext [[" ext" trim data/ext]] [""]

                reduce either data/pin [[" pin" trim data/pin]] [""]
            ]
        ][num]

        if obj [
            obj/text: res
            attempt [if 'face = obj/type [show obj]]
        ]
        res
    ]
GrahamC
24-Apr-2012
[109x2]
Ok, so this checks for legally formatted numbers.  Or formats numbers 
into the common format.
need to reboot .. clipboard not working.  When is openme coming :(
Gregg
24-Apr-2012
[111]
Yes. :-)
GrahamC
24-Apr-2012
[112x3]
p: format-phone-number "1800-123456"
== "(180) 012-3456"
might need a ittle tweaking for international numbers
How are international numbers formatted?  +181812345678  ?
Gregg
24-Apr-2012
[115x2]
As I said, it was just for US phone numbers. The +1 was not included, 
since it wasn't meant for dialing.
If you're asking how they're formatted in general, it depends.
GrahamC
24-Apr-2012
[117]
ok, so always 3 numbers in the first group if not international?
Gregg
24-Apr-2012
[118]
Yes.
GrahamC
24-Apr-2012
[119x2]
( we use 2 numbers for the first group unless it's cellular  when 
it's 3 or 4 )
can a number start with 1 ?
Gregg
24-Apr-2012
[121]
IIRC, some places use 4, grouping them into 2 sets of 2 for the area 
code.
GrahamC
24-Apr-2012
[122x2]
ohh ....
I guess users are going to have to make their own parse rules then
Gregg
24-Apr-2012
[124x2]
The code I posted doesn't do any validation according to what might 
work in the real world when dialed.
http://msdn.microsoft.com/en-us/goglobal/bb688129
http://en.wikipedia.org/wiki/Telephone_numbering_plan


I think the number of options are limited enough that you could get 
pretty far, but a custom system would be great. If you could template-ize 
things, users could submit their templates for others to use, or 
to be integrated into the default system.
GrahamC
24-Apr-2012
[126]
These rules are going into a cgi script to allow local and national 
dialiing (and emergency) but stop international
Gregg
24-Apr-2012
[127]
Ah, there you go. Validate against your local and national rules 
and fail everything else.
GrahamC
24-Apr-2012
[128]
yep
Gregg
24-Apr-2012
[129x4]
Not sure if this will help, but maybe:
parse-int-values: func [

    "Parses and returns integer values, each <n> chars long in a string."
    input [any-string!]

    spec [block!] "Dialected block of commands: <n>, skip <n>, done, 
    char, or string"
    /local
        gen'd-rules ; generated rules
        result      ; what we return to the caller

        emit emit-data-rule emit-skip-rule emit-literal-rule emit-data
        digit= n= literal=
        int-rule= skip-rule= literal-rule= done= build-rule=
        data-rule skip-rule
][

    ; This is where we put the rules we build; our gernated parse rules.
    gen'd-rules: copy []
    ; This is where we put the integer results
    result: copy []

    ; helper functions

    emit: func [rule n] [append gen'd-rules replace copy rule 'n n]
    emit-data-rule: func [n] [emit data-rule n]
    emit-skip-rule: func [n] [emit skip-rule n]
    emit-literal-rule: func [value] [append gen'd-rules value]
    emit-data: does [append result to integer! =chars]

    ; Rule templates; used to generate rules

    ;data-rule: [copy =chars n digit= (append result to integer! =chars)]
    data-rule: [copy =chars n digit= (emit-data)]
    skip-rule: [n skip]

    ; helper parse rules
	digit=: charset [#"0" - #"9"]
    n=: [set n integer!]
    literal=: [set lit-val [char! | any-string!]]

    ; Rule generation helper parse rules
    int-rule=: [n= (emit-data-rule n)]
    skip-rule=: ['skip n= (emit-skip-rule n)]
    literal-rule=: [literal= (emit-literal-rule lit-val)]
    done=: ['done (append gen'd-rules [to end])]

    ; This generates the parse rules used against the input

    build-rule=: [some [skip-rule= | int-rule= | literal-rule=] opt done=]


    ; We parse the spec they give us, and use that to generate the

    ; parse rules used against the actual input. If the spec parse

    ; fails, we return none (maybe we should throw an error though);

    ; if the data parse fails, we return false; otherwise they get
    ; back a block of integers. Have to decide what to do if they
    ; give us negative numbers as well.
    either parse spec build-rule= [
        either parse input gen'd-rules [result] [false]
    ] [none]
]
Example: parse-int-values "1234567890" [2 4 skip 2 2]
parse-int-values "(123) 456-7890" [#"(" 3 ") " 3 #"-" 4]
GrahamC
24-Apr-2012
[133]
http://en.wikipedia.org/wiki/Telephone_numbers_in_New_Zealand

learnt something new .. 02 is for Scott Base in Antarctica ;)
Gregg
24-Apr-2012
[134x2]
We had a REBOLer who worked there some years back. ...what was his 
name...
G. Scott Jones? Now it's gonna bother me. :-\
GrahamC
24-Apr-2012
[136]
yeah... though he won't be in Scott Base but the USA base there
Gregg
24-Apr-2012
[137]
Still probably a local call from Scott Base though. ;-)
GrahamC
24-Apr-2012
[138x3]
maybe not .. it may be routed thru the USA :)
zero: charset [ #"0" ]
digit: charset [ #"0" - #"9" ]
digits: [ some digit ]
local-number: charset [ #"3" #"4" #"6" #"9" ]
not01-number: charset [ #"2" - #"9" ]

national-rule: [ zero local-number not01-number digits]
local-rule: [ not01-number digits ]
emergency-rule: [ "111" ]

; toll free numbers are 0800 and 0508
toll-free: [ zero [ "800" | "508" ] digits ]

; directory assistance 018, 0172,
directory-assistance: [ "01" [ "8" | "72" ]]
 
; faults 120, 125 and business 126
faults: [ "12" [ "0" | "5" | "6" ]]

; cellular are 021, 022, 027, 028, 029
cellular-rule: [ "02" digits ]

; international 00 - country code - number
international-rule: [[ "00" | "+" ] digits ]

allowed-phone-rule: [
	local-rule |
	national-rule |
	emergency-rule |
	toll-free |
	directory-assistance |
	faults |
	cellular-rule
]

all-phone-rule: [ allowed-phone-rule | international-rule ]
hope that covers all of my dialling rules
Endo
25-Apr-2012
[141]
Also check E164 formatting:
http://en.wikipedia.org/wiki/E.164
and
http://www.wtng.info/wtng-tt.html
Arnold
26-Apr-2012
[142x2]
In PHP I had a Unix timestamp, which I didn't like, but it is in 
my database, so now I need this in my REBOL script too.
Oh found a solution at http://www.rebol.net/cookbook/recipes/0051.html
which works
Endo
26-Apr-2012
[144x2]
I use this function:
Int2Date: func [i][1-Jan-1970/0:00 + to-time i]

i is unix style date (seconds from 1970)

you may need to change 0:00 to your GMT, if you don't store GMT separately.
And for the oposite:
Date2Int: func [d /local c][
    c: 1-Jan-1970/0:00
    d - c * 86400 + to-integer d/time - c/time
]
james_nak
26-Apr-2012
[146]
Christopher Gill's form-date.r is something I can't live without.
http://www.rebol.org/view-script.r?script=form-date.r
Endo
26-Apr-2012
[147]
I use my own functions, returns always in ISO format, YYYY-MM-DD 
HH:NN:SS


padz: func ["Pad to right with zero." s n [integer!] /left /with 
c [char! string!]] [

    head insert/dup either left [tail form s] [form s] first form either 
    with [c] [#"0"] n - length? form s
]

format-date: func [d [date!]] [

 ajoin [d/date/year "-" padz d/date/month 2 "-" padz d/date/day 2 
 either d/time [ajoin [" " padz d/time/hour 2 ":" padz d/time/minute 
 2 ":" padz to-integer d/time/second 2]] [""]]
]
Gabriele
27-Apr-2012
[148]
endo, Date2Int can be simplified as:

>> to integer! difference now 1-jan-1970/0:00
== 1335506831
Endo
27-Apr-2012
[149]
That is much better. Thank you Gabriele!
Gregg
27-Apr-2012
[150x2]
DIFFERENCE will fail for future dates, starting mid-January 2038.

;>> difference 19-jan-2038/3:14:07 1-Jan-1970/0:0:0
;== 596523:14:07
;>> difference 19-jan-2038/3:14:08 1-Jan-1970/0:0:0
;== --596523:-14:-08
;>> difference 19-jan-2038/3:14:09 1-Jan-1970/0:0:0
;== -596523:14:07
Here's an old func I use: