From c8ad44e93ed9a2b01110de44947817ed0f19719c Mon Sep 17 00:00:00 2001 From: BodgeMaster <> Date: Sat, 5 Nov 2022 05:36:57 +0100 Subject: [PATCH] lambdaV: Implement parser At this point, I have no idea how well it actually works as I have only performed basic sanity checks on it. But it seems to work. --- lambdaV.py | 358 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 lambdaV.py diff --git a/lambdaV.py b/lambdaV.py new file mode 100644 index 0000000..3728f95 --- /dev/null +++ b/lambdaV.py @@ -0,0 +1,358 @@ +#!/usr/bin/env python3 + +# Copyright 2022, Jan Danielzick (aka. BodgeMaster) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published +# by the Free Software Foundation, version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# version 3 along with this program. +# If not, see https://www.gnu.org/licenses/gpl-3.0.en.html + +import re + +debug_mode = True + +cursor_up = 'Λ' +cursor_down = 'V' +cursor_left = '<' +cursor_right = '>' + +empty_field = [ + " ________________ ", + "| |", + "| |", + "| |", + "| |", + "| |", + "| |", + "| |", + "| |", + " ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ " +] + +wall = '#' +apple = 'ó' +goal = '$' + +cursor_position = [0, 0] + + +def debug_message(text): + if debug_mode: + print("DEBUG: ", end="") + print(text) + +def condition_in_front_of_wall(inverted): + return False + +def condition_goal_reached(inverted): + return False + +def condition_on_apple(inverted): + return False + +def condition_facing_north(inverted): + return False + +def condition_facing_south(inverted): + return False + +def condition_facing_east(inverted): + return False + +def condition_facing_west(inverted): + return False + +# condition prefixed with ! +def modifier_not(condition): + return condition(true) + +# return value: "" or error message +def command_step(): + #TODO + return "" + +def command_left(): + return "" + +def command_right(): + return "" + +def command_repeat(number, parsed_code): + return "" + +def command_while(parsed_condition, parsed_code): + return "" + +def command_take(): + return "" + +def command_if(parsed_condition, parsed_then_code, parsed_else_code): + return "" + + +# returns -1 for generic error +# returns -2 if end can’t be found +def get_length_of_condition(code): + if len(code)==0: + return -1 + if code[0] == '<': + length = 0 + while length0: + if code[length] == '(': + nesting_level = nesting_level+1 + elif code[length] == ')': + nesting_level = nesting_level-1 + length = length+1 + if length==len(code) and nesting_level > 0: + return -2 + return length + else: + return -1 + +# clean up whitespace +def format_code(code): + regex_pattern = re.compile(r'\s+') + code = " ".join(re.sub(regex_pattern, ' ', code).lower().split()) + code = code.replace(" (", "(") + code = code.replace("( ", "(") + code = code.replace(" )", ")") + code = code.replace(") ", ")") + code = code.replace(" <", "<") + code = code.replace("< ", "<") + code = code.replace(" >", ">") + code = code.replace("< ", "<") + code = code.replace("! ", "!") + # add spaces back after closing parenthesis + code = code.replace(")", ") ") + code = code.replace(") )", "))") + code = code.replace(") )", "))") + if len(code)>0 and code[-1]==' ': + return code[:-1] + return code + +# returns [function, inverted?, error message] +def parse_condition(condition_code, allowed_conditions): + inverted = False + condition = None + if len(condition_code)==0: + debug_message(" No condition specified") + return [condition, inverted, "Syntax error: No condition specified"] + if condition_code[0]=='!': + debug_message(" Condition is inverted") + inverted = True + condition_code = condition_code[1:] + if len(condition_code)==0: + debug_message(" No condition specified") + return [condition, inverted, "Syntax error: No condition specified"] + + if condition_code == "in front of wall": + condition = condition_in_front_of_wall + elif condition_code == "goal reached": + condition = condition_goal_reached + elif condition_code == "on apple": + condition = condition_on_apple + elif condition_code == "facing north": + condition = condition_facing_north + elif condition_code == "facing south": + condition = condition_facing_south + elif condition_code == "facing east": + condition = condition_facing_east + elif condition_code == "facing west": + condition = condition_facing_west + else: + debug_message(" Unknown condition: "+condition_code) + return [condition, inverted, "Unknown condition: "+condition_code] + return [condition, inverted, ""] + +# returns [[functions], [arguments], -1, ""] or [[], [], error_position, "error message"] +def parse_code(code, allowed_commands, allowed_conditions, unformatted_code=True): + if unformatted_code: + code = format_code(code) + debug_message("Formatted code: "+code) + + parse_position = 0 + parsed_code = [[], [], -1, ""] + while parse_position < len(code): + # find next command + next_space = code.find(' ', parse_position) + next_condition = code.find('<', parse_position) + if next_space == -1: + next_space = len(code) + if next_condition == -1: + next_condition = len(code) + # Are we working with a simple command? + if next_condition>next_space or next_condition==next_space: + next_command = code[parse_position:next_space] + debug_message("Next command is: "+next_command) + + parsed_code[1].append(()) + #TODO: add function to to the function list + if next_command == "step": + parsed_code[0].append(command_step) + elif next_command == "left": + parsed_code[0].append(command_left) + elif next_command == "right": + parsed_code[0].append(command_right) + elif next_command == "take": + parsed_code[0].append(command_take) + elif next_command == "repeat": + return [[], [], next_space, "Syntax error: Condition missing"] + elif next_command == "while": + return [[], [], next_space, "Syntax error: Condition missing"] + elif next_command == "if": + return [[], [], next_space, "Syntax error: Condition missing"] + elif next_command == "else": + return [[], [], parse_position, "Syntax error: Else without if statement"] + else: + #TODO: better error message (especially when special chars are detected inside next_command or a known command) + return [[], [], parse_position, "Unknown command: "+next_command] + parse_position = next_space+1 + else: + debug_message("Found control structure...") + control_structure = code[parse_position:next_condition] + if control_structure == "repeat": + debug_message(" Type: repeat loop") + parse_position = next_condition + repetitions_length = get_length_of_condition(code[parse_position:]) + if repetitions_length == -2: + debug_message(" Mismatched <>") + return [[], [], parse_position, "Syntax error: Cannot not find matching '>' for this '<'"] + repetitions = code[parse_position:parse_position+repetitions_length] + debug_message(" Number of repetitions: "+repetitions) + repetitions_int = 0 + try: + repetitions_int = int(repetitions[1:-1]) + except ValueError: + return [[], [], parse_position, "Not a valid number: "+repetitions[1:-1]] + parse_position = parse_position+len(repetitions) + contained_code_length = get_length_of_contained_code(code[parse_position:]) + if contained_code_length == -1: + debug_message(" Missing (") + return [[], [], parse_position, "Syntax error: Cannot not find '(' for contained code"] + if contained_code_length == -2: + debug_message(" Mismatched ()") + return [[], [], parse_position, "Syntax error: Cannot not find matching ')' for this '('"] + contained_code = code[parse_position:parse_position+contained_code_length] + debug_message(" Contained code: "+contained_code) + parsed_contained_code = parse_code(contained_code[1:-1], allowed_commands, allowed_conditions, unformatted_code=False) + if not parsed_contained_code[2]==-1: + debug_message(" Error while parsing contained code") + return [[], [], parse_position+1+parsed_contained_code[2], parsed_contained_code[3]] + parse_position = parse_position+contained_code_length+1 + + parsed_code[1].append((repetitions_int, parsed_contained_code)) + parsed_code[0].append(command_repeat) + continue + elif control_structure == "if": + debug_message(" Type: if statement") + parse_position = next_condition + condition_length = get_length_of_condition(code[parse_position:]) + if condition_length == -2: + debug_message(" Mismatched <>") + return [[], [], parse_position, "Syntax error: Cannot not find matching '>' for this '<'"] + condition = code[parse_position:parse_position+condition_length] + debug_message(" Condition is: "+condition) + parsed_condition = parse_condition(condition[1:-1], allowed_conditions) + if not parsed_condition[2]=="": + return [[], [], parse_position, parsed_condition[2]] + parse_position = parse_position+len(condition) + then_code_length = get_length_of_contained_code(code[parse_position:]) + if then_code_length == -1: + debug_message(" Missing (") + return [[], [], parse_position, "Syntax error: Cannot not find '(' for then-code"] + if then_code_length == -2: + debug_message(" Mismatched ()") + return [[], [], parse_position, "Syntax error: Cannot not find matching ')' for this '('"] + then_code = code[parse_position:parse_position+then_code_length] + debug_message(" Then-code: "+then_code) + parsed_then_code = parse_code(then_code[1:-1], allowed_commands, allowed_conditions, unformatted_code=False) + if not parsed_then_code[2]==-1: + debug_message(" Error while parsing then-code") + return [[], [], parse_position+1+parsed_then_code[2], parsed_then_code[3]] + parse_position = parse_position+then_code_length+1 + parsed_else_code = None + if parse_position") + return [[], [], parse_position, "Syntax error: Cannot not find matching '>' for this '<'"] + condition = code[parse_position:parse_position+condition_length] + debug_message(" Condition is: "+condition) + parsed_condition = parse_condition(condition[1:-1], allowed_conditions) + if not parsed_condition[2]=="": + return [[], [], parse_position, parsed_condition[2]] + parse_position = parse_position+len(condition) + contained_code_length = get_length_of_contained_code(code[parse_position:]) + if contained_code_length == -1: + debug_message(" Missing (") + return [[], [], parse_position, "Syntax error: Cannot not find '(' for contained code"] + if contained_code_length == -2: + debug_message(" Mismatched ()") + return [[], [], parse_position, "Syntax error: Cannot not find matching ')' for this '('"] + contained_code = code[parse_position:parse_position+contained_code_length] + debug_message(" Contained code: "+contained_code) + parsed_contained_code = parse_code(contained_code[1:-1], allowed_commands, allowed_conditions, unformatted_code=False) + if not parsed_contained_code[2]==-1: + debug_message(" Error while parsing contained code") + return [[], [], parse_position+1+parsed_contained_code[2], parsed_contained_code[3]] + parse_position = parse_position+contained_code_length+1 + + parsed_code[1].append((parsed_condition, parsed_contained_code)) + parsed_code[0].append(command_while) + else: + debug_message(" Type: unknown control structure: "+control_structure) + #TODO: better error message (especially when special chars are detected inside control_structure or a known command) + return [[], [], parse_position, "Syntax error: Unknown control structure: "+control_structure] + return parsed_code + +# returns success or error message +# Parsed code is the result of running the parser. +def run_code(parsed_code): + return "" + +if __name__ == "__main__": + pass