Project

Profile

Help

How to connect?
Download (7.18 KB) Statistics View on GitHub Reload from mirrored respository
| Branch: | Tag: | Revision:

github / src / sharp / engine.py @ 0713b7fd

1
# Copyright (c) 2016, LE GOFF Vincent
2
# All rights reserved.
3

    
4
# Redistribution and use in source and binary forms, with or without
5
# modification, are permitted provided that the following conditions are met:
6

    
7
# * Redistributions of source code must retain the above copyright notice, this
8
#   list of conditions and the following disclaimer.
9

    
10
# * Redistributions in binary form must reproduce the above copyright notice,
11
#   this list of conditions and the following disclaimer in the documentation
12
#   and/or other materials provided with the distribution.
13

    
14
# * Neither the name of ytranslate nor the names of its
15
#   contributors may be used to endorse or promote products derived from
16
#   this software without specific prior written permission.
17

    
18
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28

    
29
"""Module containing the SharpEngine class."""
30

    
31
from textwrap import dedent
32

    
33
from sharp import FUNCTIONS
34

    
35
class SharpScript(object):
36

    
37
    """Class representing a SharpScript engine.
38

39
    An SharpScript engine is often linked with the game's main engine
40
    and an individual client, which is itself optionally linked to
41
    the ui application.
42

43
    """
44

    
45
    def __init__(self, engine, client):
46
        self.engine = engine
47
        self.client = client
48
        self.locals = {}
49
        self.globals = globals()
50

    
51
        # Adding the functions
52
        for name, function in FUNCTIONS.items():
53
            function = function(engine, client, self)
54
            self.globals[name] = function.run
55

    
56
    def execute(self, code):
57
        """Execute the SharpScript code given as an argument."""
58
        instructions = self.feed(code)
59
        globals = self.globals
60
        locals = self.locals
61
        for instruction in instructions:
62
            exec(instruction, globals, locals)
63

    
64
    def feed(self, content):
65
        """Feed the SharpScript engine with a string content.
66

67
        The content is probably a file with several statements in
68
        SharpScript, or a single statement.  In all cases, this function
69
        returns the list of Python codes corresponding with
70
        this suite of statements.
71

72
        """
73
        # First, splits into statements
74
        statements = self.split_statements(content)
75
        codes = []
76
        for statement in statements:
77
            pycode = self.convert_to_python(statement)
78
            codes.append(pycode)
79

    
80
        return codes
81

    
82
    def convert_to_python(self, statement):
83
        """Convert the statement to Python and return the str code.
84

85
        The statement given in argument should be a tuple:  The first
86
        argument of the tuple should be a function (like '#play' or
87
        '#send').  The remaining arguments should be put in a string,
88
        except for other Sharp or Python code.
89

90
        """
91
        function_name = statement[0][1:].lower()
92
        arguments = []
93
        for argument in statement[1:]:
94
            if argument.startswith("{+"):
95
                argument = argument[3:-2]
96
                argument = repr(dedent(argument)).replace("\\n", "\n")
97
                argument = "compile(" + argument + ", 'SharpScript', 'exec')"
98
            elif argument.startswith("{"):
99
                argument = repr(argument[1:-1])
100
            elif argument.endswith("=True") or argument.endswith("=False"):
101
                pass
102
            else:
103
                argument = repr(argument)
104

    
105
            arguments.append(argument)
106

    
107
        return function_name + "(" + ", ".join(arguments) + ")"
108

    
109
    def split_statements(self, content):
110
        """Split the given string content into different statements.
111

112
        A statement is one-line short at the very least.  It can be
113
        longer by that, if it's enclosed into braces.
114

115
        """
116
        statements = []
117
        i = 0
118
        function_name = ""
119
        arguments = []
120
        while True:
121
            remaining = content[i:]
122

    
123
            # If remaining is empty, saves the statement and exits the loop
124
            if not remaining or remaining.isspace():
125
                if function_name:
126
                    statements.append((function_name, ) + tuple(arguments))
127

    
128
                break
129

    
130
            # If remaining begins with a new line
131
            if remaining[0] == "\n":
132
                if function_name:
133
                    statements.append((function_name, ) + tuple(arguments))
134
                    function_name = ""
135
                    arguments = []
136

    
137
                i += 1
138
                continue
139

    
140
            # If remaining begins with a space
141
            if remaining[0].isspace():
142
                remaining = remaining[1:]
143
                i += 1
144
                continue
145

    
146
            # If the function_name is not defined, take the first parameter
147
            if not function_name:
148
                if remaining.startswith("#"):
149
                    # This is obviously a function name
150
                    function_name = remaining.splitlines()[0].split(" ")[0]
151
                    arguments = []
152
                    i += len(function_name)
153
                else:
154
                    function_name = "#send"
155
                    argument = remaining.splitlines()[0]
156
                    i += len(argument)
157
                    arguments = [argument]
158
            elif remaining[0] in "+-":
159
                argument = remaining.splitlines()[0].split(" ")[0]
160
                flag = argument[1:]
161
                if argument.startswith("+"):
162
                    arguments.append(flag + "=True")
163
                else:
164
                    arguments.append(flag + "=False")
165
                i += len(flag) + 1
166
            elif remaining[0] == "{":
167
                end = self.find_right_brace(remaining)
168
                argument = remaining[:end + 1]
169
                arguments.append(argument)
170
                i += end + 1
171
            else:
172
                argument = remaining.splitlines()[0].split(" ")[0]
173
                i += len(argument)
174
                arguments.append(argument)
175

    
176
        return statements
177

    
178
    def find_right_brace(self, text):
179
        """Find the right brace matching the opening one.
180

181
        This function doesn't only look for the first right brace (}).
182
        It looks for a brace that would close the text and return the
183
        position of this character.  For instance:
184
            >>> Engine.find_right_brace("{first parameter {with} something} else")
185
            33
186

187
        """
188
        level = 0
189
        i = 0
190
        while i < len(text):
191
            char = text[i]
192
            if char == "{":
193
                level += 1
194
            elif char == "}":
195
                level -= 1
196

    
197
            if level == 0:
198
                return i
199

    
200
            i += 1
201

    
202
        return None
(2-2/3)