Anul 3 Semestrul 1
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
from ParserOutput import ParserOutput, TreeNode
|
||||
|
||||
class RecursiveDescentParser:
|
||||
def __init__(self, grammar, input_string):
|
||||
self.grammar = grammar
|
||||
self.input = input_string.split()
|
||||
self.current_pos = 0
|
||||
self.stack = [] # Stack for backtracking
|
||||
self.parsed_stack = [] # Current sequence of symbols being parsed
|
||||
self.production_sequence = [] # To track applied productions
|
||||
self.parse_tree = ParserOutput(self.grammar.terminals, self.grammar.nonterminals) # Parse tree output
|
||||
|
||||
def expand(self, nonterminal, prod_index=0):
|
||||
"""Expand a nonterminal using the specified production index."""
|
||||
if nonterminal in self.grammar.productions:
|
||||
production = self.grammar.productions[nonterminal][prod_index]
|
||||
self.stack.append((nonterminal, prod_index, self.current_pos, len(self.parsed_stack))) # Push state
|
||||
self.production_sequence.append((nonterminal, production)) # Track production
|
||||
self.parsed_stack = production.split() + self.parsed_stack # Add production to parsed stack
|
||||
else:
|
||||
raise ValueError(f"No production for {nonterminal}")
|
||||
|
||||
def advance(self):
|
||||
if self.current_pos < len(self.input) and self.parsed_stack:
|
||||
next_symbol = self.parsed_stack.pop(0)
|
||||
if next_symbol == self.input[self.current_pos]:
|
||||
self.current_pos += 1
|
||||
return True
|
||||
else:
|
||||
self.parsed_stack.insert(0, next_symbol) # Push it back
|
||||
return False
|
||||
return False
|
||||
|
||||
def back(self):
|
||||
if self.stack:
|
||||
# Pop the last saved state
|
||||
nonterminal, prod_index, prev_pos, stack_size = self.stack.pop()
|
||||
|
||||
# Restore the previous position in the input
|
||||
self.current_pos = prev_pos
|
||||
|
||||
# Restore the stack to the state it was before expanding the production
|
||||
self.parsed_stack = self.parsed_stack[:stack_size]
|
||||
self.production_sequence.pop()
|
||||
|
||||
# Try the next production for the same nonterminal
|
||||
return self.another_try(nonterminal, prod_index)
|
||||
|
||||
# If the stack is empty, backtracking is not possible
|
||||
return False
|
||||
|
||||
def another_try(self, nonterminal, prod_index):
|
||||
"""Try the next production of the nonterminal."""
|
||||
prod_list = self.grammar.productions.get(nonterminal, [])
|
||||
if prod_index + 1 < len(prod_list):
|
||||
self.expand(nonterminal, prod_index + 1) # Expand using the next production
|
||||
return True
|
||||
return False
|
||||
|
||||
def success(self):
|
||||
"""Check if parsing succeeded."""
|
||||
return self.current_pos == len(self.input) and not self.parsed_stack
|
||||
|
||||
def parse(self):
|
||||
if not self.grammar.start_symbol:
|
||||
raise ValueError("Grammar must have a start symbol defined.")
|
||||
|
||||
self.expand(self.grammar.start_symbol)
|
||||
|
||||
while True:
|
||||
# If parsing succeeds
|
||||
if self.success():
|
||||
self.parse_tree.build_tree(self.production_sequence)
|
||||
return True
|
||||
# If there's something to parse
|
||||
if self.parsed_stack:
|
||||
next_symbol = self.parsed_stack[0]
|
||||
|
||||
# If next symbol is a terminal, try to match it
|
||||
if next_symbol in self.grammar.terminals:
|
||||
if not self.advance():
|
||||
# If matching fails, backtrack
|
||||
if not self.back() and len(self.stack) <= 1:
|
||||
return False
|
||||
elif next_symbol in self.grammar.nonterminals:
|
||||
# Expand nonterminal
|
||||
self.parsed_stack.pop(0)
|
||||
self.expand(next_symbol)
|
||||
else:
|
||||
# Handle unexpected symbols
|
||||
print(f"Error: Unexpected symbol {next_symbol}")
|
||||
return False
|
||||
else:
|
||||
if not self.back() and len(self.stack) <= 1:
|
||||
return False
|
||||
|
||||
def print_parse_tree(self):
|
||||
"""Prints the parse tree to the screen."""
|
||||
self.parse_tree.print_to_screen()
|
||||
|
||||
def save_parse_tree(self, filename):
|
||||
"""Saves the parse tree representation to a file."""
|
||||
self.parse_tree.save_to_file(filename)
|
||||
Reference in New Issue
Block a user