1:- module(nanp, [invalid/1, valid/1, format/3]).    2:- use_module(library(clpfd)).
 valid(?Number)
True if Number is a valid phone number. A Number is represented as a term like nanp(Area,Office,Subscriber).
    8valid(nanp(Area,Office,Subscriber)) :-
    9    valid_area_code(Area,_,_,_),
   10    valid_office_code(Office,_,_,_),
   11    valid_subscriber_code(Subscriber,_,_,_,_).
   12
   13% see http://www.nanpa.com/area_codes/index.html for rules
   14valid_area_code(NXX,N,X1,X2) :-
   15    NXX in 200..999,
   16    N in 2..9,
   17    X1 in 0..8,  % 9 is prohibited until a theoretical, future expansion
   18    X2 in 0..9,
   19    NXX #= 100*N + 10*X1 + X2,
   20    N #= 3 #==> X1 #\= 7,
   21    N #= 9 #==> X1 #\= 6.
   22
   23% see http://www.nanpa.com/number_resource_info/co_codes.html for rules
   24valid_office_code(NXX,N,X1,X2) :-
   25    NXX in 200..999,
   26    N in 2..9,
   27    [X1,X2] ins 0..9,
   28    NXX #= 100*N + 10*X1 + X2,
   29    X1 #= 1 #==> X2 #\= 1.
   30
   31valid_subscriber_code(S,A,B,C,D) :-
   32    S in 0..9999,
   33    [A,B,C,D] ins 0..9,
   34    S #= 1000*A + 100*B + 10*C + D.
   35
   36% easily recognizable code
   37erc(NXX) :-
   38    valid_area_code(NXX,_,X,X).
   39
   40% N11 codes
   41n11(NXX) :-
   42    valid_office_code(NXX,_,1,1).
 invalid(+Number) is semidet
True if Number is not a valid phone number. A Number is represented as a term like nanp(Area,Office,Subscriber).
   49invalid(Number) :-
   50    \+ valid(Number).
 format(?Format, ?Number, ?String:codes)
True if String represents Number according to Format. At least one of Format, Number or String must be instantiated. Format is on of the following phone number formats:

This predicate may be used for parsing and formatting telephone numbers.

   66format(Format,Number,Codes) :-
   67    phrase(format(Format,Number),Codes).
   68
   69format(e_164,nanp(A,O,S)) --> % https://en.wikipedia.org/wiki/E.164
   70    "+1",
   71    area_code(A),
   72    office_code(O),
   73    subscriber_code(S).
   74format(dial(us),Number) --> % format describes dialing a number from US
   75    "1-",
   76    format(ten_digit,Number).
   77format(ten_digit,nanp(A,O,S)) -->
   78    area_code(A),
   79    "-",
   80    office_code(O),
   81    "-",
   82    subscriber_code(S).
   83format(traditional,nanp(A,O,S)) --> % http://goo.gl/kkHkZY
   84    "(",
   85    area_code(A),
   86    ") ",
   87    office_code(O),
   88    "-",
   89    subscriber_code(S).
   90format(uri,Number) -->
   91    "tel:",
   92    format(e_164,Number).
   93
   94area_code(A) -->
   95    { valid_area_code(A,N,X1,X2) },
   96    digit(N),
   97    digit(X1),
   98    digit(X2).
   99
  100office_code(O) -->
  101    { valid_office_code(O,N,X1,X2) },
  102    digit(N),
  103    digit(X1),
  104    digit(X2).
  105
  106subscriber_code(S) -->
  107    { valid_subscriber_code(S,A,B,C,D) },
  108    digit(A),
  109    digit(B),
  110    digit(C),
  111    digit(D).
  112
  113digit(N) -->
  114    [C],
  115    { digit_char(N,C) }.
  116
  117digit_char(0,0'0).
  118digit_char(1,0'1).
  119digit_char(2,0'2).
  120digit_char(3,0'3).
  121digit_char(4,0'4).
  122digit_char(5,0'5).
  123digit_char(6,0'6).
  124digit_char(7,0'7).
  125digit_char(8,0'8).
  126digit_char(9,0'9)