Skip to content

Commit 522e640

Browse files
authored
While loops and conditional statements (#316)
* added math to variables as well as the ability to use them in DELAY and PRINT/PRINTLN * add comparisons * fix typo * Added While loops and Conditional statements * If and While loops working
1 parent e61d232 commit 522e640

File tree

1 file changed

+117
-17
lines changed

1 file changed

+117
-17
lines changed

duckyinpython.py

Lines changed: 117 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
# Author: Dave Bailey (dbisu, @daveisu)
44
#
55
# TODO: ADD support for the following:
6-
# IF THEN ELSE
76
# Add jitter
87
# Add LED functionality
98

@@ -71,6 +70,98 @@
7170
numbers = "0123456789"
7271
specialChars = "!@#$%^&*()"
7372

73+
class IF:
74+
def __init__(self, condition, codeIter):
75+
self.condition = condition
76+
self.codeIter = list(codeIter)
77+
self.lastIfResult = None
78+
79+
def _exitIf(self):
80+
_depth = 0
81+
for line in self.codeIter:
82+
line = self.codeIter.pop(0)
83+
line = line.strip()
84+
if line.upper().startswith("END_IF"):
85+
_depth -= 1
86+
elif line.upper().startswith("IF"):
87+
_depth += 1
88+
if _depth < 0:
89+
print("No else, exiting" + str(list(self.codeIter)))
90+
break
91+
return(self.codeIter)
92+
93+
def runIf(self):
94+
if isinstance(self.condition, str):
95+
self.lastIfResult = evaluateExpression(self.condition)
96+
elif isinstance(self.condition, bool):
97+
self.lastIfResult = self.condition
98+
else:
99+
raise ValueError("Invalid condition type")
100+
101+
# print(f"condition {self.condition} result is {self.lastIfResult} since \"$VAR\" is {variables["$VAR"]}, code is {self.codeIter}")
102+
depth = 0
103+
for line in self.codeIter:
104+
line = self.codeIter.pop(0)
105+
line = line.strip()
106+
if line == "":
107+
continue
108+
# print(line)
109+
110+
if line.startswith("IF"):
111+
depth += 1
112+
elif line.startswith("END_IF"):
113+
if depth == 0:
114+
return(self.codeIter, -1)
115+
depth -=1
116+
117+
elif line.startswith("ELSE") and depth == 0:
118+
# print(f"ELSE LINE {line}, lastIfResult: {self.lastIfResult}")
119+
if self.lastIfResult is False:
120+
line = line[4:].strip() # Remove 'ELSE' and strip whitespace
121+
if line.startswith("IF"):
122+
nestedCondition = _getIfCondition(line)
123+
# print(f"nested IF {nestedCondition}")
124+
self.codeIter, self.lastIfResult = IF(nestedCondition, self.codeIter).runIf()
125+
if self.lastIfResult == -1 or self.lastIfResult == True:
126+
# print(f"self.lastIfResult {self.lastIfResult}")
127+
return(self.codeIter, True)
128+
else:
129+
return IF(True, self.codeIter).runIf() #< Regular ELSE block
130+
else:
131+
self._exitIf()
132+
break
133+
134+
# Process regular lines
135+
elif self.lastIfResult:
136+
# print(f"running line {line}")
137+
self.codeIter = list(parseLine(line, self.codeIter))
138+
# print("end of if")
139+
return(self.codeIter, self.lastIfResult)
140+
141+
def _getIfCondition(line):
142+
return str(line)[2:-4].strip()
143+
144+
def _isCodeBlock(line):
145+
line = line.upper().strip()
146+
if line.startswith("IF") or line.startswith("WHILE"):
147+
return True
148+
return False
149+
150+
def _getCodeBlock(linesIter):
151+
"""Returns the code block starting at the given line."""
152+
code = []
153+
depth = 1
154+
for line in linesIter:
155+
line = line.strip()
156+
if line.upper().startswith("END_"):
157+
depth -= 1
158+
elif _isCodeBlock(line):
159+
depth += 1
160+
if depth <= 0:
161+
break
162+
code.append(line)
163+
return code
164+
74165
def evaluateExpression(expression):
75166
"""Evaluates an expression with variables and returns the result."""
76167
# Replace variables (e.g., $FOO) in the expression with their values
@@ -82,6 +173,9 @@ def evaluateExpression(expression):
82173

83174
return eval(expression, {}, variables)
84175

176+
def deepcopy(List):
177+
return(List[:])
178+
85179
def convertLine(line):
86180
commands = []
87181
# print(line)
@@ -134,7 +228,6 @@ def replaceDefines(line):
134228

135229
def parseLine(line, script_lines):
136230
global defaultDelay, variables, functions, defines
137-
print(line)
138231
line = line.strip()
139232
line = line.replace("$_RANDOM_INT", str(random.randint(int(variables.get("$_RANDOM_MIN", 0)), int(variables.get("$_RANDOM_MAX", 65535)))))
140233
line = replaceDefines(line)
@@ -143,7 +236,7 @@ def parseLine(line, script_lines):
143236
elif line.startswith("REM_BLOCK"):
144237
while line.startswith("END_REM") == False:
145238
line = next(script_lines).strip()
146-
print(line)
239+
# print(line)
147240
elif(line[0:3] == "REM"):
148241
pass
149242
elif line.startswith("HOLD"):
@@ -190,7 +283,8 @@ def parseLine(line, script_lines):
190283
elif(line[0:6] == "STRING"):
191284
sendString(replaceVariables(line[7:]))
192285
elif(line[0:5] == "PRINT"):
193-
print("[SCRIPT]: " + line[6:])
286+
line = replaceVariables(line[6:])
287+
print("[SCRIPT]: " + line)
194288
elif(line[0:6] == "IMPORT"):
195289
runScript(line[7:])
196290
elif(line[0:13] == "DEFAULT_DELAY"):
@@ -250,26 +344,30 @@ def parseLine(line, script_lines):
250344
defineValue = line[valueLocation+1:]
251345
defines[defineName] = defineValue
252346
elif line.startswith("FUNCTION"):
347+
# print("ENTER FUNCTION")
253348
func_name = line.split()[1]
254349
functions[func_name] = []
255350
line = next(script_lines).strip()
256351
while line != "END_FUNCTION":
257352
functions[func_name].append(line)
258353
line = next(script_lines).strip()
259354
elif line.startswith("WHILE"):
260-
condition = re.search(r'\((.*?)\)', line).group(1)
261-
var_name, _, condition_value = condition.split()
262-
condition_value = int(condition_value)
263-
loop_code = []
264-
line = next(script_lines).strip()
265-
while line != "END_WHILE":
266-
if not (line.startswith("WHILE")):
267-
loop_code.append(line)
268-
line = next(script_lines).strip()
269-
while variables[var_name] > condition_value:
270-
for loop_line in loop_code:
271-
parseLine(loop_line, {})
272-
variables[var_name] -= 1
355+
# print("ENTER WHILE LOOP")
356+
condition = line[5:].strip()
357+
loopCode = list(_getCodeBlock(script_lines))
358+
while evaluateExpression(condition) == True:
359+
currentIterCode = deepcopy(loopCode)
360+
print(loopCode)
361+
while currentIterCode:
362+
loopLine = currentIterCode.pop(0)
363+
currentIterCode = list(parseLine(loopLine, iter(currentIterCode))) #< very inefficient, should be replaced later.
364+
365+
elif line.upper().startswith("IF"):
366+
# print("ENTER IF")
367+
script_lines, ret = IF(_getIfCondition(line), script_lines).runIf()
368+
print(f"IF returned {ret} code")
369+
elif line.upper().startswith("END_IF"):
370+
pass
273371
elif line == "RANDOM_LOWERCASE_LETTER":
274372
sendString(random.choice(letters))
275373
elif line == "RANDOM_UPPERCASE_LETTER":
@@ -311,6 +409,8 @@ def parseLine(line, script_lines):
311409
parseLine(func_line, iter(functions[line]))
312410
else:
313411
runScriptLine(line)
412+
413+
return(script_lines)
314414

315415
kbd = Keyboard(usb_hid.devices)
316416
consumerControl = ConsumerControl(usb_hid.devices)

0 commit comments

Comments
 (0)