This library can be used to parse string input that contains seven fundamental propositional logic symbols:
Library takes a string input and produces a multidimensional list of given literals with parseInput function. Structure of the nested output list is created to contain all information required to use it programmatically for boolean operations. Validate method is used to check if given input is in correct syntax form. You can deformat well formatted list structure back to literal representation. Furthermore evaluation of the given logic clause or parsed list structure can be done with an optional truth table.
Library is implemented in three programming languages: [Python](#Python-version), [PHP](#PHP-version) and [Javascript](#Javascript-version). For both PHP and Javascript version, see [PLCParser demo application](https://plcparser.herokuapp.com/) deployed in Heroku for live demonstration.
Download module pyPLCParser from: https://github.com/markomanninen/PLCParser
In [1]:
# load library
from pyPLCParser import parseInput
# set input
i = "(1 AND 0)"
# parse input
o = parseInput(i)
# print output
print(o)
From the output (['1', -2, '2']) we can notice that negative number -2 is used to mark $AND$ operator. You could also input: (1 & 2) or (1 ∧ 2) to get the same result. $AND$ keyword is case insensitive so use what ever format you like: AnD, and, anD, and so forth.
For an operator you can use either $AND, OR, NOT, XOR$ keywords or single ascii characters on the clause: $\&$, $|$, $\\!$, ^ respectively. These corresponding mathematical symbols are also accepted: $∧$, $∨$, $¬$, $⊕$.
Negative logical gate symbols are also supported: $NAND$ ($/$, $↑$), $XNOR$ ($=$, $↔$) and $NOR$ ($†$, $↓$). It is recommended that you use either word or mathematical symbols, because conventions of the character codes are different on the different programming systems.
In [2]:
# use | for OR and ⊕ for XOR
print(parseInput("(1 | 0)"), parseInput("(1 ⊕ 0)"))
# use NAND
print(parseInput("(1 ↑ 0)"))
But say, you have a more complex nested clause in your hands, what is the outcome?
In [3]:
i = "(A or (B and (!C and (D xor E))))"
parseInput(i)
Out[3]:
PLCParser tries to deformalize and interpret the boolean operator precedence. But sometimes it is better that you decide and choose correctly the format of the nested set / parentheses to get the right result. Order of the precedence differs by authors anyway. In PLCParser this order is used: $NOT$, $AND$, $XOR$, $OR$.
Without parentheses similar input would be interpreted like this:
In [4]:
from pyPLCParser import deformatInput
i = "A or B and !C and D or E"
deformatInput(parseInput(i))
Out[4]:
In [5]:
from pyPLCParser import PLCParser, NOT_OPERATOR, AND_OPERATOR, XOR_OPERATOR, \
OR_OPERATOR, NAND_OPERATOR, XNOR_OPERATOR, NOR_OPERATOR
# input without nested parentheses
i = "(1 and 0 nand 1 or 1 nor 0)"
# init object
c = PLCParser()
# output default nesting
print(c.parse(i))
# set up your own operator predecence. note that usually not operator is the last
# while the least weight operators are given first.
operator_precedence = (OR_OPERATOR, NOR_OPERATOR, XOR_OPERATOR, XNOR_OPERATOR,
AND_OPERATOR, NAND_OPERATOR, NOT_OPERATOR)
# use argument
c = PLCParser(operator_precedence=operator_precedence)
# output new nested set
print(c.parse(i))
Like you can see, the nesting of the lists is done a bit different on these two case.
In [6]:
i = "('Queen Elizabeth' & 'Philip, Duke of Edinburgh')"
parseInput(i)
Out[6]:
By default literals are expected to be wrapped with single ' or double " quotes. Parentheses are assumed to be ( for the right parentheses and ) for the left.
If default parentheses and literal wrappers are not suitable for your needs, you can change them and parse input accordingly:
In [7]:
from pyPLCParser import PLCParser
c = PLCParser(parentheses=['[', ']'], wrappers=['´'])
i = "[´Use´ and ´as you´ and wish]"
c.parse(i)
Out[7]:
In [8]:
parseInput("""
(!A and !(B or C))
""")
Out[8]:
Same input could be written many ways, for example:
(!A and !B and !C) is same as (!A !B !C)
Note that the meaning of !(A and B and C) however is different. It means the negation of a group where all items A, B and C exists. If only one or two of the group items existed, then negation wouldn't be true.
In [9]:
# multiple and operands
i1 = '(& 1 1 1 0)'
o1 = parseInput(i1)
print(o1)
print(i1, " = ", deformatInput(o1))
In [10]:
# xor logic -> one of the group, but not all
i1 = '(A ^ B)' # or just (^(A B))
o1 = parseInput(i1)
print(o1)
# xor logic with and, or, and not operators #1
i2 = '((A or B) and !(A and B))'
o2 = parseInput(i2)
print(o2)
# xor logic with and, or, and not operators #2
i3 = '((A and !B) or (!A and B))'
o3 = parseInput(i3)
print(o3)
# xor logic with and, or, and not operators #3
i4 = '((A or !B) and (!A or B))'
o4 = parseInput(i4)
print(o4)
Apparently using $XOR$ can save a lot of space!
ValidateInput method is used to validate given clause in string format. It can be used to roughly check that parentheses and literals are correctly formed. Then it is safer to use parseInput and evaluateInput functions.
In [11]:
from pyPLCParser import validateInput
# input has extra )
print (validateInput('(A or B))'))
# input should be ok
print (validateInput('(A or B)'))
Of cource it is good to have a method to deformat native list structure back to the string clause representation.
With an optional argument, one can use special character abbreviations for logic operators. By default formal keywords are used on output. Operator representation types are: word, char and math:
In [12]:
from pyPLCParser import PLCParser
c = PLCParser()
# set input (A and B)
i = ['A', -2, 'B']
print(c.deformat(i, operator_type="word"))
print(c.deformat(i, operator_type="char"))
print(c.deformat(i, operator_type="math"))
In [13]:
from pyPLCParser import evaluateInput
i = "(1 or 0)"
o = evaluateInput(i)
print("%s => %s" % (i, o))
i = "(1 and 0)"
o = evaluateInput(i)
print("%s => %s" % (i, o))
i = "(true and false)"
o = evaluateInput(i)
print("%s => %s" % (i, o))
i = "(p and q)"
truth_table = {'p': True, 'q': False}
o = evaluateInput(i, truth_table)
print("%s => %s" % (i, o))
The last example uses truth table to define, how different operands should be interpreted. By default only numbers 1 and 0, and booleans true and false, can be interpreted accordingly.
Last example demonstrates $NAND$, $XNOR$ and $NOR$ operators:
In [14]:
i = "(1 nand 1)"
o = evaluateInput(i)
print("%s => %s" % (i, o))
i = "(1 xnor 0)"
o = evaluateInput(i)
print("%s => %s" % (i, o))
i = "(1 nor 0)"
o = evaluateInput(i)
print("%s => %s" % (i, o))
In [15]:
%%html
require_once('./src/elonmedia/plcparser/php/PLCParser.php')
print_r(PLCParser::parseInput("('A' or 'B')"));
And output would be:
In [16]:
%%html
Array
(
[0] =>
[1] => Array
(
[0] => Array
(
[0] => Array
(
[0] => A
[2] => B
)
)
)
)
In [17]:
%%html
<script src="./dist/PLCParser.min.js"></script>
<script>
var v = PLCParser.validateInput("(A and B (C | D) !F)")
console.log(v)
var p = PLCParser.parseInput("(A and B (C | D) !F)")
console.log(p)
var e = PLCParser.evaluateInput("(true or false)")
console.log(e)
var d = PLCParser.deformatInput([-1, ['A', 'B']])
console.log(d)
</script>
For both PHP and Javascript version, see PLCParser demo application deployed in Heroku for live testing.
Copyright © 2017 Marko Manninen