newLISP for VisualNEO Win. Part 2: testing data
This tutorial is about functions that test for simple conditions. More complicated tests can be made by combining functions and logical operators (not, and, or).
NewLISP has so called predicates, that are functions that evaluate to TRUE or NIL after performing a test on its argument. As you know already, NIL is the newLISP equivalent to FALSE. There are a few built-in predicates, that are useful for you.
In this tutorial we’ll primarily stick to numbers and strings. Processing lists will be covered later.
To test the following code, run the VisualNeo sample application ‘mcTestNewLISP.exe’ (www.mcdigit.nl/mcTestnewLISP.zip), enter the expression and investigate the returned value.
Notice that newLISP is case-sensitive and uses e.g. lowercase function names!
1. Predicates, type 1
Predicates type 1 have one argument. Examples:
(even? 8)
→ true
(even? 7)
→ nil
a floating point will be converted to an integer by cutting off its fractional part:
(even? 8.7)
→ true
(odd? 7)
→ true
(odd? 8)
→ nil
(integer? 123)
→ true
(number? 123)
→ true
(number? 1.23)
→ true
(float? 1.23)
→ true
(string? “hello”)
→ true
(zero? 0)
→ true
(null? nil)
→ true
Study the following VisualNeo Win code:
SetVar "[x]" "1.23" hpwNewLispCall "(number? [x])" "[DllRetvar]"
→ true
2. Predicates, type 2
Predicates type 2 test the relationship between arguments.
(= (+ 3 3) 6)
→ true
(not (= (+ 3 3) 6))
→ nil
(null? (!= (+ 3 3) (+ 2 4)))
→ true
(< 3 (+ 2 2) 5 (+ 3 3) 7 (+ 4 4))
→ true
(> 6.5 7)
→ nil
(starts-with “Reinier Maliepaard is my name.” “babs|jennifer|reinier”)
→ nil
Add for a case insensitive check the number 1 as third argument.
(starts-with “Reinier Maliepaard is my name.” “babs|jennifer|reinier” 1)
→ true
The second argument ‘babs|jennifer|reinier’ is an example of a regular expression (see section 4).
(ends-with “My name is Reinier Maliepaard” “reinier maliepaard|babs berg|dean doe” 1)
→ true
(member “LISP” “my-newLISP”)
→ “LISP”
(member “new” “my-newLISP”)
→ “newLISP”
(member “my” “my-newLISP”)
→ “my-newLISP”
If the search-string is found, the function member returns a string starting with the search-string.
The functions ‘find’ and ‘find-all’, which also useful for tests, are covered in a next tutorial.
3. Combining functions
The logical operators ‘and’ and ‘or’ can be used to combine two or more predicates to a more complicated test.
(and (number? 123) (number? 456) (> 456 123))
→ true
What happens here? All three arguments are evaluated in order.
- The first argument (number? 123) evaluates to true
- The second argument (number? 456) evaluates to true.
- The third argument (> 456 123) evaluates to true.
Now the function ‘and’ will be applied to the arguments. Because all arguments are true, the value of the last evaluation will be returned.
(and (number? 123) (number? 456) (> 456 123) “456 is greater than 123”)
→ “456 is greater than 123”
Here the fourth argument is the string “456 is greater than 123”. Strings will always be evaluated to itself, returning simply its literal value.
(and (number? 123) (number? 456) (< 456 123) “456 is greater than 123”)
→ nil
The argument (< 456 123) evaluates to nil, which is also the returned value.
An example of VisualNeo code of combined functions:
SetVar "[x]" "123"SetVar "[y]" "456" hpwNewLispCall "(and (number? [x]) (number? [y]) (< (- [x] [y]) 0 ))" "[DllRetvar]"
→ true
4. User defined functions for testing
You can also get similar results by applying regular expressions. Regular expression pattern matching is done with the newLISP function Regex.
Although this blog is not about regular expressions, it’s necessary to introduce some basic syntax in order to understand
the power of regular expressions in newLISP (I’ll cover regular expressions later again when describing newLISP functions like find and replace).
The following metacharacters play an important role in regular expressions:
- Anchors: ^ and $
- Quantifiers: * + ? and {}
- OR operator: | or []
- Character classes: e.g. \d \w \s
The following examples demonstrate their use.
Expression | Matches |
[a-c]+ | Any (nonempty) string of a’s, b’s and c’s (such as a, abba, acbabcacaa) The quantifier + means ‘one or more’ |
^[a-c]+$ | Any (nonempty) string, containing only any of a, b and c The anchor ^ refers to the beginning and the anchor $ to the end. |
[a-c\s]+ | Any (nonempty) string of a’s, b’s, c’s and spaces, tabs or newlines |
^\w+$ | A ‘word’, i.e. nonempty sequence of alphanumeric characters and underscores |
d+ or [0-9]+ | Any one or more digits |
^\d+$ | Only one or more digits; same as ^[0-9]+$ |
^\d{2}$ | A nonempty sequence of only two digits; same as ^[0-9]{2}$ |
^\d{2}C?$ | A nonempty sequence of only two digits, followed by zero or one character C; same as ^[0-9]{2}C?$ The quantifier ? means ‘zero or one’. |
^[a-zA-Z]{2,3}.*$ | Any (nonempty) string, starting with two or three alphabetic characters followed by zero or more characters The quantifier * means ‘zero or more’. The dot in .* matches any character. |
^[a-zA-Z]{2}[\.]{1}.{3}$ | Any (nonempty) string, starting with two alphabetic characters followed by one dot and three characters. |
\. | Matches a dot. |
4.1 Have a try
Run the VisualNeo Win sample application ‘mcTestNewLISP.exe‘ (www.mcdigit.nl/mcTestnewLISP.zip), enter a regex expression and investigate the returned value. If no match is found, NIL will be returned. To match a nonempty string only containing one or more a’s, b’s, c’s, one could write
(regex “^[a-c]+$” “abcd”)
→ nil
Extend the character set into [a-d] and there will be a match.
(regex “^[a-d]+$” “abcd”)
→ (“abcd” 0 4)
If the returned value is not equal to NIL (which is here the case) then there is a match. In section 4.2 more about this.
For a case-insensitive match, add the option 1
(regex “^[A-D]+$” “abcd”)
→ nil
(regex “^[a-d]+$” “ABCD” 1) or (regex “^[A-D]+$” “abcd” 1)
→ (“abcd” 0 4)
4.2 Returned value in case of a match
If there is a match, the returned value is a in newLISP most commonly used data type: the list. In the last example of 4.1, the returned value is the list
(“abcd” 0 4)
It contains three elements. The first element is the match “abcd”. The second element is the offset of the match, the index 0. The third element is the length of the match. 4.
(regex “[a-e]+” “zzzabcdzzz”)
returns
(“abcd” 3 4)
The match is “abcd”, its offset is 3 and its length is 4.
You can of course process this list, but we’ll skip this subject for now.
4.3 Invoking Regex in hpwNewLispCall
To run the previous expressions in hpwNewLispCall, you’ve to modify the expressions slightly. The last example:
(regex “[a-e]+” “zzzabcdzzz”)
should be implemented as
hpwNewLispCall "(regex {[#91]a-e[#93]+} {zzzabcdzzz})" "[DllRetvar]"
So
- the opening double apostroph ” is replaced by { and the closing one by }
- the square bracket [ is substituted by [#91] and ] by [#93].
The curly brackets are also useful when you have backslashes in your expression, like \d
hpwNewLispCall "(regex {^\d+$} {123})" "[DllRetvar]"
instead of
hpwNewLispCall "(regex [#34]^\\d+$[#34] [#34]123[#34])" "[DllRetvar]"
5. Exercises and examples
Test for alphabetic and numeric characters
(regex “[a-zA-Z0-9]+” “123abc”)
→ (“123abc” 0 6)
Test for alphabetic and numeric characters and spaces, tabs and line breaks are allowed
(regex “[a-zA-Z0-9\\s]+” “12 3a bc@#&”)
→ (“12 3a bc” 0 8)
Test for numeric characters
(regex “[0-9]+” “1234”)
→ (“1234” 0 4)
Test for exactly 5 numeric characters
(regex {\d{5}} “1234”)
→ nil
Test for 4 or 5 numeric characters
(regex {\d{4,5}} “1234”)
→ (“1234” 0 4)
Test for alphabetic characters
(regex “[a-zA-Z]+” “abc”)
→ (“abc” 0 3)
Test for ONLY lowercase alphabetic characters
(regex “[a-z]+” “abc”)
→ (“abc” 0 3)
Test for ONLY uppercase alphabetic characters
(regex “[A-Z]+” “abc”)
→ nil
Test for valid email (yes, better validators can be made…)
(regex “^[a-zA-Z_\.]+@[a-zA-Z_]+?\.[a-zA-Z]{2,4}$” “dean.doe@some_host.info“)
→ (“dean.doe@some_host.info” 0 23)
Test for valid date with slashes 26/04/2019
(regex {^\d{1,2}\/\d{1,2}\/\d{4}$} {26/04/2019})
→ (“26/04/2019” 0 10)
Test for valid date with dots 1.5.2019
(regex {^\d{1,2}\.\d{1,2}\.\d{4}$} {1.5.2019})
→ (“1.5.2019” 0 8)
Test for valid IPv4 address
(regex {^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$} {172.16.254.1})
→ (“172.16.254.1” 0 12)
6. More about making tests in newLISP
The previous information on predicates and regular expressions in newLISP was about testing data.
More predicates and regular expression pattern matching (also supported in other newLISP functions) will be covered later.
Thank you for reading. Till next time!
Reinier Maliepaard
(last update: 31-05-2019)
Comments