Skip to content

Commit 96e68eb

Browse files
authored
Merge pull request #3 from josefalanga/main
Implement substitutions
2 parents 4d4edae + 23b8bf2 commit 96e68eb

6 files changed

Lines changed: 47 additions & 33 deletions

File tree

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ A few gotchas to look out for:
6262

6363
As of version v0.0.2, all Yarn Spinner opcodes are currently implemented, as well as Yarn Spinner 1's internal standard library of functions and operators. As of version v0.2.1, typed versions of these functions (introduced in Yarn Spinner 2) are present, but full YS2 parity has not been verified at this time. The known features currently missing are:
6464

65-
- Inline expressions [(see Yarn docs on "Using Variables in Lines")](https://docs.yarnspinner.dev/getting-started/writing-in-yarn/logic-and-variables#using-variables-in-lines)
6665
- Line conditions and the `IsAvailable` flag on options [(see Yarn Docs on "Conditional Options")](https://docs.yarnspinner.dev/getting-started/writing-in-yarn/flow-control#conditional-options)
6766
- Localisation and Line IDs [(see Yarn's Localization docs)](https://docs.yarnspinner.dev/using-yarnspinner-with-unity/assets-and-localization)
6867
- An appropriate replacement for the distinction Yarn makes between Functions and Coroutines in Unity (to allow users to register blocking command handlers via this Python runner independent of Unity)

examples/yarn2/expressions.csv

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
id,text,file,node,lineNumber
2-
line:/Users/sweaver/Git/YarnRunner-Python/examples/yarn2/expressions.yarn-Start-0,Hello there {0}.,/Users/sweaver/Git/YarnRunner-Python/examples/yarn2/expressions.yarn,Start,5
2+
line:C:\Documents\development\YarnRunner-Python\examples\yarn2\expressions.yarn-Start-0,My name is {0}.,C:\Documents\development\YarnRunner-Python\examples\yarn2\expressions.yarn,Start,7
3+
line:C:\Documents\development\YarnRunner-Python\examples\yarn2\expressions.yarn-Start-1,I want to hug {0},C:\Documents\development\YarnRunner-Python\examples\yarn2\expressions.yarn,Start,13
4+
line:C:\Documents\development\YarnRunner-Python\examples\yarn2\expressions.yarn-Start-2,You punch {0},C:\Documents\development\YarnRunner-Python\examples\yarn2\expressions.yarn,Start,14
5+
line:C:\Documents\development\YarnRunner-Python\examples\yarn2\expressions.yarn-Start-3,I want to punch {0},C:\Documents\development\YarnRunner-Python\examples\yarn2\expressions.yarn,Start,15
6+
line:C:\Documents\development\YarnRunner-Python\examples\yarn2\expressions.yarn-Start-4,You hug {0},C:\Documents\development\YarnRunner-Python\examples\yarn2\expressions.yarn,Start,16

examples/yarn2/expressions.yarn

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
title: Start
22
---
33
<<set $name to "Sam">>
4+
<<set $friend to "Jose">>
45

5-
Hello there {$name}.
6+
//simple expression in line
7+
My name is {$name}.
8+
9+
//in custom command parameters
10+
<<test_command "Hello there {$name}." 5 "My name is {$name} {$friend}.">>
11+
12+
//in options
13+
-> I want to hug {$friend}
14+
You punch {$friend}
15+
-> I want to punch {$friend}
16+
You hug {$friend}
617
===

examples/yarn2/expressions.yarnc

836 Bytes
Binary file not shown.

tests/test_expressions.py

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,26 @@
1313
runner1 = YarnRunner(compiled_yarn_f1, names_csv_f1, autostart=False)
1414
runner2 = YarnRunner(compiled_yarn_f2, names_csv_f2, autostart=False)
1515

16-
# TODO: implement a test for expression parsing
1716

17+
def expression_command_handler(first, second, third):
18+
assert first == "Hello there Sam."
19+
assert second == "5"
20+
assert third == "My name is Sam Jose."
1821

1922
def test_expressions1():
20-
try:
21-
runner1.resume()
23+
expected_line = "Hello there Sam."
2224

23-
# the runner should throw an error
24-
raise Exception(
25-
"The runner ran without any issues. This test should fail. An Exception was expected.")
26-
except Exception as e:
27-
assert str(
28-
e) == "Yarn stories with interpolated inline expressions are not yet supported."
25+
runner1.resume()
2926

27+
assert runner1.get_line() == expected_line
3028

3129
def test_expressions2():
32-
try:
33-
runner2.resume()
34-
35-
# the runner should throw an error
36-
raise Exception(
37-
"The runner ran without any issues. This test should fail. An Exception was expected.")
38-
except Exception as e:
39-
assert str(
40-
e) == "Yarn stories with interpolated inline expressions are not yet supported."
30+
runner2.add_command_handler("test_command", expression_command_handler)
31+
expected_line = "My name is Sam."
32+
33+
runner2.resume()
34+
choices = runner2.get_choices()
35+
36+
assert runner2.get_line() == expected_line
37+
assert choices[0]['text'] == "I want to hug Jose"
38+
assert choices[1]['text'] == "I want to punch Jose"

yarnrunner_python/runner.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,13 @@ def __find_label(self, label_key):
7272
f"The current node `{self.current_node}` does not have a label named `{label_key}")
7373

7474
def __find_expressions(self, operand):
75-
# TODO: implement this functionality
76-
if int(operand.float_value) != 0:
77-
raise Exception(
78-
f"Yarn stories with interpolated inline expressions are not yet supported.")
75+
params_amount = operand.float_value
76+
77+
params = []
78+
while params_amount > 0:
79+
params.insert(0, self._vm_data_stack.pop(0))
80+
params_amount -= 1
81+
return params
7982

8083
def __debug_log(self, msg, **kwargs):
8184
if self._enable_tracing:
@@ -202,10 +205,9 @@ def __run_line(self, instruction):
202205

203206
# if this instruction has a second operand, it's the number of expressions
204207
# on the line that need to be evaluated.
208+
line_substitutions = []
205209
if len(instruction.operands) > 1:
206-
line_substitutions = self.__find_expressions(
207-
instruction.operands[1])
208-
# TODO: implement substitutions
210+
line_substitutions = self.__find_expressions(instruction.operands[1])
209211

210212
if self._experimental_newlines:
211213
# attempt to add a newlines if the last thing we did was run a line
@@ -219,7 +221,7 @@ def __run_line(self, instruction):
219221
for _i in range(diff - 1):
220222
self._line_buffer.append('')
221223

222-
self._line_buffer.append(self.__lookup_string(string_key))
224+
self._line_buffer.append(self.__lookup_string(string_key).format(*line_substitutions))
223225

224226
def __run_command(self, instruction):
225227
# split the command specifier by spaces, ignoring spaces
@@ -248,9 +250,9 @@ def sanitize_quotes(arg):
248250
if len(instruction.operands) > 1:
249251
line_substitutions = self.__find_expressions(
250252
instruction.operands[1])
251-
# TODO: implement substitutions
253+
for index, arg in enumerate(args):
254+
args[index] = arg.format(*line_substitutions)
252255

253-
# TODO: maybe do some argument type parsing later
254256
ret = self._command_handlers[command](*args)
255257

256258
if type(ret) is str:
@@ -262,14 +264,14 @@ def __add_option(self, instruction):
262264

263265
# if this instruction has a second operand, it's the number of expressions
264266
# on the line that need to be evaluated.
267+
line_substitutions = []
265268
if len(instruction.operands) > 2:
266269
line_substitutions = self.__find_expressions(
267270
instruction.operands[2])
268-
# TODO: implement substitutions
269271

270272
self._option_buffer.append({
271273
'index': len(self._option_buffer),
272-
'text': self.__lookup_string(title_string_key),
274+
'text': self.__lookup_string(title_string_key).format(*line_substitutions),
273275
'choice': choice_path
274276
})
275277

0 commit comments

Comments
 (0)