<commit_before>#! /usr/bin/env python

"""
Shakespeare -- An interpreter for the Shakespeare Programming Language
"""

from .shakespeare_parser import shakespeareParser
from .errors import ShakespeareRuntimeError
from .utils import parseinfo_context
from .character import Character
import math
from functools import wraps

class Shakespeare:
    """
    Interpreter for the Shakespeare Programming Language.
    """

    def _add_interpreter_context_to_errors(func):
        @wraps(func)
        def inner_function(self, *args, **kwargs):
            try:
                return func(self, *args, **kwargs)
            except ShakespeareRuntimeError as exc:
                if not exc.interpreter:
                    exc.interpreter = self
                raise exc
        return inner_function

    def _parse_first_argument(rule_name):
        def decorator(func):
            @wraps(func)
            def inner_function(self, first_arg, *args, **kwargs):
                parsed = self._parse_if_necessary(first_arg, rule_name)

                try:
                    return func(self, parsed, *args, **kwargs)
                except ShakespeareRuntimeError as exc:
                    if not exc.parseinfo:
                        exc.parseinfo = parsed.parseinfo
                    raise exc
            return inner_function
        return decorator

    def __init__(self, play):
        self.parser = shakespeareParser()
        self.ast = self._parse_if_necessary(play, 'play')
        self._run_dramatis_personae(self.ast.dramatis_personae)

        self.current_position = {'act': 0, 'scene': 0, 'event': 0}
        self._make_position_consistent()
        self._input_buffer = ''

        self.global_boolean = False

    # PUBLIC METHODS

    @_add_interpreter_context_to_errors
    def run(self, breakpoint_callback=lambda: None):
        """
        Run the SPL play.

        Arguments:
        breakpoint_callback -- An optional callback, to be called if a debug
                               breakpoint is hit
        """
        while not self.play_over():
            if self._next_event().parseinfo.rule == 'breakpoint':
                self._advance_position()
                breakpoint_callback()
            else:
                self.step_forward()

    @_add_interpreter_context_to_errors
    def play_over(self):
        """Return whether the play has finished."""
        return self.current_position['act'] >= len(self.ast.acts)

    @_add_interpreter_context_to_errors
    def step_forward(self):
        """
        Run the next event in the play.
        """
        event_to_run = self._next_event()
        has_goto = self.run_event(event_to_run)

        if self.current_position and not has_goto:
            self._advance_position()

    @_add_interpreter_context_to_errors
    def next_event_text(self):
        """Return the contents of the next event in the play."""
        current_event = self._next_event()
        return parseinfo_context(current_event.parseinfo)

    @_add_interpreter_context_to_errors
    @_parse_first_argument('event')
    def run_event(self, event):
        """
        Run an event in the current executing context.

        Arguments:
        event -- A string or AST representation of an event (line, entrance,
                 exit, etc.)
        """
        has_goto = False

        if event.parseinfo.rule == 'line':
            has_goto = self._run_line(event)
        elif event.parseinfo.rule == 'entrance':
            self._run_entrance(event)
        elif event.parseinfo.rule == 'exeunt':
            self._run_exeunt(event)
        elif event.parseinfo.rule == 'exit':
            self._run_exit(event)
        else:
            raise ShakespeareRuntimeError('Unknown event type: ' + event.parseinfo.rule)

        return has_goto

    @_add_interpreter_context_to_errors
    @_parse_first_argument('sentence')
    def run_sentence(self, sentence, character):
        """
        Run a sentence in the current execution context.

        Arguments:
        sentence -- A string or AST representation of a sentence
        character -- A name or Shakespeare.Character representation of the
                     character speaking the sentence.
        """
        character = self._on_stage_character_by_name_if_necessary(character)

        if sentence.parseinfo.rule == 'assignment':
            self._run_assignment(sentence, character)
        elif sentence.parseinfo.rule == 'question':
            self._run_question(sentence, character)
        elif sentence.parseinfo.rule == 'output':
            self._run_output(sentence, character)
        elif sentence.parseinfo.rule == 'input':
            self._run_input(sentence, character)
        elif sentence.parseinfo.rule == 'push':
            self._run_push(sentence, character)
        elif sentence.parseinfo.rule == 'pop':
            self._run_pop(sentence, character)
        elif sentence.parseinfo.rule == 'goto':
            went_to = self._run_goto(sentence)
            if went_to:
                return True
        else:
            raise ShakespeareRuntimeError('Unknown sentence type: ' + sentence.parseinfo.rule)

    @_add_interpreter_context_to_errors
    @_parse_first_argument('question')
    def evaluate_question(self, question, character):
        """
        Evaluate a question in the current execution context.

        Arguments:
        question -- A string or AST representation of a question
        character -- A name or Shakespeare.Character representation of the
                     character asking the question.
        """
        character = self._on_stage_character_by_name_if_necessary(character)

        values = [
            self.evaluate_expression(v, character) for v in
                [question.first_value, question.second_value]
        ]
        if question.comparative.parseinfo.rule == 'positive_comparative':
            return values[0] > values[1]
        elif question.comparative.parseinfo.rule == 'negative_comparative':
            return values[0] < values[1]
        elif question.comparative.parseinfo.rule == 'neutral_comparative':
            return values[0] == values[1]
        raise ShakespeareRuntimeError('Unknown comparative type: ' + question.comparative.parseinfo.rule)

    @_add_interpreter_context_to_errors
    @_parse_first_argument('value')
    def evaluate_expression(self, value, character):
        """
        Evaluate an expression in the current execution context.

        Arguments:
        expression -- A string or AST representation of an expression
        character -- A name or Shakespeare.Character representation of the
                     character speaking the expression.
        """
        character = self._character_by_name_if_necessary(character)

        if value.parseinfo.rule == 'first_person_value':
            return character.value
        elif value.parseinfo.rule == 'second_person_value':
            return self._character_opposite(character).value
        elif value.parseinfo.rule == 'negative_noun_phrase':
            return -pow(2, len(value.adjectives))
        elif value.parseinfo.rule == 'positive_noun_phrase':
            return pow(2, len(value.adjectives))
        elif value.parseinfo.rule == 'character_name':
            return self._character_by_name(value.name).value
        elif value.parseinfo.rule == 'nothing':
            return 0
        elif value.parseinfo.rule == 'unary_expression':<commit_msg>Update docstrings to reflect moved Character class<commit_after>#! /usr/bin/env python

"""
Shakespeare -- An interpreter for the Shakespeare Programming Language
"""

from .shakespeare_parser import shakespeareParser
from .errors import ShakespeareRuntimeError
from .utils import parseinfo_context
from .character import Character
import math
from functools import wraps

class Shakespeare:
    """
    Interpreter for the Shakespeare Programming Language.
    """

    def _add_interpreter_context_to_errors(func):
        @wraps(func)
        def inner_function(self, *args, **kwargs):
            try:
                return func(self, *args, **kwargs)
            except ShakespeareRuntimeError as exc:
                if not exc.interpreter:
                    exc.interpreter = self
                raise exc
        return inner_function

    def _parse_first_argument(rule_name):
        def decorator(func):
            @wraps(func)
            def inner_function(self, first_arg, *args, **kwargs):
                parsed = self._parse_if_necessary(first_arg, rule_name)

                try:
                    return func(self, parsed, *args, **kwargs)
                except ShakespeareRuntimeError as exc:
                    if not exc.parseinfo:
                        exc.parseinfo = parsed.parseinfo
                    raise exc
            return inner_function
        return decorator

    def __init__(self, play):
        self.parser = shakespeareParser()
        self.ast = self._parse_if_necessary(play, 'play')
        self._run_dramatis_personae(self.ast.dramatis_personae)

        self.current_position = {'act': 0, 'scene': 0, 'event': 0}
        self._make_position_consistent()
        self._input_buffer = ''

        self.global_boolean = False

    # PUBLIC METHODS

    @_add_interpreter_context_to_errors
    def run(self, breakpoint_callback=lambda: None):
        """
        Run the SPL play.

        Arguments:
        breakpoint_callback -- An optional callback, to be called if a debug
                               breakpoint is hit
        """
        while not self.play_over():
            if self._next_event().parseinfo.rule == 'breakpoint':
                self._advance_position()
                breakpoint_callback()
            else:
                self.step_forward()

    @_add_interpreter_context_to_errors
    def play_over(self):
        """Return whether the play has finished."""
        return self.current_position['act'] >= len(self.ast.acts)

    @_add_interpreter_context_to_errors
    def step_forward(self):
        """
        Run the next event in the play.
        """
        event_to_run = self._next_event()
        has_goto = self.run_event(event_to_run)

        if self.current_position and not has_goto:
            self._advance_position()

    @_add_interpreter_context_to_errors
    def next_event_text(self):
        """Return the contents of the next event in the play."""
        current_event = self._next_event()
        return parseinfo_context(current_event.parseinfo)

    @_add_interpreter_context_to_errors
    @_parse_first_argument('event')
    def run_event(self, event):
        """
        Run an event in the current executing context.

        Arguments:
        event -- A string or AST representation of an event (line, entrance,
                 exit, etc.)
        """
        has_goto = False

        if event.parseinfo.rule == 'line':
            has_goto = self._run_line(event)
        elif event.parseinfo.rule == 'entrance':
            self._run_entrance(event)
        elif event.parseinfo.rule == 'exeunt':
            self._run_exeunt(event)
        elif event.parseinfo.rule == 'exit':
            self._run_exit(event)
        else:
            raise ShakespeareRuntimeError('Unknown event type: ' + event.parseinfo.rule)

        return has_goto

    @_add_interpreter_context_to_errors
    @_parse_first_argument('sentence')
    def run_sentence(self, sentence, character):
        """
        Run a sentence in the current execution context.

        Arguments:
        sentence -- A string or AST representation of a sentence
        character -- A name or shakespearelang.Character representation of the
                     character speaking the sentence.
        """
        character = self._on_stage_character_by_name_if_necessary(character)

        if sentence.parseinfo.rule == 'assignment':
            self._run_assignment(sentence, character)
        elif sentence.parseinfo.rule == 'question':
            self._run_question(sentence, character)
        elif sentence.parseinfo.rule == 'output':
            self._run_output(sentence, character)
        elif sentence.parseinfo.rule == 'input':
            self._run_input(sentence, character)
        elif sentence.parseinfo.rule == 'push':
            self._run_push(sentence, character)
        elif sentence.parseinfo.rule == 'pop':
            self._run_pop(sentence, character)
        elif sentence.parseinfo.rule == 'goto':
            went_to = self._run_goto(sentence)
            if went_to:
                return True
        else:
            raise ShakespeareRuntimeError('Unknown sentence type: ' + sentence.parseinfo.rule)

    @_add_interpreter_context_to_errors
    @_parse_first_argument('question')
    def evaluate_question(self, question, character):
        """
        Evaluate a question in the current execution context.

        Arguments:
        question -- A string or AST representation of a question
        character -- A name or shakespearelang.Character representation of the
                     character asking the question.
        """
        character = self._on_stage_character_by_name_if_necessary(character)

        values = [
            self.evaluate_expression(v, character) for v in
                [question.first_value, question.second_value]
        ]
        if question.comparative.parseinfo.rule == 'positive_comparative':
            return values[0] > values[1]
        elif question.comparative.parseinfo.rule == 'negative_comparative':
            return values[0] < values[1]
        elif question.comparative.parseinfo.rule == 'neutral_comparative':
            return values[0] == values[1]
        raise ShakespeareRuntimeError('Unknown comparative type: ' + question.comparative.parseinfo.rule)

    @_add_interpreter_context_to_errors
    @_parse_first_argument('value')
    def evaluate_expression(self, value, character):
        """
        Evaluate an expression in the current execution context.

        Arguments:
        expression -- A string or AST representation of an expression
        character -- A name or shakespearelang.Character representation of the
                     character speaking the expression.
        """
        character = self._character_by_name_if_necessary(character)

        if value.parseinfo.rule == 'first_person_value':
            return character.value
        elif value.parseinfo.rule == 'second_person_value':
            return self._character_opposite(character).value
        elif value.parseinfo.rule == 'negative_noun_phrase':
            return -pow(2, len(value.adjectives))
        elif value.parseinfo.rule == 'positive_noun_phrase':
            return pow(2, len(value.adjectives))
        elif value.parseinfo.rule == 'character_name':
            return self._character_by_name(value.name).value
        elif value.parseinfo.rule == 'nothing':
            return 0
        elif value.parseinfo.rule == 'unary_expression':
{'fraction_non_alphanumeric': 0.08359400998336107, 'fraction_numerical': 0.0015973377703826955, 'mean_word_length': 2.2800698537437243, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2465452', 'n_tokens_mistral': 3974, 'n_tokens_neox': 3845, 'n_words': 971}