Spaces:
Sleeping
Sleeping
# | |
# Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. | |
# Use of this file is governed by the BSD 3-clause license that | |
# can be found in the LICENSE.txt file in the project root. | |
# | |
# A tree structure used to record the semantic context in which | |
# an ATN configuration is valid. It's either a single predicate, | |
# a conjunction {@code p1&&p2}, or a sum of products {@code p1||p2}. | |
# | |
# <p>I have scoped the {@link AND}, {@link OR}, and {@link Predicate} subclasses of | |
# {@link SemanticContext} within the scope of this outer class.</p> | |
# | |
from antlr4.Recognizer import Recognizer | |
from antlr4.RuleContext import RuleContext | |
from io import StringIO | |
class SemanticContext(object): | |
# | |
# The default {@link SemanticContext}, which is semantically equivalent to | |
# a predicate of the form {@code {true}?}. | |
# | |
NONE = None | |
# | |
# For context independent predicates, we evaluate them without a local | |
# context (i.e., null context). That way, we can evaluate them without | |
# having to create proper rule-specific context during prediction (as | |
# opposed to the parser, which creates them naturally). In a practical | |
# sense, this avoids a cast exception from RuleContext to myruleContext. | |
# | |
# <p>For context dependent predicates, we must pass in a local context so that | |
# references such as $arg evaluate properly as _localctx.arg. We only | |
# capture context dependent predicates in the context in which we begin | |
# prediction, so we passed in the outer context here in case of context | |
# dependent predicate evaluation.</p> | |
# | |
def eval(self, parser:Recognizer , outerContext:RuleContext ): | |
pass | |
# | |
# Evaluate the precedence predicates for the context and reduce the result. | |
# | |
# @param parser The parser instance. | |
# @param outerContext The current parser context object. | |
# @return The simplified semantic context after precedence predicates are | |
# evaluated, which will be one of the following values. | |
# <ul> | |
# <li>{@link #NONE}: if the predicate simplifies to {@code true} after | |
# precedence predicates are evaluated.</li> | |
# <li>{@code null}: if the predicate simplifies to {@code false} after | |
# precedence predicates are evaluated.</li> | |
# <li>{@code this}: if the semantic context is not changed as a result of | |
# precedence predicate evaluation.</li> | |
# <li>A non-{@code null} {@link SemanticContext}: the new simplified | |
# semantic context after precedence predicates are evaluated.</li> | |
# </ul> | |
# | |
def evalPrecedence(self, parser:Recognizer, outerContext:RuleContext): | |
return self | |
# need forward declaration | |
AND = None | |
def andContext(a:SemanticContext, b:SemanticContext): | |
if a is None or a is SemanticContext.NONE: | |
return b | |
if b is None or b is SemanticContext.NONE: | |
return a | |
result = AND(a, b) | |
if len(result.opnds) == 1: | |
return result.opnds[0] | |
else: | |
return result | |
# need forward declaration | |
OR = None | |
def orContext(a:SemanticContext, b:SemanticContext): | |
if a is None: | |
return b | |
if b is None: | |
return a | |
if a is SemanticContext.NONE or b is SemanticContext.NONE: | |
return SemanticContext.NONE | |
result = OR(a, b) | |
if len(result.opnds) == 1: | |
return result.opnds[0] | |
else: | |
return result | |
def filterPrecedencePredicates(collection:set): | |
return [context for context in collection if isinstance(context, PrecedencePredicate)] | |
class Predicate(SemanticContext): | |
__slots__ = ('ruleIndex', 'predIndex', 'isCtxDependent') | |
def __init__(self, ruleIndex:int=-1, predIndex:int=-1, isCtxDependent:bool=False): | |
self.ruleIndex = ruleIndex | |
self.predIndex = predIndex | |
self.isCtxDependent = isCtxDependent # e.g., $i ref in pred | |
def eval(self, parser:Recognizer , outerContext:RuleContext ): | |
localctx = outerContext if self.isCtxDependent else None | |
return parser.sempred(localctx, self.ruleIndex, self.predIndex) | |
def __hash__(self): | |
return hash((self.ruleIndex, self.predIndex, self.isCtxDependent)) | |
def __eq__(self, other): | |
if self is other: | |
return True | |
elif not isinstance(other, Predicate): | |
return False | |
return self.ruleIndex == other.ruleIndex and \ | |
self.predIndex == other.predIndex and \ | |
self.isCtxDependent == other.isCtxDependent | |
def __str__(self): | |
return "{" + str(self.ruleIndex) + ":" + str(self.predIndex) + "}?" | |
class PrecedencePredicate(SemanticContext): | |
def __init__(self, precedence:int=0): | |
self.precedence = precedence | |
def eval(self, parser:Recognizer , outerContext:RuleContext ): | |
return parser.precpred(outerContext, self.precedence) | |
def evalPrecedence(self, parser:Recognizer, outerContext:RuleContext): | |
if parser.precpred(outerContext, self.precedence): | |
return SemanticContext.NONE | |
else: | |
return None | |
def __lt__(self, other): | |
return self.precedence < other.precedence | |
def __hash__(self): | |
return 31 | |
def __eq__(self, other): | |
if self is other: | |
return True | |
elif not isinstance(other, PrecedencePredicate): | |
return False | |
else: | |
return self.precedence == other.precedence | |
# A semantic context which is true whenever none of the contained contexts | |
# is false. | |
del AND | |
class AND(SemanticContext): | |
__slots__ = 'opnds' | |
def __init__(self, a:SemanticContext, b:SemanticContext): | |
operands = set() | |
if isinstance( a, AND ): | |
operands.update(a.opnds) | |
else: | |
operands.add(a) | |
if isinstance( b, AND ): | |
operands.update(b.opnds) | |
else: | |
operands.add(b) | |
precedencePredicates = filterPrecedencePredicates(operands) | |
if len(precedencePredicates)>0: | |
# interested in the transition with the lowest precedence | |
reduced = min(precedencePredicates) | |
operands.add(reduced) | |
self.opnds = list(operands) | |
def __eq__(self, other): | |
if self is other: | |
return True | |
elif not isinstance(other, AND): | |
return False | |
else: | |
return self.opnds == other.opnds | |
def __hash__(self): | |
h = 0 | |
for o in self.opnds: | |
h = hash((h, o)) | |
return hash((h, "AND")) | |
# | |
# {@inheritDoc} | |
# | |
# <p> | |
# The evaluation of predicates by this context is short-circuiting, but | |
# unordered.</p> | |
# | |
def eval(self, parser:Recognizer, outerContext:RuleContext): | |
return all(opnd.eval(parser, outerContext) for opnd in self.opnds) | |
def evalPrecedence(self, parser:Recognizer, outerContext:RuleContext): | |
differs = False | |
operands = [] | |
for context in self.opnds: | |
evaluated = context.evalPrecedence(parser, outerContext) | |
differs |= evaluated is not context | |
if evaluated is None: | |
# The AND context is false if any element is false | |
return None | |
elif evaluated is not SemanticContext.NONE: | |
# Reduce the result by skipping true elements | |
operands.append(evaluated) | |
if not differs: | |
return self | |
if len(operands)==0: | |
# all elements were true, so the AND context is true | |
return SemanticContext.NONE | |
result = None | |
for o in operands: | |
result = o if result is None else andContext(result, o) | |
return result | |
def __str__(self): | |
with StringIO() as buf: | |
first = True | |
for o in self.opnds: | |
if not first: | |
buf.write("&&") | |
buf.write(str(o)) | |
first = False | |
return buf.getvalue() | |
# | |
# A semantic context which is true whenever at least one of the contained | |
# contexts is true. | |
del OR | |
class OR (SemanticContext): | |
__slots__ = 'opnds' | |
def __init__(self, a:SemanticContext, b:SemanticContext): | |
operands = set() | |
if isinstance( a, OR ): | |
operands.update(a.opnds) | |
else: | |
operands.add(a) | |
if isinstance( b, OR ): | |
operands.update(b.opnds) | |
else: | |
operands.add(b) | |
precedencePredicates = filterPrecedencePredicates(operands) | |
if len(precedencePredicates)>0: | |
# interested in the transition with the highest precedence | |
s = sorted(precedencePredicates) | |
reduced = s[-1] | |
operands.add(reduced) | |
self.opnds = list(operands) | |
def __eq__(self, other): | |
if self is other: | |
return True | |
elif not isinstance(other, OR): | |
return False | |
else: | |
return self.opnds == other.opnds | |
def __hash__(self): | |
h = 0 | |
for o in self.opnds: | |
h = hash((h, o)) | |
return hash((h, "OR")) | |
# <p> | |
# The evaluation of predicates by this context is short-circuiting, but | |
# unordered.</p> | |
# | |
def eval(self, parser:Recognizer, outerContext:RuleContext): | |
return any(opnd.eval(parser, outerContext) for opnd in self.opnds) | |
def evalPrecedence(self, parser:Recognizer, outerContext:RuleContext): | |
differs = False | |
operands = [] | |
for context in self.opnds: | |
evaluated = context.evalPrecedence(parser, outerContext) | |
differs |= evaluated is not context | |
if evaluated is SemanticContext.NONE: | |
# The OR context is true if any element is true | |
return SemanticContext.NONE | |
elif evaluated is not None: | |
# Reduce the result by skipping false elements | |
operands.append(evaluated) | |
if not differs: | |
return self | |
if len(operands)==0: | |
# all elements were false, so the OR context is false | |
return None | |
result = None | |
for o in operands: | |
result = o if result is None else orContext(result, o) | |
return result | |
def __str__(self): | |
with StringIO() as buf: | |
first = True | |
for o in self.opnds: | |
if not first: | |
buf.write("||") | |
buf.write(str(o)) | |
first = False | |
return buf.getvalue() | |
SemanticContext.NONE = Predicate() | |