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:
Tzur Soffer
2025-01-26 15:26:45 -08:00
committed by GitHub
parent e61d232b46
commit 522e640c26

View File

@@ -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)