Skip to content

Commit cf0980a

Browse files
committed
Add project, with basic functionality
Tokenises expressions Prepares a truth table Simulates the expression Works with AND and OR, and variables No ( ) or NOT support
1 parent 2c11631 commit cf0980a

File tree

3 files changed

+173
-0
lines changed

3 files changed

+173
-0
lines changed

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"python.analysis.typeCheckingMode": "basic"
3+
}

boolean_expression_parser.py

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
TOKENS = {
2+
'AND': 'AND',
3+
'.': 'AND',
4+
'OR': 'OR',
5+
'+': 'OR',
6+
'NOT': 'NOT',
7+
'!': 'NOT',
8+
'^': 'NOT',
9+
'¬': 'NOT',
10+
'(': 'OPEN_PAREN',
11+
')': 'CLOSE_PAREN',
12+
}
13+
14+
OPERATORS = ['AND', 'OR', 'NOT']
15+
16+
DEBUG_LOGGING = False
17+
18+
19+
Table = list[list[bool | None]]
20+
Expression = tuple[list[str], list[str]]
21+
22+
23+
def debug_print(*values):
24+
if DEBUG_LOGGING:
25+
print(*values)
26+
27+
28+
def print_table(expr: Expression, table: Table) -> None:
29+
'''Prints a table in a nice format.'''
30+
31+
header = ' '.join(expr[1]) + ' | Result'
32+
print(header)
33+
print('-' * len(header))
34+
35+
for row in table:
36+
print(' '.join([ ('1' if x else '0') for x in row[:-1] ]), '|', '1' if row[-1] else '0')
37+
38+
39+
40+
def tokenise(text: str) -> Expression:
41+
'''Converts a boolean expression string into a list of tokens.'''
42+
43+
tokens: list[str] = []
44+
variables: list[str] = []
45+
text = text.upper()
46+
47+
i: int = 0
48+
while True:
49+
if i >= len(text):
50+
break
51+
52+
char = text[i]
53+
54+
if char == ' ':
55+
i += 1
56+
continue
57+
58+
if ' ' in text[i:]:
59+
next_word_index = text.index(' ', i)
60+
else:
61+
next_word_index = len(text) - 1
62+
63+
word = text[i:next_word_index]
64+
65+
debug_print(f'{char} {next_word_index} {word}')
66+
67+
if word in TOKENS:
68+
tokens.append(TOKENS[word])
69+
i = next_word_index
70+
continue
71+
72+
if char in TOKENS:
73+
tokens.append(TOKENS[char])
74+
i += 1
75+
continue
76+
77+
if char not in variables:
78+
variables.append(char)
79+
80+
tokens.append(f'VAR_{char}')
81+
82+
i += 1
83+
84+
variables.sort()
85+
86+
return tokens, variables
87+
88+
89+
90+
def prepare_table(expr: Expression):
91+
variables = { x[0]: 0 for x in expr[1] }
92+
93+
num_rows = 2 ** len(variables)
94+
table: Table = []
95+
96+
for row in range(num_rows):
97+
bin_str = bin(row)[2:].zfill(len(variables)) # Remove 0b prefix
98+
table.append([ bool(int(x)) for x in bin_str ])
99+
table[row].append(None)
100+
101+
return table
102+
103+
104+
105+
def simulate(expr: Expression, table: Table):
106+
107+
tokens = expr[0]
108+
variables = expr[1]
109+
110+
for row in table:
111+
for i, token in enumerate(tokens):
112+
if token in OPERATORS:
113+
result: bool | None = None
114+
115+
match token:
116+
case 'AND':
117+
operand1_token = tokens[i - 1]
118+
operand2_token = tokens[i + 1]
119+
operand1 = row[variables.index(operand1_token.replace('VAR_', ''))]
120+
operand2 = row[variables.index(operand2_token.replace('VAR_', ''))]
121+
result = operand1 and operand2
122+
case 'OR':
123+
operand1_token = tokens[i - 1]
124+
operand2_token = tokens[i + 1]
125+
operand1 = row[variables.index(operand1_token.replace('VAR_', ''))]
126+
operand2 = row[variables.index(operand2_token.replace('VAR_', ''))]
127+
result = operand1 or operand2
128+
case 'NOT':
129+
operand1_token = tokens[i + 1]
130+
operand1 = row[variables.index(operand1_token.replace('VAR_', ''))]
131+
result = not operand1
132+
133+
row[-1] = result
134+
135+
return table
136+
137+
138+
139+
140+
141+
def main():
142+
'''Main program loop.'''
143+
144+
print('Basic information:')
145+
print(' - inputs should be letters, case insensitive (i.e. a=A)')
146+
print(' - allowed gates: AND (.), OR (+), NOT (!,¬,^)')
147+
print(' - allowed punctuation: (, )')
148+
print(' - enter \'q\' to quit')
149+
150+
stop = False
151+
while not stop:
152+
153+
expr1 = tokenise('A AND B . C OR A')
154+
debug_print(f'Tokens: {",".join(expr1[0])}')
155+
debug_print(f'Variables: {",".join(expr1[1])}')
156+
157+
table1 = prepare_table(expr1)
158+
table1 = simulate(expr1, table1)
159+
160+
print_table(expr1, table1)
161+
162+
163+
stop = True
164+
165+
166+
167+
if __name__ == '__main__':
168+
main()

errors.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class UnknownTokenError(Exception):
2+
pass

0 commit comments

Comments
 (0)