Search code examples
listparsingprologswi-prolog

Checking IP like address list with Prolog


I'm trying to build a predicate that given a list (eg. ['1', '2', '3', '.', ... , '2']) it has to recognise it as an IP string in the format (NNN.NNN.NNN.NNN) where N is a digit (between 0 and 9) it return true if it is correctly formatted. It must accept also the case where the IP is only (N.N.N.N or NNN.NNN.N.N and other combinations).

This is what I've done so far

test([X,Y,Z,D | String]) :- digit(X), digit(Y), digit(Z), dot(D), test(String).

Where digit is a list of facts like digit('0'). from 0 to 9; and dot is a fact defined as dot('.'). My code lacks of modularity (it can't recognise single N). Can you help me achieve this?

Thank you!


Solution

  • Welcome to SO. I would do it like this:

    seperate the input list into 4 "blocks", where the dot is the delimiter and accept only blocks with 1-3 digit elements in them. To do this prolog can be a bit tricky due to the constraints on the length of the numbers (1-3) and the constraints on the dots (exactly 3 dots). So my approach would look like this (basic but a bit twisted):

    test(In):-
        digits123(In,['.'|R1]),
        digits123(R1,['.'|R2]),
        digits123(R2,['.'|R3]),
        digits123(R3,[]).
        
    digits123([A,B,C|R],R):-
        digit(A),
        digit(B),
        digit(C).
    digits123([A,B|R],R):-
        digit(A),
        digit(B).
    digits123([A|R],R):-
        digit(A).
    

    What does it do? digits123/2 gets as input a list and it checks if either the first 3, 2, or one elements are digits. Also the rest of the list is unified with the second argument, making this a test for the numbers and a generator/test for the rest list. digits123/2 alone would be buggy since three digits could be mistaken for 2 or one digit. So the call makes it special: at first digits123/2 is called in the inputlist In, returning the list with the remaining elements. But this remaining list has to start with a dot (['.'|R1]). By writing it like this, R1 is the remaining list without the dot as delimiter. Repeat this two times more to check the 2nd and 3rd block. The last block is different: it has 1-3 digits but no dot (or anything else) afterwards. In other words: there should no elements remain (empty list, []).

    Tested with SWISH:

    ?- test(['1', '2', '3', '.', '2', '3', '.', '2', '.' , '2']).
    true;
    false.
    
    ?- test(['1', '2', '3', '.', '2', '3', '.', '2', '.' ]).
    false.