mirror of
https://github.com/dbisu/pico-ducky.git
synced 2025-12-06 02:41:45 +00:00
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
This commit is contained in:
134
duckyinpython.py
134
duckyinpython.py
@@ -3,7 +3,6 @@
|
|||||||
# Author: Dave Bailey (dbisu, @daveisu)
|
# Author: Dave Bailey (dbisu, @daveisu)
|
||||||
#
|
#
|
||||||
# TODO: ADD support for the following:
|
# TODO: ADD support for the following:
|
||||||
# IF THEN ELSE
|
|
||||||
# Add jitter
|
# Add jitter
|
||||||
# Add LED functionality
|
# Add LED functionality
|
||||||
|
|
||||||
@@ -71,6 +70,98 @@ letters = "abcdefghijklmnopqrstuvwxyz"
|
|||||||
numbers = "0123456789"
|
numbers = "0123456789"
|
||||||
specialChars = "!@#$%^&*()"
|
specialChars = "!@#$%^&*()"
|
||||||
|
|
||||||
|
class IF:
|
||||||
|
def __init__(self, condition, codeIter):
|
||||||
|
self.condition = condition
|
||||||
|
self.codeIter = list(codeIter)
|
||||||
|
self.lastIfResult = None
|
||||||
|
|
||||||
|
def _exitIf(self):
|
||||||
|
_depth = 0
|
||||||
|
for line in self.codeIter:
|
||||||
|
line = self.codeIter.pop(0)
|
||||||
|
line = line.strip()
|
||||||
|
if line.upper().startswith("END_IF"):
|
||||||
|
_depth -= 1
|
||||||
|
elif line.upper().startswith("IF"):
|
||||||
|
_depth += 1
|
||||||
|
if _depth < 0:
|
||||||
|
print("No else, exiting" + str(list(self.codeIter)))
|
||||||
|
break
|
||||||
|
return(self.codeIter)
|
||||||
|
|
||||||
|
def runIf(self):
|
||||||
|
if isinstance(self.condition, str):
|
||||||
|
self.lastIfResult = evaluateExpression(self.condition)
|
||||||
|
elif isinstance(self.condition, bool):
|
||||||
|
self.lastIfResult = self.condition
|
||||||
|
else:
|
||||||
|
raise ValueError("Invalid condition type")
|
||||||
|
|
||||||
|
# print(f"condition {self.condition} result is {self.lastIfResult} since \"$VAR\" is {variables["$VAR"]}, code is {self.codeIter}")
|
||||||
|
depth = 0
|
||||||
|
for line in self.codeIter:
|
||||||
|
line = self.codeIter.pop(0)
|
||||||
|
line = line.strip()
|
||||||
|
if line == "":
|
||||||
|
continue
|
||||||
|
# print(line)
|
||||||
|
|
||||||
|
if line.startswith("IF"):
|
||||||
|
depth += 1
|
||||||
|
elif line.startswith("END_IF"):
|
||||||
|
if depth == 0:
|
||||||
|
return(self.codeIter, -1)
|
||||||
|
depth -=1
|
||||||
|
|
||||||
|
elif line.startswith("ELSE") and depth == 0:
|
||||||
|
# print(f"ELSE LINE {line}, lastIfResult: {self.lastIfResult}")
|
||||||
|
if self.lastIfResult is False:
|
||||||
|
line = line[4:].strip() # Remove 'ELSE' and strip whitespace
|
||||||
|
if line.startswith("IF"):
|
||||||
|
nestedCondition = _getIfCondition(line)
|
||||||
|
# print(f"nested IF {nestedCondition}")
|
||||||
|
self.codeIter, self.lastIfResult = IF(nestedCondition, self.codeIter).runIf()
|
||||||
|
if self.lastIfResult == -1 or self.lastIfResult == True:
|
||||||
|
# print(f"self.lastIfResult {self.lastIfResult}")
|
||||||
|
return(self.codeIter, True)
|
||||||
|
else:
|
||||||
|
return IF(True, self.codeIter).runIf() #< Regular ELSE block
|
||||||
|
else:
|
||||||
|
self._exitIf()
|
||||||
|
break
|
||||||
|
|
||||||
|
# Process regular lines
|
||||||
|
elif self.lastIfResult:
|
||||||
|
# print(f"running line {line}")
|
||||||
|
self.codeIter = list(parseLine(line, self.codeIter))
|
||||||
|
# print("end of if")
|
||||||
|
return(self.codeIter, self.lastIfResult)
|
||||||
|
|
||||||
|
def _getIfCondition(line):
|
||||||
|
return str(line)[2:-4].strip()
|
||||||
|
|
||||||
|
def _isCodeBlock(line):
|
||||||
|
line = line.upper().strip()
|
||||||
|
if line.startswith("IF") or line.startswith("WHILE"):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _getCodeBlock(linesIter):
|
||||||
|
"""Returns the code block starting at the given line."""
|
||||||
|
code = []
|
||||||
|
depth = 1
|
||||||
|
for line in linesIter:
|
||||||
|
line = line.strip()
|
||||||
|
if line.upper().startswith("END_"):
|
||||||
|
depth -= 1
|
||||||
|
elif _isCodeBlock(line):
|
||||||
|
depth += 1
|
||||||
|
if depth <= 0:
|
||||||
|
break
|
||||||
|
code.append(line)
|
||||||
|
return code
|
||||||
|
|
||||||
def evaluateExpression(expression):
|
def evaluateExpression(expression):
|
||||||
"""Evaluates an expression with variables and returns the result."""
|
"""Evaluates an expression with variables and returns the result."""
|
||||||
# Replace variables (e.g., $FOO) in the expression with their values
|
# Replace variables (e.g., $FOO) in the expression with their values
|
||||||
@@ -82,6 +173,9 @@ def evaluateExpression(expression):
|
|||||||
|
|
||||||
return eval(expression, {}, variables)
|
return eval(expression, {}, variables)
|
||||||
|
|
||||||
|
def deepcopy(List):
|
||||||
|
return(List[:])
|
||||||
|
|
||||||
def convertLine(line):
|
def convertLine(line):
|
||||||
commands = []
|
commands = []
|
||||||
# print(line)
|
# print(line)
|
||||||
@@ -134,7 +228,6 @@ def replaceDefines(line):
|
|||||||
|
|
||||||
def parseLine(line, script_lines):
|
def parseLine(line, script_lines):
|
||||||
global defaultDelay, variables, functions, defines
|
global defaultDelay, variables, functions, defines
|
||||||
print(line)
|
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
line = line.replace("$_RANDOM_INT", str(random.randint(int(variables.get("$_RANDOM_MIN", 0)), int(variables.get("$_RANDOM_MAX", 65535)))))
|
line = line.replace("$_RANDOM_INT", str(random.randint(int(variables.get("$_RANDOM_MIN", 0)), int(variables.get("$_RANDOM_MAX", 65535)))))
|
||||||
line = replaceDefines(line)
|
line = replaceDefines(line)
|
||||||
@@ -143,7 +236,7 @@ def parseLine(line, script_lines):
|
|||||||
elif line.startswith("REM_BLOCK"):
|
elif line.startswith("REM_BLOCK"):
|
||||||
while line.startswith("END_REM") == False:
|
while line.startswith("END_REM") == False:
|
||||||
line = next(script_lines).strip()
|
line = next(script_lines).strip()
|
||||||
print(line)
|
# print(line)
|
||||||
elif(line[0:3] == "REM"):
|
elif(line[0:3] == "REM"):
|
||||||
pass
|
pass
|
||||||
elif line.startswith("HOLD"):
|
elif line.startswith("HOLD"):
|
||||||
@@ -190,7 +283,8 @@ def parseLine(line, script_lines):
|
|||||||
elif(line[0:6] == "STRING"):
|
elif(line[0:6] == "STRING"):
|
||||||
sendString(replaceVariables(line[7:]))
|
sendString(replaceVariables(line[7:]))
|
||||||
elif(line[0:5] == "PRINT"):
|
elif(line[0:5] == "PRINT"):
|
||||||
print("[SCRIPT]: " + line[6:])
|
line = replaceVariables(line[6:])
|
||||||
|
print("[SCRIPT]: " + line)
|
||||||
elif(line[0:6] == "IMPORT"):
|
elif(line[0:6] == "IMPORT"):
|
||||||
runScript(line[7:])
|
runScript(line[7:])
|
||||||
elif(line[0:13] == "DEFAULT_DELAY"):
|
elif(line[0:13] == "DEFAULT_DELAY"):
|
||||||
@@ -250,6 +344,7 @@ def parseLine(line, script_lines):
|
|||||||
defineValue = line[valueLocation+1:]
|
defineValue = line[valueLocation+1:]
|
||||||
defines[defineName] = defineValue
|
defines[defineName] = defineValue
|
||||||
elif line.startswith("FUNCTION"):
|
elif line.startswith("FUNCTION"):
|
||||||
|
# print("ENTER FUNCTION")
|
||||||
func_name = line.split()[1]
|
func_name = line.split()[1]
|
||||||
functions[func_name] = []
|
functions[func_name] = []
|
||||||
line = next(script_lines).strip()
|
line = next(script_lines).strip()
|
||||||
@@ -257,19 +352,22 @@ def parseLine(line, script_lines):
|
|||||||
functions[func_name].append(line)
|
functions[func_name].append(line)
|
||||||
line = next(script_lines).strip()
|
line = next(script_lines).strip()
|
||||||
elif line.startswith("WHILE"):
|
elif line.startswith("WHILE"):
|
||||||
condition = re.search(r'\((.*?)\)', line).group(1)
|
# print("ENTER WHILE LOOP")
|
||||||
var_name, _, condition_value = condition.split()
|
condition = line[5:].strip()
|
||||||
condition_value = int(condition_value)
|
loopCode = list(_getCodeBlock(script_lines))
|
||||||
loop_code = []
|
while evaluateExpression(condition) == True:
|
||||||
line = next(script_lines).strip()
|
currentIterCode = deepcopy(loopCode)
|
||||||
while line != "END_WHILE":
|
print(loopCode)
|
||||||
if not (line.startswith("WHILE")):
|
while currentIterCode:
|
||||||
loop_code.append(line)
|
loopLine = currentIterCode.pop(0)
|
||||||
line = next(script_lines).strip()
|
currentIterCode = list(parseLine(loopLine, iter(currentIterCode))) #< very inefficient, should be replaced later.
|
||||||
while variables[var_name] > condition_value:
|
|
||||||
for loop_line in loop_code:
|
elif line.upper().startswith("IF"):
|
||||||
parseLine(loop_line, {})
|
# print("ENTER IF")
|
||||||
variables[var_name] -= 1
|
script_lines, ret = IF(_getIfCondition(line), script_lines).runIf()
|
||||||
|
print(f"IF returned {ret} code")
|
||||||
|
elif line.upper().startswith("END_IF"):
|
||||||
|
pass
|
||||||
elif line == "RANDOM_LOWERCASE_LETTER":
|
elif line == "RANDOM_LOWERCASE_LETTER":
|
||||||
sendString(random.choice(letters))
|
sendString(random.choice(letters))
|
||||||
elif line == "RANDOM_UPPERCASE_LETTER":
|
elif line == "RANDOM_UPPERCASE_LETTER":
|
||||||
@@ -311,6 +409,8 @@ def parseLine(line, script_lines):
|
|||||||
parseLine(func_line, iter(functions[line]))
|
parseLine(func_line, iter(functions[line]))
|
||||||
else:
|
else:
|
||||||
runScriptLine(line)
|
runScriptLine(line)
|
||||||
|
|
||||||
|
return(script_lines)
|
||||||
|
|
||||||
kbd = Keyboard(usb_hid.devices)
|
kbd = Keyboard(usb_hid.devices)
|
||||||
consumerControl = ConsumerControl(usb_hid.devices)
|
consumerControl = ConsumerControl(usb_hid.devices)
|
||||||
|
|||||||
Reference in New Issue
Block a user