Project

Profile

Help

Download (7.08 KB) Statistics View on GitHub Reload from mirrored respository
| Branch: | Tag: | Revision:

github / src / sharp / engine.py @ 95c4bc24

1 fa348e33 Vincent Le Goff
# 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 8d408f2d Vincent Le Goff
            function = function(engine, client, self)
54 fa348e33 Vincent Le Goff
            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 95c4bc24 Vincent Le Goff
        kwargs = {}
94 fa348e33 Vincent Le Goff
        for argument in statement[1:]:
95
            if argument.startswith("{+"):
96
                argument = argument[3:-2]
97 b57dc8cb Vincent Le Goff
                argument = repr(dedent(argument)).replace("\\n", "\n")
98
                argument = "compile(" + argument + ", 'SharpScript', 'exec')"
99 fa348e33 Vincent Le Goff
            elif argument.startswith("{"):
100
                argument = repr(argument[1:-1])
101 95c4bc24 Vincent Le Goff
            elif argument[0] in "-+":
102
                kwargs[argument[1:]] = True if argument[0] == "+" else False
103
                continue
104 fa348e33 Vincent Le Goff
            else:
105
                argument = repr(argument)
106
107
            arguments.append(argument)
108
109 95c4bc24 Vincent Le Goff
        code = function_name + "(" + ", ".join(arguments)
110
        if arguments and kwargs:
111
            code += ", "
112
113
        code += ", ".join([name + "=" + repr(value) for name, value in \
114
                kwargs.items()])
115
116
        return code + ")"
117 fa348e33 Vincent Le Goff
118
    def split_statements(self, content):
119
        """Split the given string content into different statements.
120

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

124
        """
125
        statements = []
126
        i = 0
127
        function_name = ""
128
        arguments = []
129
        while True:
130
            remaining = content[i:]
131
132
            # If remaining is empty, saves the statement and exits the loop
133
            if not remaining or remaining.isspace():
134
                if function_name:
135
                    statements.append((function_name, ) + tuple(arguments))
136
137
                break
138
139
            # If remaining begins with a new line
140
            if remaining[0] == "\n":
141
                if function_name:
142
                    statements.append((function_name, ) + tuple(arguments))
143
                    function_name = ""
144
                    arguments = []
145
146
                i += 1
147
                continue
148
149
            # If remaining begins with a space
150
            if remaining[0].isspace():
151
                remaining = remaining[1:]
152
                i += 1
153
                continue
154
155
            # If the function_name is not defined, take the first parameter
156
            if not function_name:
157
                if remaining.startswith("#"):
158
                    # This is obviously a function name
159
                    function_name = remaining.splitlines()[0].split(" ")[0]
160
                    arguments = []
161
                    i += len(function_name)
162
                else:
163
                    function_name = "#send"
164
                    argument = remaining.splitlines()[0]
165
                    i += len(argument)
166
                    arguments = [argument]
167
            elif remaining[0] == "{":
168
                end = self.find_right_brace(remaining)
169
                argument = remaining[:end + 1]
170
                arguments.append(argument)
171
                i += end + 1
172
            else:
173
                argument = remaining.splitlines()[0].split(" ")[0]
174
                i += len(argument)
175
                arguments.append(argument)
176
177
        return statements
178
179
    def find_right_brace(self, text):
180
        """Find the right brace matching the opening one.
181

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

188
        """
189
        level = 0
190
        i = 0
191
        while i < len(text):
192
            char = text[i]
193
            if char == "{":
194
                level += 1
195
            elif char == "}":
196
                level -= 1
197
198
            if level == 0:
199
                return i
200
201
            i += 1
202
203
        return None