Skip to content

Commit

Permalink
Add warnings for unimplemented features. Update README.
Browse files Browse the repository at this point in the history
  • Loading branch information
weaversam8 committed Dec 10, 2021
1 parent e0296b0 commit 2fdf3f6
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 28 deletions.
25 changes: 4 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,27 +50,10 @@ A few gotchas to look out for:
- Make sure to open the compiled story file as a binary file (see the above example, use `open(filename, 'rb')`) in order for it to be properly parsed by the compiled protobuf library.
- Unless you pass `autostart=False` to the runner when creating it, it will automatically start and run to the next choice point.

All Yarn Spinner opcodes are currently implemented. This may certainly change over time, if new opcodes are added to the language. The current status is:

| OpCode | Status |
| ---------------- | ------------------------------------------------------ |
| `JUMP_TO` |  Implemented in `runner.__jump_to` |
| `JUMP` |  Implemented in `runner.__jump` |
| `RUN_LINE` |  Implemented in `runner.__run_line` |
| `RUN_COMMAND` |  Implemented in `runner.__run_command` |
| `ADD_OPTION` |  Implemented in `runner.__add_option` |
| `SHOW_OPTIONS` |  Implemented in `runner.__show_options` |
| `PUSH_STRING` |  Implemented in `runner.__push_string` |
| `PUSH_FLOAT` |  Implemented in `runner.__push_float` |
| `PUSH_BOOL` |  Implemented in `runner.__push_bool` |
| `PUSH_NULL` |  Implemented in `runner.__push_null` |
| `JUMP_IF_FALSE` |  Implemented in `runner.__jump_if_false` |
| `POP` |  Implemented in `runner.__pop` |
| `CALL_FUNC` |  Implemented in `runner.__call_func` |
| `PUSH_VARIABLE` |  Implemented in `runner.__push_variable` |
| `STORE_VARIABLE` |  Implemented in `runner.__store_variable` |
| `STOP` |  Implemented in `runner.__stop` |
| `RUN_NODE` |  Implemented in `runner.__run_node` |
As of version 2.0, all Yarn Spinner opcodes are currently implemented, as well as Yarn's internal standard library of functions and operators. This may certainly change over time, if new opcodes, functions, or operators are added to the language. The current missing features are:

- Inline expressions [(see Yarn docs)](https://yarnspinner.dev/docs/writing/expressions-and-variables/#inline-expressions)
- Localisation tags and Format functions [(see Yarn syntax reference)](https://yarnspinner.dev/docs/syntax/#localisation-tags)

## Development

Expand Down
2 changes: 2 additions & 0 deletions examples/expressions.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
id,text,file,node,lineNumber
expressions-Start-0,Hello there {0}.,expressions,Start,5
6 changes: 6 additions & 0 deletions examples/expressions.yarn
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
title: Start
---
<<set $name to "Sam">>

Hello there {$name}.
===
Binary file added examples/expressions.yarnc
Binary file not shown.
23 changes: 23 additions & 0 deletions tests/test_expressions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import os
from .context import YarnRunner

compiled_yarn_f = open(os.path.join(os.path.dirname(
__file__), '../examples/expressions.yarnc'), 'rb')
names_csv_f = open(os.path.join(os.path.dirname(
__file__), '../examples/expressions.csv'), 'r')

runner = YarnRunner(compiled_yarn_f, names_csv_f, autostart=False)

# TODO: implement a test for expression parsing


def test_expressions():
try:
runner.resume()

# the runner should throw an error
raise Exception(
"The runner ran without any issues. This test should fail. An Exception was expected.")
except Exception as e:
assert str(
e) == "Yarn stories with interpolated inline expressions are not yet supported."
43 changes: 36 additions & 7 deletions yarnrunner_python/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ def __find_label(self, label_key):
raise Exception(
f"The current node `{self.current_node}` does not have a label named `{label_key}")

def __find_expressions(self, operand):
# TODO: implement this functionality
if int(operand.float_value) != 0:
raise Exception(
f"Yarn stories with interpolated inline expressions are not yet supported.")

def __debug_log(self, msg, **kwargs):
if self._enable_tracing:
print(msg, **kwargs)
Expand Down Expand Up @@ -160,21 +166,43 @@ def __go_to_node(self, node_key):
def __run_line(self, instruction):
string_key = instruction.operands[0].string_value

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

self._line_buffer.append(self.__lookup_string(string_key))

def __run_command(self, instruction):
command, *args = instruction.operands[0].string_value.split(" ")

if command not in self._command_handlers.keys():
warn(
f"Command '{command}' does not have a registered command handler.")
else:
# TODO: maybe do some argument parsing later
# if this instruction has a second operand, it's the number of expressions
# on the line that need to be evaluated.
if len(instruction.operands) > 1:
line_substitutions = self.__find_expressions(
instruction.operands[1])
# TODO: implement substitutions

# TODO: maybe do some argument type parsing later
self._command_handlers[command](*args)

def __add_option(self, instruction):
title_string_key = instruction.operands[0].string_value
choice_path = instruction.operands[1].string_value

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

self._option_buffer.append({
'index': len(self._option_buffer),
'text': self.__lookup_string(title_string_key),
Expand Down Expand Up @@ -281,12 +309,6 @@ def __process_instruction(self):

instruction = self._vm_instruction_stack[self._program_counter]

def noop(instruction):
if (len(instruction.operands) > 0):
print(instruction.operands)
raise Exception(
f"OpCode {Instruction.OpCode.Name(instruction.opcode)} is not yet implemented")

opcode_functions = {
Instruction.OpCode.JUMP_TO: self.__jump_to,
Instruction.OpCode.JUMP: self.__jump,
Expand All @@ -308,6 +330,13 @@ def noop(instruction):
}

self._program_counter += 1

if instruction.opcode not in opcode_functions:
if (len(instruction.operands) > 0):
print(instruction.operands)
raise Exception(
f"OpCode {Instruction.OpCode.Name(instruction.opcode)} is not yet implemented")

opcode_functions[instruction.opcode](instruction)

if not self.paused and not self.finished:
Expand Down

0 comments on commit 2fdf3f6

Please sign in to comment.