Seite 1 von 1

statt " 1 and 0 or 1 " => 1 and 0 or 1

Verfasst: Sonntag 20. Januar 2019, 18:24
von Pyreno
Hallo zusammen,

kann mir jemand hier irgendwie helfen:

ich werte Daten aus und bekomme zb. als Ergebnis einen String
code = ' ((1 or 0 or 0 and 1) or (0 or 0 and 1) and not 0)'
nun will als Ergebnis newcode = ((1 or 0 or 0 and 1) or (0 or 0 and 1) and not 0)
und am Ende True or False
ich wäre sehr dankbar wenn jemand eine Idee für mich hat

Re: statt " 1 and 0 or 1 " => 1 and 0 or 1

Verfasst: Sonntag 20. Januar 2019, 18:32
von Sirius3
Du sollst also als Hausaufgabe einen logischen Ausdruck parsen und auswerten. Was hast Du schon versucht?

Re: statt " 1 and 0 or 1 " => 1 and 0 or 1

Verfasst: Sonntag 20. Januar 2019, 20:16
von __blackjack__
Spasseshalber mal mit `redbaron` gelöst, weil sich dessen Parser (fälschlicherweise) nicht an dem führenden Leerzeichen in `code` stört, im Gegensatz zum `ast.parser`, mit dem das natürlich ansonsten grundsätzlich analog funktionieren würde:

Code: Alles auswählen

#!/usr/bin/env python3
from functools import singledispatch
from operator import and_, or_

from redbaron import RedBaron
from redbaron.nodes import (
    AssociativeParenthesisNode,
    BooleanOperatorNode,
    IntNode,
    UnitaryOperatorNode,
)

NAME_TO_FUNCTION = {'and': and_, 'or': or_}


@singledispatch
def evaluate(node):
    raise ValueError(f'Unknown node type {type(node)}: {node.dumps()}')


@evaluate.register(AssociativeParenthesisNode)
def evaluate_parenthesis(node):
    return evaluate(node.value)


@evaluate.register(UnitaryOperatorNode)
def evaluate_unitary_operator(node):
    if node.value != 'not':
        raise ValueError(f'Unknown unitary operator {node.value!r}')
    else:
        return not evaluate(node.target)


@evaluate.register(BooleanOperatorNode)
def evaluate_boolean_operator(node):
    operator = NAME_TO_FUNCTION[node.value]
    return operator(evaluate(node.first), evaluate(node.second))


@evaluate.register(IntNode)
def evaluate_int(node):
    value = int(node.value)
    if value not in [0, 1]:
        raise ValueError(f'Unexcpected value {value} not 0 or 1')
    return bool(value)


def main():
    code = ' ((1 or 0 or 0 and 1) or (0 or 0 and 1) and not 0)'
    red = RedBaron(code)
    if not red:
        raise ValueError('No code found.')
    print(evaluate(red[0]))
    

if __name__ == "__main__":
    main()

Re: statt " 1 and 0 or 1 " => 1 and 0 or 1

Verfasst: Freitag 25. Januar 2019, 12:07
von __blackjack__
Wie schon geschrieben, mit `ast.parse()` geht das analog:

Code: Alles auswählen

#!/usr/bin/env python3
import ast
from ast import And, BoolOp, Expr, Not, Num, Or, UnaryOp
from functools import reduce, singledispatch
from operator import and_, or_

TYPE_TO_OPERATOR_FUNCTION = {And: and_, Or: or_}


@singledispatch
def evaluate(node):
    raise ValueError(f'Unknown node type {type(node)}: {ast.dump(node)}')


@evaluate.register(Expr)
def evaluate_expression(node):
    return evaluate(node.value)


@evaluate.register(UnaryOp)
def evaluate_not(node):
    if not isinstance(node.op, Not):
        raise ValueError(f'Unknown unitary operator {node.op}')
    else:
        return not evaluate(node.operand)


@evaluate.register(BoolOp)
def evaluate_boolean_operation(node):
    operator = TYPE_TO_OPERATOR_FUNCTION[type(node.op)]
    return reduce(operator, map(evaluate, node.values))


@evaluate.register(Num)
def evaluate_number(node):
    if node.n not in [0, 1]:
        raise ValueError(f'Unexcpected value {node.n} not 0 or 1')
    else:
        return bool(node.n)


def main():
    code = '((1 or 0 or 0 and 1) or (0 or 0 and 1) and not 0)'
    print(evaluate(ast.parse(code, mode='single').body[0]))
    

if __name__ == "__main__":
    main()

Re: statt " 1 and 0 or 1 " => 1 and 0 or 1

Verfasst: Freitag 1. Februar 2019, 20:52
von __blackjack__
Wenn man sich keinen fertigen Python-Parser hernehmen möchte, kann man sich natürlich auch selbst einen Parser schreiben. Zum Beispiel mit Pyparsing:

Code: Alles auswählen

#!/usr/bin/env python3
from functools import reduce
from operator import and_, or_

import pyparsing as pp


VALUE = pp.oneOf('0 1').setParseAction(lambda s, loc, toks: bool(int(toks[0])))
NOT, AND, OR = (pp.Suppress(s).setName(s) for s in ['not', 'and', 'or'])
EXPRESSION = pp.infixNotation(
    VALUE,
    [
        (NOT, 1, pp.opAssoc.RIGHT, lambda s, loc, toks: not toks[0][0]),
        (AND, 2, pp.opAssoc.LEFT, lambda s, loc, toks: reduce(and_, toks[0])),
        (OR, 2, pp.opAssoc.LEFT, lambda s, loc, toks: reduce(or_, toks[0])),
    ]
)
GRAMMAR = EXPRESSION + pp.lineEnd
GRAMMAR.enablePackrat()


def main():
    source = '((1 or 0 or 0 and 1) or (0 or 0 and 1) and not 0)'
    try:
        result = GRAMMAR.parseString(source)
    except pp.ParseException as error:
        print(error)
    else:
        print(result[0])


if __name__ == "__main__":
    main()