Loading lib/parser/__init__.py +66 −34 Original line number Original line Diff line number Diff line from typing import List, Dict, Tuple, Optional, Union, Set, TypeVar from typing import List, Tuple, Union, Set from copy import deepcopy from copy import deepcopy from lib.common import State, Character, Eps, Terminal, Nonterminal, Emptyset from lib.common import State, Character, Eps, Terminal, Nonterminal, Emptyset from lib.reg import DFA, NFA, RegGrammar from lib.reg import DFA, NFA, RegGrammar Loading @@ -19,20 +19,23 @@ from lib.parser.CFGLexer import CFGLexer from lib.parser.CFGParser import CFGParser from lib.parser.CFGParser import CFGParser from lib.parser.CFGListener import CFGListener from lib.parser.CFGListener import CFGListener class ParsingError(Exception): class ParsingError(Exception): def __init__(self, args): def __init__(self, args): self.args = args self.args = args # This is needed because antlr is too smart and parse at least something possible # This is needed because antlr is too smart and parse at least something # even when input formalism and given type don't match. This way it aborts on any parsing problem. # possible even when input formalism and given type don't match. This way it # aborts on any parsing problem. class ErrorShouter(ErrorListener): class ErrorShouter(ErrorListener): def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e): def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e): raise Exception("ERROR: when parsing line %d column %d: %s\n" % \ raise Exception( (line, column, msg)) f"ERROR: when parsing line {line} column {column}: {msg}") def _anyvalue_attributes(parser: Union[DFAParser, NFAParser, RegExParser, CFGParser]) -> List: def _anyvalue_attributes( parser: Union[DFAParser, NFAParser, RegExParser, CFGParser]) -> List: return [func for func in dir(parser.AnyvalueContext) return [func for func in dir(parser.AnyvalueContext) if callable(getattr(parser.AnyvalueContext, func)) if callable(getattr(parser.AnyvalueContext, func)) and not func.startswith("__") and func.isupper()] and not func.startswith("__") and func.isupper()] Loading @@ -49,7 +52,9 @@ def _rules_to_str(rules: Union[CFG.Rules, RegGrammar.Rules], for nonterminal in nonterminals: for nonterminal in nonterminals: if nonterminal not in rules: if nonterminal not in rules: continue continue rewritten = ' | '.join(set(map(lambda x: _rewrite_variant(x), rules[nonterminal]))) rewritten = ' | '.join(set(map(lambda x: _rewrite_variant(x), rules[nonterminal]))) out += f"{nonterminal.name} -> {rewritten}\n" out += f"{nonterminal.name} -> {rewritten}\n" return out[:-1] return out[:-1] Loading @@ -60,6 +65,7 @@ def _rewrite_variant(variant: Union[Eps, Terminal, return ''.join(map(lambda x: x.name, variant)) return ''.join(map(lambda x: x.name, variant)) return variant.name return variant.name def dfa_to_str(dfa: DFA, full: bool = False) -> str: def dfa_to_str(dfa: DFA, full: bool = False) -> str: transition = "" transition = "" for key, dest_state in dfa.transition.items(): for key, dest_state in dfa.transition.items(): Loading @@ -71,7 +77,8 @@ def dfa_to_str(dfa: DFA, full : bool = False) -> str: # full - verbose description of DFA - only for development, dismiss later # full - verbose description of DFA - only for development, dismiss later if full: if full: return f"DFA = ({_names_to_str(dfa.states)}, {_names_to_str(dfa.characters)}, " \ return f"DFA = ({_names_to_str(dfa.states)}, " \ f"{_names_to_str(dfa.characters)}, " \ f"d, {init}, {final})\n{transition}" f"d, {init}, {final})\n{transition}" return f"{init} {transition} {final}" return f"{init} {transition} {final}" Loading @@ -86,7 +93,9 @@ def reggrammar_to_str(reg: RegGrammar, full: bool = False) -> str: # full - verbose description of DFA - only for development, dismiss later # full - verbose description of DFA - only for development, dismiss later nonterminals_names = _names_to_str(reg.nonterminals) nonterminals_names = _names_to_str(reg.nonterminals) terminals = _names_to_str(reg.terminals) terminals = _names_to_str(reg.terminals) return f"Grammar: ({nonterminals_names}, {terminals}, P, {reg.init.name})\n{_rules_to_str(reg.rules, nonterminals)}" return f"Grammar: ({nonterminals_names}, {terminals}, P, " \ f"{reg.init.name})\n{_rules_to_str(reg.rules, nonterminals)}" def cfg_to_str(gra: CFG, full: bool = False) -> str: def cfg_to_str(gra: CFG, full: bool = False) -> str: nonterminals = deepcopy(gra.nonterminals).difference({gra.init}) nonterminals = deepcopy(gra.nonterminals).difference({gra.init}) Loading @@ -98,26 +107,32 @@ def cfg_to_str(gra: CFG, full: bool = False) -> str: # full - verbose description of DFA - only for development, dismiss later # full - verbose description of DFA - only for development, dismiss later nonterminals_names = _names_to_str(gra.nonterminals) nonterminals_names = _names_to_str(gra.nonterminals) terminals = _names_to_str(gra.terminals) terminals = _names_to_str(gra.terminals) return f"Grammar: ({nonterminals_names}, {terminals}, P, {gra.init.name})\n{_rules_to_str(gra.rules, nonterminals)}" return f"Grammar: ({nonterminals_names}, {terminals}, P, " \ f"{gra.init.name})\n{_rules_to_str(gra.rules, nonterminals)}" def nfa_to_str(nfa: NFA, full: bool = False) -> str: def nfa_to_str(nfa: NFA, full: bool = False) -> str: transition = "" transition = "" for key, set_states in nfa.transition.items(): for key, set_states in nfa.transition.items(): state, character = key state, character = key dest_states = nfa.transition[state, character] dest_states = nfa.transition[state, character] transition += f"({state.name},{character.name})={_names_to_str(dest_states)} " transition += f"({state.name},{character.name})=" \ f"{_names_to_str(dest_states)} " init = f"init={nfa.init.name}" init = f"init={nfa.init.name}" final = f"final={_names_to_str(nfa.final)}" final = f"final={_names_to_str(nfa.final)}" if full: if full: return f"NFA = ({_names_to_str(nfa.states)}, {_names_to_str(nfa.characters)}, " \ return f"NFA = ({_names_to_str(nfa.states)}, " \ f"{_names_to_str(nfa.characters)}, " \ f"d, {init}, {final})\n{transition}" f"d, {init}, {final})\n{transition}" return f"{init} {transition} {final}" return f"{init} {transition} {final}" def regex_to_str(reg: RegEx) -> str: def regex_to_str(reg: RegEx) -> str: return reg.expression.astprint() return reg.expression.astprint() def _common_parse(string: str, given_lexer, given_parser, given_builder): def _common_parse(string: str, given_lexer, given_parser, given_builder): error_listener = ErrorShouter() error_listener = ErrorShouter() chars = antlr4.InputStream(string) chars = antlr4.InputStream(string) Loading @@ -137,7 +152,8 @@ def _common_parse(string: str, given_lexer, given_parser, given_builder): def cfg(string: str) -> CFG: def cfg(string: str) -> CFG: try: try: builder = _common_parse(string, CFGLexer, CFGParser, CFGBuilder) builder = _common_parse(string, CFGLexer, CFGParser, CFGBuilder) return CFG(builder.nonterminals, builder.terminals, builder.rules, builder.init) return CFG(builder.nonterminals, builder.terminals, builder.rules, builder.init) except Exception as e: except Exception as e: raise ParsingError(e.args) raise ParsingError(e.args) Loading @@ -159,9 +175,11 @@ def dfa(string: str) -> DFA: if builder.init is None: if builder.init is None: builder.init = builder.first_state builder.init = builder.first_state if builder.init is None: if builder.init is None: raise ParsingError("Automat musí obsahovat alespoň jeden stav.") raise ParsingError( "Automat musí obsahovat alespoň jeden stav.") dfa = DFA(builder.states, builder.characters, builder.transition, builder.init, builder.final) dfa = DFA(builder.states, builder.characters, builder.transition, builder.init, builder.final) return dfa return dfa except Exception as e: except Exception as e: Loading @@ -175,9 +193,11 @@ def nfa(string: str) -> NFA: if builder.init is None: if builder.init is None: builder.init = builder.first_state builder.init = builder.first_state if builder.init is None: if builder.init is None: raise ParsingError("Automat musí obsahovat alespoň jeden stav.") raise ParsingError( "Automat musí obsahovat alespoň jeden stav.") return NFA(builder.states, builder.characters, builder.transition, builder.init, builder.final) return NFA(builder.states, builder.characters, builder.transition, builder.init, builder.final) except Exception as e: except Exception as e: raise ParsingError(e.args) raise ParsingError(e.args) Loading Loading @@ -252,7 +272,8 @@ class DFABuilder(DFAListener, StateVisitor): self.characters.add(character) self.characters.add(character) if (state, character) in self.transition: if (state, character) in self.transition: print(f"Upozornění: v textovém zápisu se objevilo více přechodů pro stejnou dvojici ({state.name}, {character.name}).") print("Upozornění: v textovém zápisu se objevilo více přechodů " f"pro stejnou dvojici ({state.name}, {character.name}).") self.transition[state, character] = dest_state self.transition[state, character] = dest_state if self.first_state is None: if self.first_state is None: Loading Loading @@ -295,21 +316,25 @@ class NFABuilder(NFAListener, StateVisitor): dest_states = set() dest_states = set() i = 0 i = 0 while ctx.stateset().statename(i) is not None: while ctx.stateset().statename(i) is not None: dest_state = State(self.visitStatename(ctx.stateset().statename(i))) dest_state = State( self.visitStatename(ctx.stateset().statename(i))) self.states.add(dest_state) self.states.add(dest_state) dest_states.add(dest_state) dest_states.add(dest_state) i += 1 i += 1 if ctx.EPSILON(): if ctx.EPSILON(): if (state, Eps()) in self.transition: if (state, Eps()) in self.transition: print(f"Upozornění: v textovém zápisu se objevilo více přechodů pro stejnou dvojici ({state.name}, ε).") print("Upozornění: v textovém zápisu se objevilo více " f"přechodů pro stejnou dvojici ({state.name}, ε).") self.transition[state, Eps()] = dest_states self.transition[state, Eps()] = dest_states self.efa = True self.efa = True else: else: character = Character(self.visitStatename(ctx.statename(1))) character = Character(self.visitStatename(ctx.statename(1))) self.characters.add(character) self.characters.add(character) if (state, character) in self.transition: if (state, character) in self.transition: print(f"Upozornění: v textovém zápisu se objevilo více přechodů pro stejnou dvojici ({state.name}, {character.name}).") print("Upozornění: v textovém zápisu se objevilo více " f"přechodů pro stejnou dvojici ({state.name}, " f"{character.name}).") self.transition[state, character] = dest_states self.transition[state, character] = dest_states if self.first_state is None: if self.first_state is None: Loading Loading @@ -342,10 +367,13 @@ class RegExBuilder(RegExVisitor): # Binary operation: union or explicit concatenation # Binary operation: union or explicit concatenation if ctx.UNION() or ctx.CONCAT(): if ctx.UNION() or ctx.CONCAT(): op = Bin.Union if ctx.UNION() is not None else Bin.Concat op = Bin.Union if ctx.UNION() is not None else Bin.Concat return BinOp(self.visitExpr(ctx.expr(0)), op, self.visitExpr(ctx.expr(1))) return BinOp(self.visitExpr(ctx.expr(0)), op, self.visitExpr(ctx.expr(1))) # Implicit concatenation of (iterated) symbols or expressions in parentheses # Implicit concatenation of (iterated) symbols or expressions in expressions = list(map(lambda x: self.visitConcatenable(x), ctx.concatenated())) # parentheses expressions = list(map(lambda x: self.visitConcatenable(x), ctx.concatenated())) return self.implicit_concat(expressions) return self.implicit_concat(expressions) def visitConcatenable(self, ctx): def visitConcatenable(self, ctx): Loading Loading @@ -396,7 +424,8 @@ class RegExBuilder(RegExVisitor): elif ctx.parentheses(): elif ctx.parentheses(): expression = self.visitParentheses(ctx.parentheses()) expression = self.visitParentheses(ctx.parentheses()) return IterOp(expression, Iter.Positive) if positive else IterOp(expression, Iter.Iteration) return IterOp(expression, Iter.Positive) if positive \ else IterOp(expression, Iter.Iteration) def implicit_concat(self, to_concat): def implicit_concat(self, to_concat): ast = to_concat[0] ast = to_concat[0] Loading @@ -405,11 +434,11 @@ class RegExBuilder(RegExVisitor): ast = BinOp(ast, Bin.Concat, expression) ast = BinOp(ast, Bin.Concat, expression) return ast return ast # for future support of comments # for future support of comments def exitComment(self, ctx) -> None: def exitComment(self, ctx) -> None: return None return None class CFGBuilder(CFGListener): class CFGBuilder(CFGListener): anyvalue_attributes = _anyvalue_attributes(CFGParser) anyvalue_attributes = _anyvalue_attributes(CFGParser) Loading @@ -431,11 +460,13 @@ class CFGBuilder(CFGListener): if ctx.CAPS(): if ctx.CAPS(): name = str(ctx.CAPS()) name = str(ctx.CAPS()) elif ctx.LEFT_ANGLE(): elif ctx.LEFT_ANGLE(): name = '<' + ''.join(map(lambda x: self.visitSymbol(x), ctx.symbol())) + '>' name = '<' + ''.join(map(lambda x: self.visitSymbol(x), ctx.symbol())) + '>' if ctx.APOSTROPHE(): if ctx.APOSTROPHE(): name = name + len(ctx.APOSTROPHE()) * "'" name = name + len(ctx.APOSTROPHE()) * "'" elif ctx.APOSTROPHE(): elif ctx.APOSTROPHE(): name = self.visitSymbol(ctx.symbol(0)) + len(ctx.APOSTROPHE())*"'" name = self.visitSymbol(ctx.symbol(0)) \ + len(ctx.APOSTROPHE()) * "'" nonterminal = Nonterminal(name) nonterminal = Nonterminal(name) self.nonterminals.add(nonterminal) self.nonterminals.add(nonterminal) Loading Loading @@ -465,7 +496,8 @@ class CFGBuilder(CFGListener): sequence.append(terminal) sequence.append(terminal) else: else: sequence.append(self.visitNonterminal(ctx.term_or_nonterm(i).nonterminal())) sequence.append(self.visitNonterminal( ctx.term_or_nonterm(i).nonterminal())) i += 1 i += 1 return sequence return sequence Loading @@ -473,7 +505,7 @@ class CFGBuilder(CFGListener): def exitOnerule(self, ctx): def exitOnerule(self, ctx): nonterminal = self.visitNonterminal(ctx.nonterminal()) nonterminal = self.visitNonterminal(ctx.nonterminal()) self.nonterminals.add(nonterminal) self.nonterminals.add(nonterminal) if self.init == None: if self.init is None: self.init = nonterminal self.init = nonterminal # multiple lines for one nonterminal are possible this way # multiple lines for one nonterminal are possible this way Loading Loading
lib/parser/__init__.py +66 −34 Original line number Original line Diff line number Diff line from typing import List, Dict, Tuple, Optional, Union, Set, TypeVar from typing import List, Tuple, Union, Set from copy import deepcopy from copy import deepcopy from lib.common import State, Character, Eps, Terminal, Nonterminal, Emptyset from lib.common import State, Character, Eps, Terminal, Nonterminal, Emptyset from lib.reg import DFA, NFA, RegGrammar from lib.reg import DFA, NFA, RegGrammar Loading @@ -19,20 +19,23 @@ from lib.parser.CFGLexer import CFGLexer from lib.parser.CFGParser import CFGParser from lib.parser.CFGParser import CFGParser from lib.parser.CFGListener import CFGListener from lib.parser.CFGListener import CFGListener class ParsingError(Exception): class ParsingError(Exception): def __init__(self, args): def __init__(self, args): self.args = args self.args = args # This is needed because antlr is too smart and parse at least something possible # This is needed because antlr is too smart and parse at least something # even when input formalism and given type don't match. This way it aborts on any parsing problem. # possible even when input formalism and given type don't match. This way it # aborts on any parsing problem. class ErrorShouter(ErrorListener): class ErrorShouter(ErrorListener): def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e): def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e): raise Exception("ERROR: when parsing line %d column %d: %s\n" % \ raise Exception( (line, column, msg)) f"ERROR: when parsing line {line} column {column}: {msg}") def _anyvalue_attributes(parser: Union[DFAParser, NFAParser, RegExParser, CFGParser]) -> List: def _anyvalue_attributes( parser: Union[DFAParser, NFAParser, RegExParser, CFGParser]) -> List: return [func for func in dir(parser.AnyvalueContext) return [func for func in dir(parser.AnyvalueContext) if callable(getattr(parser.AnyvalueContext, func)) if callable(getattr(parser.AnyvalueContext, func)) and not func.startswith("__") and func.isupper()] and not func.startswith("__") and func.isupper()] Loading @@ -49,7 +52,9 @@ def _rules_to_str(rules: Union[CFG.Rules, RegGrammar.Rules], for nonterminal in nonterminals: for nonterminal in nonterminals: if nonterminal not in rules: if nonterminal not in rules: continue continue rewritten = ' | '.join(set(map(lambda x: _rewrite_variant(x), rules[nonterminal]))) rewritten = ' | '.join(set(map(lambda x: _rewrite_variant(x), rules[nonterminal]))) out += f"{nonterminal.name} -> {rewritten}\n" out += f"{nonterminal.name} -> {rewritten}\n" return out[:-1] return out[:-1] Loading @@ -60,6 +65,7 @@ def _rewrite_variant(variant: Union[Eps, Terminal, return ''.join(map(lambda x: x.name, variant)) return ''.join(map(lambda x: x.name, variant)) return variant.name return variant.name def dfa_to_str(dfa: DFA, full: bool = False) -> str: def dfa_to_str(dfa: DFA, full: bool = False) -> str: transition = "" transition = "" for key, dest_state in dfa.transition.items(): for key, dest_state in dfa.transition.items(): Loading @@ -71,7 +77,8 @@ def dfa_to_str(dfa: DFA, full : bool = False) -> str: # full - verbose description of DFA - only for development, dismiss later # full - verbose description of DFA - only for development, dismiss later if full: if full: return f"DFA = ({_names_to_str(dfa.states)}, {_names_to_str(dfa.characters)}, " \ return f"DFA = ({_names_to_str(dfa.states)}, " \ f"{_names_to_str(dfa.characters)}, " \ f"d, {init}, {final})\n{transition}" f"d, {init}, {final})\n{transition}" return f"{init} {transition} {final}" return f"{init} {transition} {final}" Loading @@ -86,7 +93,9 @@ def reggrammar_to_str(reg: RegGrammar, full: bool = False) -> str: # full - verbose description of DFA - only for development, dismiss later # full - verbose description of DFA - only for development, dismiss later nonterminals_names = _names_to_str(reg.nonterminals) nonterminals_names = _names_to_str(reg.nonterminals) terminals = _names_to_str(reg.terminals) terminals = _names_to_str(reg.terminals) return f"Grammar: ({nonterminals_names}, {terminals}, P, {reg.init.name})\n{_rules_to_str(reg.rules, nonterminals)}" return f"Grammar: ({nonterminals_names}, {terminals}, P, " \ f"{reg.init.name})\n{_rules_to_str(reg.rules, nonterminals)}" def cfg_to_str(gra: CFG, full: bool = False) -> str: def cfg_to_str(gra: CFG, full: bool = False) -> str: nonterminals = deepcopy(gra.nonterminals).difference({gra.init}) nonterminals = deepcopy(gra.nonterminals).difference({gra.init}) Loading @@ -98,26 +107,32 @@ def cfg_to_str(gra: CFG, full: bool = False) -> str: # full - verbose description of DFA - only for development, dismiss later # full - verbose description of DFA - only for development, dismiss later nonterminals_names = _names_to_str(gra.nonterminals) nonterminals_names = _names_to_str(gra.nonterminals) terminals = _names_to_str(gra.terminals) terminals = _names_to_str(gra.terminals) return f"Grammar: ({nonterminals_names}, {terminals}, P, {gra.init.name})\n{_rules_to_str(gra.rules, nonterminals)}" return f"Grammar: ({nonterminals_names}, {terminals}, P, " \ f"{gra.init.name})\n{_rules_to_str(gra.rules, nonterminals)}" def nfa_to_str(nfa: NFA, full: bool = False) -> str: def nfa_to_str(nfa: NFA, full: bool = False) -> str: transition = "" transition = "" for key, set_states in nfa.transition.items(): for key, set_states in nfa.transition.items(): state, character = key state, character = key dest_states = nfa.transition[state, character] dest_states = nfa.transition[state, character] transition += f"({state.name},{character.name})={_names_to_str(dest_states)} " transition += f"({state.name},{character.name})=" \ f"{_names_to_str(dest_states)} " init = f"init={nfa.init.name}" init = f"init={nfa.init.name}" final = f"final={_names_to_str(nfa.final)}" final = f"final={_names_to_str(nfa.final)}" if full: if full: return f"NFA = ({_names_to_str(nfa.states)}, {_names_to_str(nfa.characters)}, " \ return f"NFA = ({_names_to_str(nfa.states)}, " \ f"{_names_to_str(nfa.characters)}, " \ f"d, {init}, {final})\n{transition}" f"d, {init}, {final})\n{transition}" return f"{init} {transition} {final}" return f"{init} {transition} {final}" def regex_to_str(reg: RegEx) -> str: def regex_to_str(reg: RegEx) -> str: return reg.expression.astprint() return reg.expression.astprint() def _common_parse(string: str, given_lexer, given_parser, given_builder): def _common_parse(string: str, given_lexer, given_parser, given_builder): error_listener = ErrorShouter() error_listener = ErrorShouter() chars = antlr4.InputStream(string) chars = antlr4.InputStream(string) Loading @@ -137,7 +152,8 @@ def _common_parse(string: str, given_lexer, given_parser, given_builder): def cfg(string: str) -> CFG: def cfg(string: str) -> CFG: try: try: builder = _common_parse(string, CFGLexer, CFGParser, CFGBuilder) builder = _common_parse(string, CFGLexer, CFGParser, CFGBuilder) return CFG(builder.nonterminals, builder.terminals, builder.rules, builder.init) return CFG(builder.nonterminals, builder.terminals, builder.rules, builder.init) except Exception as e: except Exception as e: raise ParsingError(e.args) raise ParsingError(e.args) Loading @@ -159,9 +175,11 @@ def dfa(string: str) -> DFA: if builder.init is None: if builder.init is None: builder.init = builder.first_state builder.init = builder.first_state if builder.init is None: if builder.init is None: raise ParsingError("Automat musí obsahovat alespoň jeden stav.") raise ParsingError( "Automat musí obsahovat alespoň jeden stav.") dfa = DFA(builder.states, builder.characters, builder.transition, builder.init, builder.final) dfa = DFA(builder.states, builder.characters, builder.transition, builder.init, builder.final) return dfa return dfa except Exception as e: except Exception as e: Loading @@ -175,9 +193,11 @@ def nfa(string: str) -> NFA: if builder.init is None: if builder.init is None: builder.init = builder.first_state builder.init = builder.first_state if builder.init is None: if builder.init is None: raise ParsingError("Automat musí obsahovat alespoň jeden stav.") raise ParsingError( "Automat musí obsahovat alespoň jeden stav.") return NFA(builder.states, builder.characters, builder.transition, builder.init, builder.final) return NFA(builder.states, builder.characters, builder.transition, builder.init, builder.final) except Exception as e: except Exception as e: raise ParsingError(e.args) raise ParsingError(e.args) Loading Loading @@ -252,7 +272,8 @@ class DFABuilder(DFAListener, StateVisitor): self.characters.add(character) self.characters.add(character) if (state, character) in self.transition: if (state, character) in self.transition: print(f"Upozornění: v textovém zápisu se objevilo více přechodů pro stejnou dvojici ({state.name}, {character.name}).") print("Upozornění: v textovém zápisu se objevilo více přechodů " f"pro stejnou dvojici ({state.name}, {character.name}).") self.transition[state, character] = dest_state self.transition[state, character] = dest_state if self.first_state is None: if self.first_state is None: Loading Loading @@ -295,21 +316,25 @@ class NFABuilder(NFAListener, StateVisitor): dest_states = set() dest_states = set() i = 0 i = 0 while ctx.stateset().statename(i) is not None: while ctx.stateset().statename(i) is not None: dest_state = State(self.visitStatename(ctx.stateset().statename(i))) dest_state = State( self.visitStatename(ctx.stateset().statename(i))) self.states.add(dest_state) self.states.add(dest_state) dest_states.add(dest_state) dest_states.add(dest_state) i += 1 i += 1 if ctx.EPSILON(): if ctx.EPSILON(): if (state, Eps()) in self.transition: if (state, Eps()) in self.transition: print(f"Upozornění: v textovém zápisu se objevilo více přechodů pro stejnou dvojici ({state.name}, ε).") print("Upozornění: v textovém zápisu se objevilo více " f"přechodů pro stejnou dvojici ({state.name}, ε).") self.transition[state, Eps()] = dest_states self.transition[state, Eps()] = dest_states self.efa = True self.efa = True else: else: character = Character(self.visitStatename(ctx.statename(1))) character = Character(self.visitStatename(ctx.statename(1))) self.characters.add(character) self.characters.add(character) if (state, character) in self.transition: if (state, character) in self.transition: print(f"Upozornění: v textovém zápisu se objevilo více přechodů pro stejnou dvojici ({state.name}, {character.name}).") print("Upozornění: v textovém zápisu se objevilo více " f"přechodů pro stejnou dvojici ({state.name}, " f"{character.name}).") self.transition[state, character] = dest_states self.transition[state, character] = dest_states if self.first_state is None: if self.first_state is None: Loading Loading @@ -342,10 +367,13 @@ class RegExBuilder(RegExVisitor): # Binary operation: union or explicit concatenation # Binary operation: union or explicit concatenation if ctx.UNION() or ctx.CONCAT(): if ctx.UNION() or ctx.CONCAT(): op = Bin.Union if ctx.UNION() is not None else Bin.Concat op = Bin.Union if ctx.UNION() is not None else Bin.Concat return BinOp(self.visitExpr(ctx.expr(0)), op, self.visitExpr(ctx.expr(1))) return BinOp(self.visitExpr(ctx.expr(0)), op, self.visitExpr(ctx.expr(1))) # Implicit concatenation of (iterated) symbols or expressions in parentheses # Implicit concatenation of (iterated) symbols or expressions in expressions = list(map(lambda x: self.visitConcatenable(x), ctx.concatenated())) # parentheses expressions = list(map(lambda x: self.visitConcatenable(x), ctx.concatenated())) return self.implicit_concat(expressions) return self.implicit_concat(expressions) def visitConcatenable(self, ctx): def visitConcatenable(self, ctx): Loading Loading @@ -396,7 +424,8 @@ class RegExBuilder(RegExVisitor): elif ctx.parentheses(): elif ctx.parentheses(): expression = self.visitParentheses(ctx.parentheses()) expression = self.visitParentheses(ctx.parentheses()) return IterOp(expression, Iter.Positive) if positive else IterOp(expression, Iter.Iteration) return IterOp(expression, Iter.Positive) if positive \ else IterOp(expression, Iter.Iteration) def implicit_concat(self, to_concat): def implicit_concat(self, to_concat): ast = to_concat[0] ast = to_concat[0] Loading @@ -405,11 +434,11 @@ class RegExBuilder(RegExVisitor): ast = BinOp(ast, Bin.Concat, expression) ast = BinOp(ast, Bin.Concat, expression) return ast return ast # for future support of comments # for future support of comments def exitComment(self, ctx) -> None: def exitComment(self, ctx) -> None: return None return None class CFGBuilder(CFGListener): class CFGBuilder(CFGListener): anyvalue_attributes = _anyvalue_attributes(CFGParser) anyvalue_attributes = _anyvalue_attributes(CFGParser) Loading @@ -431,11 +460,13 @@ class CFGBuilder(CFGListener): if ctx.CAPS(): if ctx.CAPS(): name = str(ctx.CAPS()) name = str(ctx.CAPS()) elif ctx.LEFT_ANGLE(): elif ctx.LEFT_ANGLE(): name = '<' + ''.join(map(lambda x: self.visitSymbol(x), ctx.symbol())) + '>' name = '<' + ''.join(map(lambda x: self.visitSymbol(x), ctx.symbol())) + '>' if ctx.APOSTROPHE(): if ctx.APOSTROPHE(): name = name + len(ctx.APOSTROPHE()) * "'" name = name + len(ctx.APOSTROPHE()) * "'" elif ctx.APOSTROPHE(): elif ctx.APOSTROPHE(): name = self.visitSymbol(ctx.symbol(0)) + len(ctx.APOSTROPHE())*"'" name = self.visitSymbol(ctx.symbol(0)) \ + len(ctx.APOSTROPHE()) * "'" nonterminal = Nonterminal(name) nonterminal = Nonterminal(name) self.nonterminals.add(nonterminal) self.nonterminals.add(nonterminal) Loading Loading @@ -465,7 +496,8 @@ class CFGBuilder(CFGListener): sequence.append(terminal) sequence.append(terminal) else: else: sequence.append(self.visitNonterminal(ctx.term_or_nonterm(i).nonterminal())) sequence.append(self.visitNonterminal( ctx.term_or_nonterm(i).nonterminal())) i += 1 i += 1 return sequence return sequence Loading @@ -473,7 +505,7 @@ class CFGBuilder(CFGListener): def exitOnerule(self, ctx): def exitOnerule(self, ctx): nonterminal = self.visitNonterminal(ctx.nonterminal()) nonterminal = self.visitNonterminal(ctx.nonterminal()) self.nonterminals.add(nonterminal) self.nonterminals.add(nonterminal) if self.init == None: if self.init is None: self.init = nonterminal self.init = nonterminal # multiple lines for one nonterminal are possible this way # multiple lines for one nonterminal are possible this way Loading