did a bunch things to finalize

This commit is contained in:
Bram Dingelstad 2021-11-21 23:10:48 +01:00
parent bad1dcb372
commit c4868130eb
14 changed files with 345 additions and 138 deletions

View file

@ -16,6 +16,8 @@ __meta__ = {
script = ExtResource( 2 )
path = "res://dialogue.yarn"
auto_start = true
variable_storage = {
}
[node name="RichTextLabel" type="RichTextLabel" parent="."]
anchor_right = 1.0

View file

@ -17,15 +17,17 @@ signal started
signal finished
export(String, FILE, '*.wol,*.yarn') var path setget set_path
export(String) var start_node = 'Start'
export(bool) var auto_start = false
export var starting_node = 'Start'
export var auto_start = false
export var auto_show_options = true
export var auto_substitute = true
export(Dictionary) var variable_storage = {}
const Constants = preload('res://addons/Wol/core/Constants.gd')
const WolCompiler = preload('res://addons/Wol/core/compiler/Compiler.gd')
const WolLibrary = preload('res://addons/Wol/core/Library.gd')
const Compiler = preload('res://addons/Wol/core/compiler/Compiler.gd')
const Library = preload('res://addons/Wol/core/Library.gd')
const VirtualMachine = preload('res://addons/Wol/core/VirtualMachine.gd')
const StandardLibrary = preload('res://addons/Wol/core/StandardLibrary.gd')
@ -35,10 +37,11 @@ func _ready():
if Engine.editor_hint:
return
var libraries = WolLibrary.new()
libraries.import_library(StandardLibrary.new())
var libraries = Library.new()
virtual_machine = VirtualMachine.new(self, libraries)
libraries.import_library(StandardLibrary.new())
set_path(path)
if auto_start:
@ -48,10 +51,16 @@ func set_path(_path):
path = _path
if not Engine.editor_hint and virtual_machine:
var compiler = WolCompiler.new(path)
var compiler = Compiler.new(path)
virtual_machine.program = compiler.compile()
func _on_line(line):
if auto_substitute:
var index = 0
for substitute in line.substitutions:
line.text = line.text.replace('{%d}' % index, substitute)
index += 1
call_deferred('emit_signal', 'line', line)
if auto_show_options \
and virtual_machine.get_next_instruction().operation == Constants.ByteCode.AddOption:
@ -88,7 +97,7 @@ func select_option(id):
func pause():
virtual_machine.call_deferred('pause')
func start(node = start_node):
func start(node = starting_node):
emit_signal('started')
virtual_machine.set_node(node)

View file

@ -1,5 +1,6 @@
extends Object
var Value : GDScript = load("res://addons/Wol/core/Value.gd")
const Value = preload("res://addons/Wol/core/Value.gd")
var name = ''
# NOTE: -1 means variable arguments
@ -18,12 +19,18 @@ func invoke(parameters = []):
if parameters != null:
length = parameters.size()
if check_param_count(length):
if check_parameter_count(length):
if returns_value:
var return_value
if length > 0:
return Value.new(function.call_funcv(parameters))
return_value = function.call_funcv(parameters)
else:
return Value.new(function.call_func())
return_value = function.call_func()
if return_value is Value:
return return_value
else:
return Value.new(return_value)
else:
if length > 0:
function.call_funcv(parameters)
@ -31,5 +38,5 @@ func invoke(parameters = []):
function.call_func()
return null
func check_param_count(_parameter_count):
func check_parameter_count(_parameter_count):
return parameter_count == _parameter_count or parameter_count == -1

View file

@ -72,25 +72,14 @@ func xor(param1, param2):
func lnot(param1):
return not param1.as_bool()
var visited_node_count = {}
func is_node_visited(node = virtual_machine.current_node_name()):
func is_node_visited(node = virtual_machine.current_node.name):
return node_visit_count(node) > 0
func node_visit_count(node = virtual_machine.current_node_name()):
func node_visit_count(node = virtual_machine.current_node.name):
if node is Value:
node = virtual_machine.program.strings[node.value()].text
var visit_count = 0
if visited_node_count.has(node):
visit_count = visited_node_count[node]
var variable_storage = virtual_machine.dialogue.variable_storage
var visited_node_count = variable_storage[virtual_machine.program.filename]
return visit_count
func get_visited_nodes():
return visited_node_count.keys()
func set_visited_nodes(visitedList):
visited_node_count.clear()
for string in visitedList:
visited_node_count[string] = 1
return visited_node_count[node] if visited_node_count.has(node) else 0

View file

@ -1,6 +1,7 @@
extends Node
extends Object
const Constants = preload('res://addons/Wol/core/Constants.gd')
const Program = preload('res://addons/Wol/core/Program.gd')
const Value = preload('res://addons/Wol/core/Value.gd')
# Function references to handlers
@ -59,8 +60,17 @@ func set_node(name):
current_node = program.nodes[name]
reset()
state.current_node_name = name
node_start_handler.call_func(name)
if not dialogue.variable_storage.has(program.filename):
dialogue.variable_storage[program.filename] = {}
if not dialogue.variable_storage[program.filename].has(name):
dialogue.variable_storage[program.filename][name] = 0
dialogue.variable_storage[program.filename][name] += 1
return true
func pause():
@ -91,8 +101,8 @@ func reset():
state = VmState.new()
func get_next_instruction():
if current_node.instructions.size() - 1 > state.programCounter:
return current_node.instructions[state.programCounter + 1]
if current_node.instructions.size() - 1 > state.program_counter:
return current_node.instructions[state.program_counter + 1]
return
func start():
@ -115,14 +125,13 @@ func resume():
execution_state = Constants.ExecutionState.Running
#execute instruction until something cool happens
while execution_state == Constants.ExecutionState.Running:
var current_instruction = current_node.instructions[state.programCounter]
var current_instruction = current_node.instructions[state.program_counter]
run_instruction(current_instruction)
state.programCounter += 1
state.program_counter += 1
if state.programCounter >= current_node.instructions.size():
node_finished_handler.call_func(current_node.nodeName)
if state.program_counter >= current_node.instructions.size():
node_finished_handler.call_func(current_node.name)
execution_state = Constants.ExecutionState.Stopped
reset()
dialogue_finished_handler.call_func()
@ -142,17 +151,22 @@ func run_instruction(instruction):
# Jump to named label
Constants.ByteCode.JumpTo:
state.programCounter = find_label_instruction(instruction.operands[0].value) - 1
state.program_counter = find_label_instruction(instruction.operands[0].value) - 1
Constants.ByteCode.RunLine:
# Lookup string and give back as line
var key = instruction.operands[0].value
var line = program.strings[key]
var line = program.strings[key].clone()
# The second operand is the expression count of format function
# TODO: Add format functions supportk
# TODO: Add format functions support
line.substitutions = []
if instruction.operands.size() > 1:
pass
var expression_count = int(instruction.operands[1].value)
while expression_count > 0:
line.substitutions.append(state.pop_value().as_string())
expression_count -= 1
var return_state = line_handler.call_func(line)
@ -177,12 +191,12 @@ func run_instruction(instruction):
# Jump to named label if value of stack top is false
Constants.ByteCode.JumpIfFalse:
if not state.peek_value().as_bool():
state.programCounter = find_label_instruction(instruction.operands[0].value) - 1
state.program_counter = find_label_instruction(instruction.operands[0].value) - 1
# Jump to label whose name is on the stack
Constants.ByteCode.Jump:
var destination = state.peek_value().as_string()
state.programCounter = find_label_instruction(destination) - 1
state.program_counter = find_label_instruction(destination) - 1
Constants.ByteCode.Pop:
state.pop_value()
@ -204,25 +218,29 @@ func run_instruction(instruction):
if actual_parameter_count == 0:
result = function.invoke()
else:
var params = []
var parameters = []
for _i in range(actual_parameter_count):
params.push_front(state.pop_value())
parameters.push_front(state.pop_value())
result = function.invoke(params)
result = function.invoke(parameters)
if function.returns_value:
state.push_value(result)
Constants.ByteCode.PushVariable:
var name = instruction.operands[0].value
var godot_value = dialogue.variable_storage[name]
var value = Value.new(godot_value)
var value = dialogue.variable_storage[name.replace('$', '')]
state.push_value(value)
Constants.ByteCode.StoreVariable:
var value = state.peek_value()
if value.type == Constants.ValueType.Str:
value = program.strings[value.value()].text
else:
value = value.value()
var name = instruction.operands[0].value.replace('$', '')
dialogue.variable_storage[name] = value.value()
dialogue.variable_storage[name] = value
Constants.ByteCode.Stop:
node_finished_handler.call_func(current_node.name)
@ -239,15 +257,15 @@ func run_instruction(instruction):
var return_state = node_finished_handler.call_func(current_node.name)
set_node(name)
state.programCounter -= 1
if return_state == Constants.HandlerState.PauseExecution:
execution_state = Constants.ExecutionState.Suspended
Constants.ByteCode.AddOption:
var key = instruction.operands[0].value
var line = program.strings[key]
var line = program.strings[key].clone()
# TODO: Add format functions supportk
# TODO: Add format functions support
if instruction.operands.size() > 2:
pass
@ -280,11 +298,11 @@ func run_instruction(instruction):
class VmState:
var current_node_name = ''
var programCounter = 0
var program_counter = 0
var current_options = []
var stack = []
func push_value(value)->void:
func push_value(value):
if value is Value:
stack.push_back(value)
else:

View file

@ -79,7 +79,6 @@ func compile():
line_number += 1
body = PoolStringArray(body_lines).join('\n')
var lexer = Lexer.new(filename, title, body)
var tokens = lexer.tokenize()
@ -94,6 +93,7 @@ func compile():
line_number += 1
var program = Program.new()
program.filename = filename
for node in parsed_nodes:
compile_node(program, node)
@ -203,7 +203,7 @@ func generate_statement(node, statement):
generate_assignment(node, statement.assignment)
Constants.StatementTypes.Line:
generate_line(node, statement,statement.line)
generate_line(node, statement)
_:
assert(false, 'Illegal statement type [%s]. Could not generate code.' % statement.type)
@ -218,10 +218,17 @@ func generate_custom_command(node, command):
else :
emit(Constants.ByteCode.RunCommand, node, [Program.Operand.new(command_string)])
func generate_line(node, statement, line):
func generate_line(node, statement):
# TODO: Implement proper line numbers (global and local)
var num = register_string(line, node.name, '', statement.line_number, []);
emit(Constants.ByteCode.RunLine, node, [Program.Operand.new(num)])
var line = statement.line
var expression_count = line.substitutions.size()
while not line.substitutions.empty():
var inline_expression = line.substitutions.pop_back()
generate_expression(node, inline_expression.expression)
var num = register_string(line.line_text, node.name, line.line_id, statement.line_number, line.tags);
emit(Constants.ByteCode.RunLine, node,[Program.Operand.new(num), Program.Operand.new(expression_count)])
func generate_shortcut_group(node, shortcut_group):
var end = register_label('group_end')
@ -243,7 +250,8 @@ func generate_shortcut_group(node,shortcut_group):
var label_string_id = register_string(
option.label,
node.name,
label_line_id,option.line_number,
label_line_id,
option.line_number,
[]
)

View file

@ -17,6 +17,8 @@ const ASSIGNMENT = 'assignment'
const OPTION = 'option'
const OR = 'or'
const DESTINATION = 'destination'
const INLINE = 'inline'
const FORMAT_FUNCTION = 'format'
var WHITESPACE = '\\s*'
@ -84,27 +86,40 @@ func createstates():
patterns[Constants.TokenType.EndIf] = ['endif(?!\\w)', '"endif"']
patterns[Constants.TokenType.Set] = ['set(?!\\w)', '"set"']
patterns[Constants.TokenType.ShortcutOption] = ['\\-\\>\\s*', '"->"']
patterns[Constants.TokenType.ExpressionFunctionStart] = ['\\{', '"{"']
patterns[Constants.TokenType.ExpressionFunctionEnd] = ['\\}', '"}"']
patterns[Constants.TokenType.FormatFunctionStart] = ['(?<!\\[)\\[(?!\\[)', '"["']
patterns[Constants.TokenType.FormatFunctionEnd] = ['\\]', '"]"']
var shortcut_option = SHORTCUT + DASH + OPTION
var shortcut_option_tag = shortcut_option + DASH + TAG
var command_or_expression = COMMAND + DASH + OR + DASH + EXPRESSION
var link_destination = LINK + DASH + DESTINATION
var format_expression = FORMAT_FUNCTION + DASH + EXPRESSION
var inline_expression = INLINE + DASH + EXPRESSION
var link_inline_expression = LINK + DASH + INLINE + DASH + EXPRESSION
var link_format_expression = LINK + DASH + FORMAT_FUNCTION + DASH + EXPRESSION
states = {}
states[BASE] = LexerState.new(patterns)
states[BASE].add_transition(Constants.TokenType.BeginCommand, COMMAND, true)
states[BASE].add_transition(Constants.TokenType.ExpressionFunctionStart, inline_expression, true)
states[BASE].add_transition(Constants.TokenType.FormatFunctionStart, FORMAT_FUNCTION, true)
states[BASE].add_transition(Constants.TokenType.OptionStart, LINK, true)
states[BASE].add_transition(Constants.TokenType.ShortcutOption, shortcut_option)
states[BASE].add_transition(Constants.TokenType.TagMarker, TAG, true)
states[BASE].add_text_rule(Constants.TokenType.Text)
#TODO: FIXME - Tags are not being proccessed properly this way. We must look for the format #{identifier}:{value}
# Possible solution is to add more transitions
states[TAG] = LexerState.new(patterns)
states[TAG].add_transition(Constants.TokenType.Identifier, BASE)
states[shortcut_option] = LexerState.new(patterns)
states[shortcut_option].track_indent = true
states[shortcut_option].add_transition(Constants.TokenType.BeginCommand, EXPRESSION, true)
states[shortcut_option].add_transition(Constants.TokenType.ExpressionFunctionStart, inline_expression, true)
states[shortcut_option].add_transition(Constants.TokenType.TagMarker, shortcut_option_tag, true)
states[shortcut_option].add_text_rule(Constants.TokenType.Text, BASE)
@ -134,40 +149,42 @@ func createstates():
states[ASSIGNMENT].add_transition(Constants.TokenType.MultiplyAssign, EXPRESSION)
states[ASSIGNMENT].add_transition(Constants.TokenType.DivideAssign, EXPRESSION)
states[FORMAT_FUNCTION] = LexerState.new(patterns)
states[FORMAT_FUNCTION].add_transition(Constants.TokenType.FormatFunctionEnd, BASE, true)
states[FORMAT_FUNCTION].add_transition(Constants.TokenType.ExpressionFunctionStart, format_expression, true)
states[FORMAT_FUNCTION].add_text_rule(Constants.TokenType.Text)
states[format_expression] = LexerState.new(patterns)
states[format_expression].add_transition(Constants.TokenType.ExpressionFunctionEnd, FORMAT_FUNCTION)
form_expression_state(states[format_expression])
states[inline_expression] = LexerState.new(patterns)
states[inline_expression].add_transition(Constants.TokenType.ExpressionFunctionEnd, BASE)
form_expression_state(states[inline_expression])
states[EXPRESSION] = LexerState.new(patterns)
states[EXPRESSION].add_transition(Constants.TokenType.EndCommand, BASE)
states[EXPRESSION].add_transition(Constants.TokenType.Number)
states[EXPRESSION].add_transition(Constants.TokenType.Str)
states[EXPRESSION].add_transition(Constants.TokenType.LeftParen)
states[EXPRESSION].add_transition(Constants.TokenType.RightParen)
states[EXPRESSION].add_transition(Constants.TokenType.EqualTo)
states[EXPRESSION].add_transition(Constants.TokenType.EqualToOrAssign)
states[EXPRESSION].add_transition(Constants.TokenType.NotEqualTo)
states[EXPRESSION].add_transition(Constants.TokenType.GreaterThanOrEqualTo)
states[EXPRESSION].add_transition(Constants.TokenType.GreaterThan)
states[EXPRESSION].add_transition(Constants.TokenType.LessThanOrEqualTo)
states[EXPRESSION].add_transition(Constants.TokenType.LessThan)
states[EXPRESSION].add_transition(Constants.TokenType.Add)
states[EXPRESSION].add_transition(Constants.TokenType.Minus)
states[EXPRESSION].add_transition(Constants.TokenType.Multiply)
states[EXPRESSION].add_transition(Constants.TokenType.Divide)
states[EXPRESSION].add_transition(Constants.TokenType.Modulo)
states[EXPRESSION].add_transition(Constants.TokenType.And)
states[EXPRESSION].add_transition(Constants.TokenType.Or)
states[EXPRESSION].add_transition(Constants.TokenType.Xor)
states[EXPRESSION].add_transition(Constants.TokenType.Not)
states[EXPRESSION].add_transition(Constants.TokenType.Variable)
states[EXPRESSION].add_transition(Constants.TokenType.Comma)
states[EXPRESSION].add_transition(Constants.TokenType.TrueToken)
states[EXPRESSION].add_transition(Constants.TokenType.FalseToken)
states[EXPRESSION].add_transition(Constants.TokenType.NullToken)
states[EXPRESSION].add_transition(Constants.TokenType.Identifier)
# states[EXPRESSION].add_transition(Constants.TokenType.FormatFunctionEnd, BASE)
form_expression_state(states[EXPRESSION])
states[LINK] = LexerState.new(patterns)
states[LINK].add_transition(Constants.TokenType.OptionEnd, BASE, true)
states[LINK].add_transition(Constants.TokenType.ExpressionFunctionStart, link_inline_expression, true)
states[LINK].add_transition(Constants.TokenType.FormatFunctionStart, link_format_expression, true)
states[LINK].add_transition(Constants.TokenType.FormatFunctionEnd, LINK, true)
states[LINK].add_transition(Constants.TokenType.OptionDelimit, link_destination, true)
states[LINK].add_text_rule(Constants.TokenType.Text)
states[link_format_expression] = LexerState.new(patterns)
states[link_format_expression].add_transition(Constants.TokenType.FormatFunctionEnd, LINK, true)
states[link_format_expression].add_transition(Constants.TokenType.ExpressionFunctionStart, link_inline_expression, true)
states[link_format_expression].add_text_rule(Constants.TokenType.Text)
states[link_inline_expression] = LexerState.new(patterns)
states[link_inline_expression].add_transition(Constants.TokenType.ExpressionFunctionEnd, LINK)
form_expression_state(states[link_inline_expression])
states[link_destination] = LexerState.new(patterns)
states[link_destination].add_transition(Constants.TokenType.Identifier)
states[link_destination].add_transition(Constants.TokenType.OptionEnd, BASE)
@ -177,6 +194,34 @@ func createstates():
for key in states.keys():
states[key].name = key
func form_expression_state(expression_state):
expression_state.add_transition(Constants.TokenType.Number)
expression_state.add_transition(Constants.TokenType.Str)
expression_state.add_transition(Constants.TokenType.LeftParen)
expression_state.add_transition(Constants.TokenType.RightParen)
expression_state.add_transition(Constants.TokenType.EqualTo)
expression_state.add_transition(Constants.TokenType.EqualToOrAssign)
expression_state.add_transition(Constants.TokenType.NotEqualTo)
expression_state.add_transition(Constants.TokenType.GreaterThanOrEqualTo)
expression_state.add_transition(Constants.TokenType.GreaterThan)
expression_state.add_transition(Constants.TokenType.LessThanOrEqualTo)
expression_state.add_transition(Constants.TokenType.LessThan)
expression_state.add_transition(Constants.TokenType.Add)
expression_state.add_transition(Constants.TokenType.Minus)
expression_state.add_transition(Constants.TokenType.Multiply)
expression_state.add_transition(Constants.TokenType.Divide)
expression_state.add_transition(Constants.TokenType.Modulo)
expression_state.add_transition(Constants.TokenType.And)
expression_state.add_transition(Constants.TokenType.Or)
expression_state.add_transition(Constants.TokenType.Xor)
expression_state.add_transition(Constants.TokenType.Not)
expression_state.add_transition(Constants.TokenType.Variable)
expression_state.add_transition(Constants.TokenType.Comma)
expression_state.add_transition(Constants.TokenType.TrueToken)
expression_state.add_transition(Constants.TokenType.FalseToken)
expression_state.add_transition(Constants.TokenType.NullToken)
expression_state.add_transition(Constants.TokenType.Identifier)
func tokenize():
var tokens = []

View file

@ -87,8 +87,6 @@ class ParseNode:
var tokens = _parser.tokens as Array
if tokens.size() > 0:
line_number = tokens.front().line_number
else:
line_number = -1
tags = []
@ -132,6 +130,105 @@ class WolNode extends ParseNode:
class Header extends ParseNode:
pass
class InlineExpression extends ParseNode:
var expression
func _init(parent, parser).(parent, parser):
parser.expect_symbol([Constants.TokenType.ExpressionFunctionStart])
expression = ExpressionNode.parse(self, parser)
parser.expect_symbol([Constants.TokenType.ExpressionFunctionEnd])
static func can_parse(parser):
return parser.next_symbol_is([Constants.TokenType.ExpressionFunctionStart])
func tree_string(_indent_level):
return "InlineExpression:"
# Returns a format_text string as [ name "{0}" key1="value1" key2="value2" ]
class FormatFunctionNode extends ParseNode:
var format_text = ''
var expression_value
func _init(parent:ParseNode, parser, expressionCount:int).(parent, parser):
format_text="["
parser.expect_symbol([Constants.TokenType.FormatFunctionStart])
while !parser.next_symbol_is([Constants.TokenType.FormatFunctionEnd]):
if parser.next_symbol_is([Constants.TokenType.Text]):
format_text += parser.expect_symbol().value
if InlineExpression.can_parse(parser):
expression_value = InlineExpression.new(self, parser)
format_text +=" \"{%d}\" " % expressionCount
parser.expect_symbol()
format_text+="]"
static func can_parse(parser):
return parser.next_symbol_is([Constants.TokenType.FormatFunctionStart])
# TODO: Make format prettier and add more information
func tree_string(_indent_level):
return "FormatFunction"
class LineNode extends ParseNode:
var line_text = ''
# FIXME: Right now we are putting the formatfunctions and inline expressions in the same
# list but if at some point we want to stronly type our sub list we need to make a new
# parse node that can have either an InlineExpression or a FunctionFormat
# .. This is a consideration for Godot4.x
var substitutions = []
var line_id = ''
var line_tags = []
# NOTE: If format function an inline functions are both present
# returns a line in the format "Some text {0} and some other {1}[format "{2}" key="value" key="value"]"
func _init(parent, parser).(parent, parser):
while parser.next_symbol_is(
[
Constants.TokenType.FormatFunctionStart,
Constants.TokenType.ExpressionFunctionStart,
Constants.TokenType.Text,
Constants.TokenType.TagMarker
]
):
if FormatFunctionNode.can_parse(parser):
var format_function = FormatFunctionNode.new(self, parser, substitutions.size())
if format_function.expression_value != null:
substitutions.append(format_function.expression_value)
line_text += format_function.format_text
elif InlineExpression.can_parse(parser):
var inline_expression = InlineExpression.new(self, parser)
line_text += '{%d}' % substitutions.size()
substitutions.append(inline_expression)
elif parser.next_symbols_are([Constants.TokenType.TagMarker, Constants.TokenType.Identifier]):
parser.expect_symbol()
var tag_token = parser.expect_symbol([ Constants.TokenType.Identifier ])
if tag_token.value.begins_with("line:"):
if line_id.empty():
line_id = tag_token.value
else:
printerr("Too many line_tags @[%s:%d]" %[parser.currentNodeName, tag_token.line_number])
return
else:
tags.append(tag_token.value)
else:
var token = parser.expect_symbol()
if token.line_number == line_number and token.type != Constants.TokenType.BeginCommand:
line_text += token.value
else:
parser.tokens.push_front(token)
break
func tree_string(indent_level):
return tab(indent_level, 'Line: (%s)[%d]' % [line_text, substitutions.size()])
class Statement extends ParseNode:
var Type = Constants.StatementTypes
@ -142,7 +239,7 @@ class Statement extends ParseNode:
var assignment
var shortcut_option_group
var custom_command
var line = ''
var line
func _init(parent, parser).(parent, parser):
if Block.can_parse(parser):
@ -170,7 +267,7 @@ class Statement extends ParseNode:
type = Type.CustomCommand
elif parser.next_symbol_is([Constants.TokenType.Text]):
line = parser.expect_symbol([Constants.TokenType.Text]).value
line = LineNode.new(self, parser)
type = Type.Line
else:
@ -203,7 +300,7 @@ class Statement extends ParseNode:
Type.CustomCommand:
info.append(custom_command.tree_string(indent_level))
Type.Line:
info.append(tab(indent_level, 'Line: %s' % line))
info.append(line.tree_string(indent_level))
_:
printerr('Cannot print statement')
@ -233,11 +330,14 @@ class CustomCommand extends ParseNode:
#if first token is identifier and second is leftt parenthesis
#evaluate as function
if (command_tokens.size() > 1 && command_tokens[0].type == Constants.TokenType.Identifier
&& command_tokens[1].type == Constants.TokenType.LeftParen):
if command_tokens.size() > 1 \
and command_tokens[0].type == Constants.TokenType.Identifier \
and command_tokens[1].type == Constants.TokenType.LeftParen:
var p = get_script().new(command_tokens, parser.library)
expression = ExpressionNode.parse(self, p)
type = Type.Expression
else:
#otherwise evaluuate command
type = Type.ClientCommand
@ -545,10 +645,10 @@ class ExpressionNode extends ParseNode:
var parameters = []
func _init(parent, parser, _value, _function = '', _parameters = []).(parent, parser):
if _value != null:
type = Constants.ExpressionType.Value
value = _value
else:
type = Constants.ExpressionType.FunctionCall
function = _function
@ -568,7 +668,7 @@ class ExpressionNode extends ParseNode:
return info.join('')
# using Djikstra's shunting-yard algorithm to convert stream of expresions into postfix notation,
# Using Djikstra's shunting-yard algorithm to convert stream of expresions into postfix notation,
# & then build a tree of expressions
static func parse(parent, parser):
var rpn = []
@ -595,7 +695,7 @@ class ExpressionNode extends ParseNode:
var last
#read expression content
while parser.tokens.size() > 0 && parser.next_symbol_is(valid_types):
while parser.tokens.size() > 0 and parser.next_symbol_is(valid_types):
var next = parser.expect_symbol(valid_types)
if next.type == Constants.TokenType.Variable \
@ -633,7 +733,7 @@ class ExpressionNode extends ParseNode:
#find the closest function on stack
#increment parameters
func_stack.back().param_count+=1
func_stack.back().parameter_count+=1
elif Operator.is_op(next.type):
#this is an operator
@ -685,7 +785,7 @@ class ExpressionNode extends ParseNode:
#else
#we have more than 1 param
if last.type != Constants.TokenType.LeftParen:
func_stack.back().param_count+=1
func_stack.back().parameter_count+=1
rpn.append(op_stack.pop_back())
func_stack.pop_back()
@ -730,7 +830,7 @@ class ExpressionNode extends ParseNode:
var function_name = next.value
var function_parameters = []
for _i in range(next.param_count):
for _i in range(next.parameter_count):
function_parameters.append(eval_stack.pop_back())
function_parameters.invert()
@ -802,7 +902,7 @@ class Assignment extends ParseNode:
info.append(tab(indent_level + 1, destination))
info.append(tab(indent_level + 1, Constants.token_type_name(operation)))
info.append(value.tree_string(indent_level + 1))
return info.join('')
return PoolStringArray(info).join('')
static func can_parse(parser):

View file

@ -76,6 +76,12 @@ enum TokenType {
#8 Command syntax ('<<foo>>')
BeginCommand, EndCommand,
ExpressionFunctionStart, # {
ExpressionFunctionEnd, # }
FormatFunctionStart, # [
FormatFunctionEnd, # ]
#10 Variables ('$foo')
Variable,
@ -145,7 +151,8 @@ enum TokenType {
}
enum ExpressionType {
Value, FunctionCall
Value,
FunctionCall
}
enum StatementTypes {
@ -155,7 +162,8 @@ enum StatementTypes {
IfStatement,
OptionStatement,
AssignmentStatement,
Line
Line,
InlineExpression
}
enum ValueType {

View file

@ -15,6 +15,7 @@ func get_function(name):
func import_library(other):
Constants.merge_dir(functions, other.functions)
other.virtual_machine = virtual_machine
func register_function(name, parameter_count, function, returns_value):
var functionInfo = FunctionInfo.new(name, parameter_count, function, returns_value)

View file

@ -3,6 +3,7 @@ extends Object
const Constants = preload('res://addons/Wol/core/Constants.gd')
var name = ''
var filename = ''
var strings = {}
var nodes = {}
@ -12,6 +13,7 @@ class Line:
var line_number = -1
var file_name = ''
var implicit = false
var substitutions = []
var meta = []
func _init(_text, _node_name, _line_number, _file_name, _implicit, _meta):
@ -21,6 +23,13 @@ class Line:
implicit = _implicit
meta = _meta
func clone():
return get_script().new(text, node_name, line_number, file_name, implicit, meta)
func _to_string():
return '%s:%d: "%s"' % [file_name.get_file(), line_number, text]
class Option:
var line
var id = -1
@ -31,6 +40,9 @@ class Option:
id = _id
destination = _destination
func clone():
return get_script().new(self)
class Command:
var command = ''

View file

@ -2,9 +2,6 @@ extends Object
const Constants = preload('res://addons/Wol/core/Constants.gd')
const NULL_STRING = 'null'
const FALSE_STRING = 'false'
const TRUE_STRING = 'true'
const NANI = 'NaN'
var type = Constants.ValueType.Nullean

View file

@ -7,24 +7,53 @@ position: 0, 0
<<command_with multiple arguments>>
// remove "to" to trigger error
<<set $direction to 'this'>>
<<set $direction to 'that'>>
<<set $one to 1>>
// Implement inline expressions
Bob: Theresa, {$direction} way! #line:5d7a7c
Theresa: Did you know one + one equals {$one + $one}?
Bob: You wanna go somewhere?
<<if visit_count() == 1>>
Narrator: You, {$direction} way!
<<endif>>
Narrator: Do you know you've been here {visit_count()} times?
You: Did you know one + one equals {$one + $one}?
Narrator: You wanna go somewhere?
[[Go to the store|TheStore]]
[[Lets stay here and talk|Talk]]
-> Go to the store
[[TheStore]]
// -> Wait, how many times have I been to the store?
// Narrator: You've been to the store {visit_count('TheStore')} times.
// [[Start]]
-> Lets stay here and talk
[[Talk]]
===
title: TheStore
tags:
colorID: 0
position: 0, 200
---
Clerk: Welcome to the store.
Clerk: Can I help you with anything?
Guy: Hey what's up I need your help can you come here?
You: Well I can't I'm buying clothes.
All right well hurry up and come over here.
You: I can't find them.
Guy: What do you mean you can't find them?
You: I can't find them there's only soup.
Guy: What do you mean there's only soup?!
You: It means there's only soup.
Guy: WELL THEN GET OUT OF THE SOUP ISLE!!
You: Alright you dont have to shout at me!
You: There's more soup.
Guy: What do you mean there's more soup?
You: There's just more soup.
Guy: Then go to the next aisle!
You: There's still soup!
Guy: Where are you right now?!
You: I'm at soup!
Guy: What do you mean you're at soup?!
You: I mean I'm at soup.
Guy: WHAT STORE ARE YOU IN?!
You: IM AT THE SOUP STORE!!
Guy: WHY ARE YOU BUYING CLOTHES AT THE SOUP STORE?!
You: FUCK YOU!
[[Go home|Start]]
===
title: Talk
@ -32,9 +61,9 @@ tags:
colorID: 0
position: 0, 400
---
Bob: So how are you really?
Theresa: I'm good!
Bob: Do you want to continue talking?
Narrator: So how are you really?
You: I'm good!
Narrator: Do you want to continue talking?
-> Yes
[[Start]]
-> No

View file

@ -9,30 +9,12 @@
config_version=4
_global_script_classes=[ {
"base": "Object",
"class": "Compiler",
"language": "GDScript",
"path": "res://addons/Wol/core/compiler/Compiler.gd"
}, {
"base": "Object",
"class": "Lexer",
"language": "GDScript",
"path": "res://addons/Wol/core/compiler/Lexer.gd"
}, {
"base": "Object",
"class": "Program",
"language": "GDScript",
"path": "res://addons/Wol/core/Program.gd"
}, {
"base": "Node",
"class": "Wol",
"language": "GDScript",
"path": "res://addons/Wol/Wol.gd"
} ]
_global_script_class_icons={
"Compiler": "",
"Lexer": "",
"Program": "",
"Wol": ""
}