268 lines
9.0 KiB
Python
268 lines
9.0 KiB
Python
import sys
|
|
import subprocess
|
|
|
|
class AbstractInterpreter:
|
|
def __init__(self):
|
|
pass
|
|
def isValid(self,formula)->bool:
|
|
raise NotImplementedError
|
|
class SimpleInterpreter(AbstractInterpreter):
|
|
def __init__(self):
|
|
pass
|
|
def isValid(self,formula)->bool:
|
|
|
|
variables = {}
|
|
f=[]
|
|
def isBlacklisted(char):
|
|
return char in ['\n', '\t', ' ']
|
|
def isSign(char):
|
|
logic_signs = ['=', '>', '&', '|', '!', '(', ')']
|
|
return char in logic_signs
|
|
def isParentice(char):
|
|
return char in ['(', ')']
|
|
def isVariable(char):
|
|
return not isSign(char)
|
|
def isNegation(char):
|
|
return char == '!'
|
|
#print(vars(formula))
|
|
if isSign(formula.s[-1]) and not isParentice(formula.s[-1]): #obrona przed a> na końcu
|
|
return False
|
|
for i,char in enumerate(formula.s):
|
|
if isBlacklisted(char): return False
|
|
|
|
prev = formula.s[i-1] if i>0 else None #None, gdy jesteśmy na pierwszym znaku
|
|
if (char=='(' and isVariable(prev) and prev is not None): #a(b|c)
|
|
|
|
return False
|
|
if (prev is None):
|
|
if not (isVariable(char) or isParentice(char) or isNegation(char)):
|
|
|
|
return False
|
|
if isSign(char) and prev is not None:
|
|
|
|
if (not isNegation(char)):
|
|
|
|
if isSign(prev) and not isNegation(prev): # tylko negacje mogą się powtarzać po innym znaku
|
|
return False
|
|
if isNegation(prev):
|
|
|
|
if not (isNegation(char) or isParentice(char)):
|
|
|
|
return False
|
|
if isNegation(char):
|
|
if isVariable(prev): #a!
|
|
return False
|
|
elif not isSign(char) and prev is not None:
|
|
#if current char is a variable
|
|
if isVariable(prev) and prev is not None: #Obrona abc>d
|
|
return False
|
|
return True
|
|
class RegexInterpreter(AbstractInterpreter):
|
|
def __init__(self):
|
|
#check if grep is available
|
|
import subprocess
|
|
# r=subprocess.run("grep")
|
|
|
|
def isValid(self,f)->bool:
|
|
|
|
formula = "({0})".format(f.s.replace("!", "\\!"))
|
|
|
|
make_reg = lambda formula,pattern: 'echo "{0}" | grep -P "{1}"'.format(formula,pattern)
|
|
def check_assertion(formula, pattern):
|
|
cmd = make_reg(formula,pattern)
|
|
#print("EXECUTING... ",cmd)
|
|
result = subprocess.run([cmd], shell=True, stdout=subprocess.PIPE)
|
|
return not len(result.stdout)>0
|
|
|
|
cmd =make_reg(formula,'^(\((?:[^()]++|(?1))*\))$')
|
|
#print("EXECUTING... ",cmd)
|
|
|
|
result = subprocess.run([cmd], shell=True, stdout=subprocess.PIPE)
|
|
|
|
isValid = len(result.stdout)>0
|
|
if not isValid: return False
|
|
|
|
#assertions
|
|
assertions = ['(?:[>|&|\|=][\(\)]*){2,}','\((?:[>|&|\|=][\(\)]*)','[a-z]{2,}','\((?:[>|&|\|=|!])?\)', '[>\|&=](?:[^a-z\(]|$)','[^>&\|=](\()' ]
|
|
for assertion in assertions:
|
|
if not check_assertion(formula,assertion):
|
|
#print(assertion)
|
|
return False
|
|
|
|
return True
|
|
# (?:[>|&|\|=][\(\)]*){2,} - czy powtarzają się jakieś operatory obok siebie?
|
|
|
|
|
|
# echo "formuła" | grep -P "regex" - to jeżeli coś zwróci to znaczy, że mamy dopasowanie
|
|
#najpierw sprawdzamy, czy parentices są ok
|
|
#^(\((?:[^()]++|(?1))*\))$ - powinno zwrócić cały nasz string
|
|
#potem sprawdzamy czy dopasuje cokolwiek co może być nie tak
|
|
# [>\|&=](?:[^a-z\(]|$) - operator przed czymś innym niż nawias otwierający lub zmienna
|
|
# [^>&\|=](\() - zmienna przed nawiasem bez operatora pomiędzy
|
|
# \((?:[>|&|\|=][\(\)]*) - (>b)
|
|
# [a-z]{2,} - czy powtarzają się jakieś lierki obok siebie?
|
|
# \((?:[>|&|\|=|!])?\) - czy istnieją puste nawiasy, bądź nawiasy z samym operatorem?
|
|
class ProperInterpreter(AbstractInterpreter):
|
|
class Expression:
|
|
def __init__(self,operator,left,right):
|
|
self.right=right
|
|
self.left = left
|
|
self.operator = operator
|
|
|
|
def __init__(self):
|
|
pass
|
|
def isValid(self,formula)->bool:
|
|
try:
|
|
self.parse(formula.s)
|
|
except Exception as e:
|
|
#print(e)
|
|
return False
|
|
else:
|
|
return True
|
|
def check_simple_formula(self,simple_formula:str)->bool:
|
|
#używamy SimpleInterpretera
|
|
n = SimpleInterpreter()
|
|
return Formula(simple_formula,n).isValid()
|
|
def parse(self,formula:str):
|
|
#przypisywanie głębi do każdego znaku
|
|
if len(formula)==0:
|
|
# print("Empty subformula")
|
|
return None
|
|
if (formula=='!'): raise Exception("Negacja źle postawiona")
|
|
x=[0 for u in formula]
|
|
current_depth=0
|
|
for i,char in enumerate(formula):
|
|
if current_depth<0:
|
|
break
|
|
if char == '(':
|
|
current_depth+=1
|
|
x[i] = current_depth
|
|
elif char == ')':
|
|
x[i] = current_depth
|
|
current_depth-=1
|
|
else:
|
|
x[i] = current_depth
|
|
|
|
|
|
if current_depth!=0:
|
|
raise Exception("Nawiasy źle umieszczone w podformule: {0}".format(formula))
|
|
|
|
#print(formula)
|
|
|
|
min_depth = min(x)
|
|
|
|
slice_point = 0
|
|
#czy formuła jest prosta?
|
|
is_simple = True
|
|
for i in x:
|
|
if i != x[0]:
|
|
is_simple=False;
|
|
break
|
|
#Szukanie najbardziej znaczącego spójnika
|
|
for i,char in enumerate(formula):
|
|
if char in ('>', '&', '|', '=') and x[i] == min_depth:
|
|
slice_point = i
|
|
break
|
|
if is_simple:
|
|
#print(formula, " is simple")
|
|
if not self.check_simple_formula(formula):
|
|
raise Exception("Błąd na poziomie formuły bez zagnieżdzeń: {0}".format(formula))
|
|
return False
|
|
|
|
else:
|
|
s=0
|
|
e=len(formula)
|
|
if min_depth>0:
|
|
# print("There are useless parentices... Deleting")
|
|
s=1
|
|
e=-1
|
|
# print("Slice point: ", slice_point)
|
|
# print("min depth: ", min_depth)
|
|
if (s==slice_point):
|
|
#print("Nie ma spójnika - formuła jest jednak prosta")
|
|
if not self.check_simple_formula(formula):
|
|
raise Exception("Błąd na poziomie formuły bez zagnieżdzeń: {0}".format(formula))
|
|
return False
|
|
self.parse(formula[s:slice_point])
|
|
self.parse(formula[slice_point+1:e])
|
|
class Formula:
|
|
def __init__(self, formula_string:str, interpreter:AbstractInterpreter=None):
|
|
self.s = formula_string
|
|
self.__i = interpreter
|
|
|
|
def isValid(self, interpreter=None)->bool:
|
|
i = self.__i if interpreter is None else interpreter
|
|
return i.isValid(self)
|
|
def print_help():
|
|
print("""
|
|
Użycie:
|
|
python program.py -f plik.txt
|
|
python program.py a&b>c|(a&!g
|
|
python program.py
|
|
python program.py -h
|
|
""")
|
|
|
|
sim = SimpleInterpreter()
|
|
if len(sys.argv) > 1 :
|
|
i = ProperInterpreter()
|
|
if '-h' in sys.argv:
|
|
print_help()
|
|
else:
|
|
if '-g' in sys.argv:
|
|
i = RegexInterpreter()
|
|
#print(sys.argv)
|
|
|
|
if '-f' in sys.argv:
|
|
#open from file
|
|
print("opening from file")
|
|
g = open(sys.argv[sys.argv.index('-f')+1], 'r')
|
|
u = g.readlines()
|
|
for l in u:
|
|
print(Formula(l.replace("\n",'').replace("\r",'').replace(' ',''),i).isValid())
|
|
else:
|
|
o = sys.argv[1] if sys.argv[1] != '-g' else sys.argv[2]
|
|
if len(o)>0:
|
|
print(Formula(o.replace(' ',''),i).isValid())
|
|
|
|
else:
|
|
print_help()
|
|
print("Tryb interaktywny:")
|
|
print("""
|
|
Program spawdza poprawność formuł logicznych,
|
|
znaki [a-z] są uznawane za zmienne logiczne
|
|
znaki języka rachunku zdań:
|
|
! - negacja
|
|
- implikacja
|
|
|
|
| - alternatywa
|
|
& - komiunkcja
|
|
= - równoważność
|
|
wyjście programu:
|
|
True - zdanie logiczne jest poprawne w rozumieniu KRZ
|
|
False - zdanie logiczne nie jest poprawne w rozumienu KRZ
|
|
|
|
""")
|
|
print("Wprowadź formułę lub wpisz \n\n 1. Regex \n 2. Regular \n \n aby zmienić rodzaj używanego walidatora. Domyślnym jest Regular.\n Wpisz exit żeby wyjść")
|
|
i = ProperInterpreter()
|
|
|
|
while True:
|
|
try:
|
|
x = input('> ')
|
|
except:
|
|
break
|
|
if x=="Regex":
|
|
i=RegexInterpreter()
|
|
print("Zmieniono walidator")
|
|
|
|
elif x=="Regular":
|
|
i=ProperInterpreter()
|
|
print("Zmieniono walidator")
|
|
elif x=="exit" or x =="Exit":
|
|
break
|
|
else:
|
|
|
|
print(Formula(x.replace(" ",''),i).isValid())
|
|
|
|
|
|
|