1# Copyright 2004-2021 Tom Rothamel <pytom@bishoujo.us>
2#
3# Permission is hereby granted, free of charge, to any person
4# obtaining a copy of this software and associated documentation files
5# (the "Software"), to deal in the Software without restriction,
6# including without limitation the rights to use, copy, modify, merge,
7# publish, distribute, sublicense, and/or sell copies of the Software,
8# and to permit persons to whom the Software is furnished to do so,
9# subject to the following conditions:
10#
11# The above copyright notice and this permission notice shall be
12# included in all copies or substantial portions of the Software.
13#
14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22from __future__ import division, absolute_import, with_statement, print_function, unicode_literals
23from renpy.compat import *
24
25import renpy.test.testast as testast
26import renpy
27
28
29def parse_click(l, loc, target):
30
31    rv = testast.Click(loc, target)
32
33    while True:
34        if l.keyword('button'):
35            rv.button = int(l.require(l.integer))
36
37        elif l.keyword('pos'):
38            rv.position = l.require(l.simple_expression)
39
40        elif l.keyword('always'):
41            rv.always = True
42
43        else:
44            break
45
46    return rv
47
48
49def parse_type(l, loc, keys):
50    rv = testast.Type(loc, keys)
51
52    while True:
53
54        if l.keyword('pattern'):
55            rv.pattern = l.require(l.string)
56
57        elif l.keyword('pos'):
58            rv.position = l.require(l.simple_expression)
59
60        else:
61            break
62
63    return rv
64
65
66def parse_move(l, loc):
67    rv = testast.Move(loc)
68
69    rv.position = l.require(l.simple_expression)
70
71    while True:
72
73        if l.keyword('pattern'):
74            rv.pattern = l.require(l.string)
75
76        else:
77            break
78
79    return rv
80
81
82def parse_drag(l, loc):
83
84    points = l.require(l.simple_expression)
85
86    rv = testast.Drag(loc, points)
87
88    while True:
89        if l.keyword('button'):
90            rv.button = int(l.require(l.integer))
91
92        elif l.keyword('pattern'):
93            rv.pattern = l.require(l.string)
94
95        elif l.keyword('steps'):
96            rv.steps = int(l.require(l.integer))
97
98        else:
99            break
100
101    return rv
102
103
104def parse_clause(l, loc):
105    if l.keyword("run"):
106
107        expr = l.require(l.simple_expression)
108        return testast.Action(loc, expr)
109
110    elif l.keyword("pause"):
111
112        expr = l.require(l.simple_expression)
113        return testast.Pause(loc, expr)
114
115    elif l.keyword("label"):
116
117        name = l.require(l.name)
118        return testast.Label(loc, name)
119
120    elif l.keyword("type"):
121
122        name = l.name()
123        if name is not None:
124            return parse_type(l, loc, [ name ])
125
126        string = l.require(l.string)
127
128        return parse_type(l, loc, list(string))
129
130    elif l.keyword("drag"):
131
132        return parse_drag(l, loc)
133
134    elif l.keyword("move"):
135        return parse_move(l, loc)
136
137    elif l.keyword("click"):
138        return parse_click(l, loc, None)
139
140    elif l.keyword("scroll"):
141        pattern = l.require(l.string)
142        return testast.Scroll(loc, pattern)
143
144    else:
145        target = l.string()
146        if target:
147            return parse_click(l, loc, target)
148
149    l.error("Expected a test language statement or clause.")
150    return testast.Click(loc, target)
151
152
153def parse_statement(l, loc):
154
155    if l.keyword('python'):
156
157        l.require(':')
158
159        l.expect_block("python block")
160
161        source = l.python_block()
162
163        code = renpy.ast.PyCode(source, loc)
164        return testast.Python(loc, code)
165
166    if l.keyword("if"):
167        l.expect_block("if block")
168
169        condition = parse_clause(l, loc)
170        l.require(':')
171        block = parse_block(l.subblock_lexer(False), loc)
172
173        return testast.If(loc, condition, block)
174
175    # Single-line statements only below here.
176
177    l.expect_noblock('statement')
178
179    if l.match(r'\$'):
180
181        source = l.require(l.rest)
182
183        code = renpy.ast.PyCode(source, loc)
184        return testast.Python(loc, code)
185
186    elif l.keyword('assert'):
187        source = l.require(l.rest)
188        return testast.Assert(loc, source)
189
190    elif l.keyword('jump'):
191        target = l.require(l.name)
192        return testast.Jump(loc, target)
193
194    elif l.keyword('call'):
195        target = l.require(l.name)
196        return testast.Call(loc, target)
197
198    rv = parse_clause(l, loc)
199
200    if l.keyword("until"):
201        right = parse_clause(l, loc)
202        rv = testast.Until(loc, rv, right)
203
204    return rv
205
206
207def parse_block(l, loc):
208    """
209    Parses a named block of testcase statements.
210    """
211
212    block = [ ]
213
214    while l.advance():
215        stmt = parse_statement(l, l.get_location())
216        block.append(stmt)
217
218        l.expect_eol()
219
220    return testast.Block(loc, block)
221