fully cleaned up the compiler

This commit is contained in:
Bram Dingelstad 2021-11-21 14:44:13 +01:00
parent 13d2391e57
commit 83c4808d5a
4 changed files with 264 additions and 363 deletions

View file

@ -83,7 +83,6 @@ func _on_dialogue_finished():
func _on_node_start(node):
emit_signal('node_started', node)
resume()
func _on_node_finished(node):
emit_signal('node_finished', node)

View file

@ -8,21 +8,15 @@ const Parser = preload('res://addons/Wol/core/compiler/parser.gd')
const INVALID_TITLE = '[\\[<>\\]{}\\|:\\s#\\$]'
const NO_ERROR = 0x00
const LEXER_FAILURE = 0x01
const PARSER_FAILURE = 0x02
const INVALID_HEADER = 0x04
const ERR_COMPILATION_FAILED = 0x10
var source = ''
var filename = ''
var _current_node
var _contains_implicit_string_tags = false
var _label_count = 0
var current_node
var has_implicit_string_tags = false
var _string_table = {}
var _string_count = 0
var string_count = 0
var string_table = {}
var label_count = 0
func _init(_filename, _source = null):
filename = _filename
@ -50,7 +44,6 @@ func compile():
source_lines[i] = source_lines[i].strip_edges(false, true)
var parsed_nodes = []
while line_number < source_lines.size():
var title = ''
var body = ''
@ -77,8 +70,6 @@ func compile():
if line_number >= source_lines.size() or line == '---':
break
line_number += 1
# past header
var body_lines = []
@ -108,8 +99,8 @@ func compile():
for node in parsed_nodes:
compile_node(program, node)
for key in _string_table:
program.strings[key] = _string_table[key]
for key in string_table:
program.strings[key] = string_table[key]
return program
@ -131,7 +122,7 @@ func compile_node(program, parsed_node):
)
else:
var start_label = register_label()
emit(Constants.ByteCode.Label,node_compiled,[Program.Operand.new(start_label)])
emit(Constants.ByteCode.Label, node_compiled, [Program.Operand.new(start_label)])
for statement in parsed_node.statements:
generate_statement(node_compiled, statement)
@ -151,110 +142,102 @@ func compile_node(program, parsed_node):
program.nodes[node_compiled.name] = node_compiled
func register_string(text:String,node_name:String,id:String='',line_number:int=-1,tags:Array=[])->String:
var line_id_used : String
var implicit : bool
func register_string(text, node_name, id = '', line_number = -1, tags = []):
var line_id_used = ''
var implicit = false
if id.empty():
line_id_used = '%s-%s-%d' % [self.filename,node_name,self._string_count]
self._string_count+=1
line_id_used = '%s-%s-%d' % [filename, node_name, string_count]
string_count += 1
#use this when we generate implicit tags
#they are not saved and are generated
#aka dummy tags that change on each compilation
_contains_implicit_string_tags = true
has_implicit_string_tags = true
implicit = true
else :
line_id_used = id
implicit = false
var string_info = Program.Line.new(text,node_name,line_number,filename,implicit,tags)
#add to string table and return id
self._string_table[line_id_used] = string_info
var string_info = Program.Line.new(text, node_name, line_number, filename, implicit, tags)
string_table[line_id_used] = string_info
return line_id_used
func register_label(comment:String='')->String:
_label_count+=1
return 'L%s%s' %[ _label_count , comment]
func register_label(comment = ''):
label_count += 1
return 'Label%s%s' % [label_count, comment]
func emit(bytecode, node = _current_node, operands = []):
func emit(bytecode, node = current_node, operands = []):
var instruction = Program.Instruction.new(null)
instruction.operation = bytecode
instruction.operands = operands
if node == null:
printerr('trying to emit to null node with byte_code: %s' % bytecode)
printerr('Trying to emit to null node with byte_code: %s' % bytecode)
return
node.instructions.append(instruction)
if bytecode == Constants.ByteCode.Label:
#add to label table
node.labels[instruction.operands[0].value] = node.instructions.size()-1
func generate_header():
pass
func generate_statement(node,statement):
node.labels[instruction.operands[0].value] = node.instructions.size() - 1
func generate_statement(node, statement):
match statement.type:
Constants.StatementTypes.CustomCommand:
generate_custom_command(node,statement.custom_command)
generate_custom_command(node, statement.custom_command)
Constants.StatementTypes.ShortcutOptionGroup:
generate_shortcut_group(node,statement.shortcut_option_group)
generate_shortcut_group(node, statement.shortcut_option_group)
Constants.StatementTypes.Block:
generate_block(node,statement.block.statements)
generate_block(node, statement.block.statements)
Constants.StatementTypes.IfStatement:
generate_if(node,statement.if_statement)
generate_if(node, statement.if_statement)
Constants.StatementTypes.OptionStatement:
generate_option(node,statement.option_statement)
generate_option(node, statement.option_statement)
Constants.StatementTypes.AssignmentStatement:
generate_assignment(node,statement.assignment)
generate_assignment(node, statement.assignment)
Constants.StatementTypes.Line:
generate_line(node,statement,statement.line)
generate_line(node, statement,statement.line)
_:
assert(false, 'Illegal statement type [%s]. Could not generate code.' % statement.type)
#compile instructions for custom commands
func generate_custom_command(node,command):
#print('generating custom command')
#can evaluate command
func generate_custom_command(node, command):
# TODO: See if the first tree of this statement is being used
if command.expression != null:
generate_expression(node,command.expression)
generate_expression(node, command.expression)
else:
var command_string = command.client_command
if command_string == 'stop':
emit(Constants.ByteCode.Stop,node)
emit(Constants.ByteCode.Stop, node)
else :
emit(Constants.ByteCode.RunCommand,node,[Program.Operand.new(command_string)])
emit(Constants.ByteCode.RunCommand, node, [Program.Operand.new(command_string)])
#compile instructions for linetags and use them
# \#line:number
func generate_line(node,statement,line:String):
var num : String = register_string(line, node.name, '', statement.line_number, []);
func generate_line(node, statement, line):
# 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)])
func generate_shortcut_group(node,shortcut_group):
# print('generating shortcutoptopn group')
var end : String = register_label('group_end')
var labels : Array = []#String
var option_count : int = 0
var end = register_label('group_end')
var labels = []
var option_count = 0
for option in shortcut_group.options:
var op_destination : String = register_label('option_%s'%[option_count+1])
var endof_clause = ''
var op_destination = register_label('option_%s' % [option_count + 1])
labels.append(op_destination)
var endof_clause : String = ''
if option.condition != null :
endof_clause = register_label('conditional_%s'%option_count)
generate_expression(node,option.condition)
if option.condition != null:
endof_clause = register_label('conditional_%s' % option_count)
generate_expression(node, option.condition)
emit(Constants.ByteCode.JumpIfFalse, node, [Program.Operand.new(endof_clause)])
var label_line_id = '' #TODO: Add tag support
@ -265,168 +248,153 @@ func generate_shortcut_group(node,shortcut_group):
[]
)
emit(Constants.ByteCode.AddOption,node,[Program.Operand.new(label_string_id),Program.Operand.new(op_destination)])
emit(Constants.ByteCode.AddOption, node, [Program.Operand.new(label_string_id), Program.Operand.new(op_destination)])
if option.condition != null :
emit(Constants.ByteCode.Label,node,[Program.Operand.new(endof_clause)])
emit(Constants.ByteCode.Pop,node)
if option.condition != null:
emit(Constants.ByteCode.Label, node, [Program.Operand.new(endof_clause)])
emit(Constants.ByteCode.Pop, node)
option_count+=1
option_count += 1
emit(Constants.ByteCode.ShowOptions,node)
emit(Constants.ByteCode.Jump,node)
emit(Constants.ByteCode.ShowOptions, node)
emit(Constants.ByteCode.Jump, node)
option_count = 0
for option in shortcut_group.options:
emit(Constants.ByteCode.Label,node,[Program.Operand.new(labels[option_count])])
emit(Constants.ByteCode.Label, node, [Program.Operand.new(labels[option_count])])
if option.node != null :
generate_block(node,option.node.statements)
emit(Constants.ByteCode.JumpTo,node,[Program.Operand.new(end)])
option_count+=1
if option.node != null:
generate_block(node, option.node.statements)
emit(Constants.ByteCode.JumpTo, node, [Program.Operand.new(end)])
option_count += 1
#end of option group
emit(Constants.ByteCode.Label,node,[Program.Operand.new(end)])
#clean up
emit(Constants.ByteCode.Pop,node)
emit(Constants.ByteCode.Label, node, [Program.Operand.new(end)])
emit(Constants.ByteCode.Pop, node)
#compile instructions for block
#blocks are just groups of statements
func generate_block(node,statements:Array=[]):
# print('generating block')
if !statements.empty():
func generate_block(node, statements = []):
if not statements.empty():
for statement in statements:
generate_statement(node,statement)
generate_statement(node, statement)
#compile if branching instructions
func generate_if(node,if_statement):
# print('generating if')
#jump to label @ end of every clause
var endif : String = register_label('endif')
func generate_if(node, if_statement):
var endif = register_label('endif')
for clause in if_statement.clauses:
var end_clause : String = register_label('skip_clause')
var end_clause = register_label('skip_clause')
if clause.expression!=null:
generate_expression(node,clause.expression)
emit(Constants.ByteCode.JumpIfFalse,node,[Program.Operand.new(end_clause)])
if clause.expression != null:
generate_expression(node, clause.expression)
emit(Constants.ByteCode.JumpIfFalse, node, [Program.Operand.new(end_clause)])
generate_block(node,clause.statements)
emit(Constants.ByteCode.JumpTo,node,[Program.Operand.new(endif)])
emit(Constants.ByteCode.JumpTo, node, [Program.Operand.new(endif)])
if clause.expression!=null:
emit(Constants.ByteCode.Label,node,[Program.Operand.new(end_clause)])
if clause.expression != null:
emit(Constants.ByteCode.Label, node, [Program.Operand.new(end_clause)])
if clause.expression!=null:
if clause.expression != null:
emit(Constants.ByteCode.Pop)
emit(Constants.ByteCode.Label, node, [Program.Operand.new(endif)])
emit(Constants.ByteCode.Label,node,[Program.Operand.new(endif)])
#compile instructions for options
func generate_option(node,option):
# print('generating option')
var destination : String = option.destination
func generate_option(node, option):
var destination = option.destination
if option.label == null or option.label.empty():
#jump to another node
emit(Constants.ByteCode.RunNode,node,[Program.Operand.new(destination)])
emit(Constants.ByteCode.RunNode, node, [Program.Operand.new(destination)])
else :
var line_iD : String = ''#tags not supported TODO: ADD TAG SUPPORT
var string_iD = register_string(option.label,node.name,line_iD,option.line_number,[])
var line_id = '' #TODO: ADD TAG SUPPORT
var string_id = register_string(option.label, node.name, line_id, option.line_number, [])
emit(Constants.ByteCode.AddOption,node,[Program.Operand.new(string_iD),Program.Operand.new(destination)])
emit(Constants.ByteCode.AddOption, node, [Program.Operand.new(string_id), Program.Operand.new(destination)])
#compile instructions for assigning values
func generate_assignment(node,assignment):
# print('generating assign')
#assignment
func generate_assignment(node, assignment):
if assignment.operation == Constants.TokenType.EqualToOrAssign:
#evaluate the expression to a value for the stack
generate_expression(node,assignment.value)
generate_expression(node, assignment.value)
else :
#this is combined op
#get value of var
emit(Constants.ByteCode.PushVariable,node,[assignment.destination])
#evaluate the expression and push value to stack
generate_expression(node,assignment.value)
#stack contains oldvalue and result
emit(Constants.ByteCode.PushVariable, node, [assignment.destination])
generate_expression(node, assignment.value)
match assignment.operation:
Constants.TokenType.AddAssign:
emit(Constants.ByteCode.CallFunc,node,
[Program.Operand.new(Constants.token_type_name(Constants.TokenType.Add))])
emit(
Constants.ByteCode.CallFunc,
node,
[Program.Operand.new(Constants.token_type_name(Constants.TokenType.Add))]
)
Constants.TokenType.MinusAssign:
emit(Constants.ByteCode.CallFunc,node,
[Program.Operand.new(Constants.token_type_name(Constants.TokenType.Minus))])
emit(
Constants.ByteCode.CallFunc,
node,
[Program.Operand.new(Constants.token_type_name(Constants.TokenType.Minus))]
)
Constants.TokenType.MultiplyAssign:
emit(Constants.ByteCode.CallFunc,node,
[Program.Operand.new(Constants.token_type_name(Constants.TokenType.MultiplyAssign))])
emit(
Constants.ByteCode.CallFunc,
node,
[Program.Operand.new(Constants.token_type_name(Constants.TokenType.MultiplyAssign))]
)
Constants.TokenType.DivideAssign:
emit(Constants.ByteCode.CallFunc,node,
[Program.Operand.new(Constants.token_type_name(Constants.TokenType.DivideAssign))])
emit(
Constants.ByteCode.CallFunc,
node,
[Program.Operand.new(Constants.token_type_name(Constants.TokenType.DivideAssign))]
)
_:
printerr('Unable to generate assignment')
#stack contains destination value
#store the top of the stack in variable
emit(Constants.ByteCode.StoreVariable,node,[Program.Operand.new(assignment.destination)])
emit(Constants.ByteCode.StoreVariable, node, [Program.Operand.new(assignment.destination)])
emit(Constants.ByteCode.Pop, node)
#clean stack
emit(Constants.ByteCode.Pop,node)
#compile expression instructions
func generate_expression(node,expression):
# print('generating expression')
#expression = value or func call
func generate_expression(node, expression):
match expression.type:
Constants.ExpressionType.Value:
generate_value(node,expression.value)
generate_value(node, expression.value)
Constants.ExpressionType.FunctionCall:
#eval all parameters
for param in expression.params:
generate_expression(node,param)
for parameter in expression.parameters:
generate_expression(node, parameter)
#put the num of of params to stack
emit(Constants.ByteCode.PushNumber,node,[Program.Operand.new(expression.params.size())])
#call function
emit(Constants.ByteCode.CallFunc,node,[Program.Operand.new(expression.function)])
emit(Constants.ByteCode.PushNumber, node, [Program.Operand.new(expression.parameters.size())])
emit(Constants.ByteCode.CallFunc, node, [Program.Operand.new(expression.function)])
_:
printerr('no expression')
printerr('No expression.')
#compile value instructions
func generate_value(node,value):
# print('generating value')
#push value to stack
func generate_value(node, value):
match value.value.type:
Constants.ValueType.Number:
emit(Constants.ByteCode.PushNumber,node,[Program.Operand.new(value.value.as_number())])
emit(
Constants.ByteCode.PushNumber,
node,
[Program.Operand.new(value.value.as_number())]
)
Constants.ValueType.Str:
var id = register_string(value.value.as_string(),
node.name,'',value.line_number,[])
emit(Constants.ByteCode.PushString,node,[Program.Operand.new(id)])
var id = register_string(
value.value.as_string(),
node.name,
'',
value.line_number,
[]
)
emit(
Constants.ByteCode.PushString,
node,
[Program.Operand.new(id)]
)
Constants.ValueType.Boolean:
emit(Constants.ByteCode.PushBool,node,[Program.Operand.new(value.value.as_bool())])
emit(
Constants.ByteCode.PushBool,
node,
[Program.Operand.new(value.value.as_bool())]
)
Constants.ValueType.Variable:
emit(Constants.ByteCode.PushVariable,node,[Program.Operand.new(value.value.variable)])
emit(
Constants.ByteCode.PushVariable,
node,
[Program.Operand.new(value.value.variable)]
)
Constants.ValueType.Nullean:
emit(Constants.ByteCode.PushNull,node)
emit(Constants.ByteCode.PushNull, node)
_:
printerr('Unrecognized valuenode type: %s' % value.value.type)
static func print_tokens(tokens:Array=[]):
var list : PoolStringArray = []
list.append('\n')
for token in tokens:
list.append('%s (%s line %s)\n'%[Constants.token_type_name(token.type),token.value,token.line_number])
print('TOKENS:')
print(list.join(''))

View file

@ -3,30 +3,29 @@ class_name Lexer
const Constants = preload('res://addons/Wol/core/constants.gd')
const LINE_COMENT : String = '//'
const FORWARD_SLASH : String = '/'
const LINE_COMENT = '//'
const FORWARD_SLASH = '/'
const LINE_SEPARATOR = '\n'
const LINE_SEPARATOR : String = '\n'
const BASE = 'base'
const DASH = '-'
const COMMAND = 'command'
const LINK = 'link'
const SHORTCUT = 'shortcut'
const TAG = 'tag'
const EXPRESSION = 'expression'
const ASSIGNMENT = 'assignment'
const OPTION = 'option'
const OR = 'or'
const DESTINATION = 'destination'
const BASE : String = 'base'
const DASH : String = '-'
const COMMAND : String = 'command'
const LINK : String = 'link'
const SHORTCUT : String = 'shortcut'
const TAG : String = 'tag'
const EXPRESSION : String = 'expression'
const ASSIGNMENT : String = 'assignment'
const OPTION : String = 'option'
const OR : String = 'or'
const DESTINATION : String = 'destination'
var WHITESPACE = '\\s*'
var WHITESPACE : String = '\\s*'
var _states = {}
var _defaultState
var _currentState
var _states : Dictionary = {}
var _defaultState : LexerState
var _currentState : LexerState
var _indentStack : Array = []
var _indentStack = []
var _shouldTrackIndent : bool = false
var filename = ''
@ -185,11 +184,11 @@ func tokenize():
_indentStack.push_front(IntBoolPair.new(0, false))
_shouldTrackIndent = false
var tokens : Array = []
var tokens = []
_currentState = _defaultState
var lines : PoolStringArray = text.split(LINE_SEPARATOR)
var lines = text.split(LINE_SEPARATOR)
lines.append('')
var line_number : int = 1

View file

@ -1,11 +1,7 @@
extends Node
const Constants = preload('res://addons/Wol/core/constants.gd')
var Value = load('res://addons/Wol/core/value.gd')
const EXECUTION_COMPLETE : String = 'execution_complete_command'
var NULL_VALUE = Value.new(null)
const Value = preload('res://addons/Wol/core/value.gd')
# Function references to handlers
var line_handler
@ -18,9 +14,9 @@ var dialogue_finished_handler
var dialogue
var libraries
var program
var _state
var state
var _currentNode
var current_node
var execution_state = Constants.ExecutionState.Stopped
@ -43,73 +39,64 @@ func _init(_dialogue, _libraries):
assert(command_handler.is_valid(), 'Cannot run without a command handler (_on_command)')
assert(node_start_handler.is_valid(), 'Cannot run without a node start handler (_on_node_start)')
assert(node_finished_handler.is_valid(), 'Cannot run without a node finished handler (_on_node_finished)')
assert(dialogue_finished_handler.is_valid(), 'Cannot run without a dialogue finished handler (_on_dialogue_finished)')
_state = VmState.new()
state = VmState.new()
#set the node to run
#return true if successeful false if no node
#of that name found
func set_node(name):
if program == null || program.nodes.size() == 0:
if program == null or program.nodes.size() == 0:
printerr('Could not load %s : no nodes loaded' % name)
return false
if !program.nodes.has(name):
if not program.nodes.has(name):
execution_state = Constants.ExecutionState.Stopped
reset()
printerr('No node named %s has been loaded' % name)
printerr('No node named %s has been found in dialogue, so not loading' % name)
return false
_currentNode = program.nodes[name]
current_node = program.nodes[name]
reset()
_state.currentNodeName = name
state.current_node_name = name
node_start_handler.call_func(name)
return true
func current_node_name()->String:
return _currentNode.nodeName
func current_node():
return _currentNode
func pause():
execution_state = Constants.ExecutionState.Suspended
#stop exectuion
func stop():
execution_state = Constants.ExecutionState.Stopped
reset()
_currentNode = null
current_node = null
#set the currently selected option and
#resume execution if waiting for result
#return false if error
func set_selected_option(id):
if execution_state != Constants.ExecutionState.WaitingForOption:
printerr('Unable to select option when dialogue not waiting for option')
return false
if id < 0 or id >= _state.currentOptions.size():
printerr('%d is not a valid option ' % id)
if id < 0 or id >= state.current_options.size():
printerr('%d is not a valid option!' % id)
return false
var destination = _state.currentOptions[id].value
_state.push_value(destination)
_state.currentOptions.clear()
var destination = state.current_options[id][1]
state.push_value(destination)
state.current_options.clear()
#no longer waiting for option
execution_state = Constants.ExecutionState.Suspended
return true
func reset():
_state = VmState.new()
state = VmState.new()
func get_next_instruction():
return null if _currentNode.instructions.size() - 1 <= _state.programCounter else _currentNode.instructions[_state.programCounter + 1]
if current_node.instructions.size() - 1 > state.programCounter:
return current_node.instructions[state.programCounter + 1]
return
func resume():
if _currentNode == null:
if current_node == null:
printerr('Cannot run dialogue with no node selected')
return false
@ -117,118 +104,90 @@ func resume():
printerr('Cannot run while waiting for option')
return false
execution_state = Constants.ExecutionState.Running
#execute instruction until something cool happens
while execution_state == Constants.ExecutionState.Running:
var currentInstruction = _currentNode.instructions[_state.programCounter]
var current_instruction = current_node.instructions[state.programCounter]
run_instruction(current_instruction)
state.programCounter += 1
run_instruction(currentInstruction)
_state.programCounter += 1
if _state.programCounter >= _currentNode.instructions.size():
node_finished_handler.call_func(_currentNode.nodeName)
if state.programCounter >= current_node.instructions.size():
node_finished_handler.call_func(current_node.nodeName)
execution_state = Constants.ExecutionState.Stopped
reset()
dialogue_finished_handler.call_func()
return true
func find_label_instruction(label:String)->int:
if !_currentNode.labels.has(label):
printerr('Unknown label:'+label)
func find_label_instruction(label):
if not current_node.labels.has(label):
printerr('Unknown label:' + label)
return -1
return _currentNode.labels[label]
return current_node.labels[label]
func run_instruction(instruction)->bool:
func run_instruction(instruction):
match instruction.operation:
Constants.ByteCode.Label:
pass
# Jump to named label
Constants.ByteCode.JumpTo:
#jump to named label
_state .programCounter = find_label_instruction(instruction.operands[0].value)-1
state.programCounter = find_label_instruction(instruction.operands[0].value) - 1
Constants.ByteCode.RunLine:
#look up string from string table
#pass it to client as line
# Lookup string and give back as line
var key = instruction.operands[0].value
var line = program.strings[key]
#the second operand is the expression count
# of format function
# The second operand is the expression count of format function
# TODO: Add format functions supportk
if instruction.operands.size() > 1:
pass#add format function support
pass
var pause = line_handler.call_func(line)
var return_state = line_handler.call_func(line)
if pause == Constants.HandlerState.PauseExecution:
if return_state == Constants.HandlerState.PauseExecution:
execution_state = Constants.ExecutionState.Suspended
Constants.ByteCode.RunCommand:
var commandText : String = instruction.operands[0].value
var command_text = instruction.operands[0].value
var command = Program.Command.new(command_text)
if instruction.operands.size() > 1:
pass#add format function
var command = Program.Command.new(commandText)
var pause = command_handler.call_func(command) as int
if pause == Constants.HandlerState.PauseExecution:
var return_state = command_handler.call_func(command)
if return_state == Constants.HandlerState.PauseExecution:
execution_state = Constants.ExecutionState.Suspended
Constants.ByteCode.PushString:
#push String var to stack
_state.push_value(instruction.operands[0].value)
Constants.ByteCode.PushNumber:
#push number to stack
_state.push_value(instruction.operands[0].value)
Constants.ByteCode.PushString, \
Constants.ByteCode.PushNumber, \
Constants.ByteCode.PushBool:
#push boolean to stack
_state.push_value(instruction.operands[0].value)
state.push_value(instruction.operands[0].value)
Constants.ByteCode.PushNull:
#push null t
_state.push_value(NULL_VALUE)
state.push_value(Value.new(null))
# Jump to named label if value of stack top is false
Constants.ByteCode.JumpIfFalse:
#jump to named label if value of stack top is false
if !_state.peek_value().as_bool():
_state.programCounter = find_label_instruction(instruction.operands[0].value)-1
if not state.peek_value().as_bool():
state.programCounter = find_label_instruction(instruction.operands[0].value) - 1
# Jump to label whose name is on the stack
Constants.ByteCode.Jump:
#jump to label whose name is on the stack
var dest : String = _state.peek_value().as_string()
_state.programCounter = find_label_instruction(dest)-1
var destination = state.peek_value().as_string()
state.programCounter = find_label_instruction(destination) - 1
Constants.ByteCode.Pop:
#pop value from stack
_state.pop_value()
state.pop_value()
Constants.ByteCode.CallFunc:
#call function with params on stack
#push any return value to stack
var functionName : String = instruction.operands[0].value
var function_name = instruction.operands[0].value
var function = libraries.get_function(function_name)
var expected_parameter_count = function.parameter_count
var actual_parameter_count = state.pop_value().as_number()
var function = libraries.get_function(functionName)
var expected_parameter_count : int = function.paramCount
var actual_parameter_count : int = _state.pop_value().as_number()
#if function takes in -1 params disregard
#expect the compiler to have placed the number of params
#at the top of the stack
if expected_parameter_count == -1:
expected_parameter_count = actual_parameter_count
if expected_parameter_count != actual_parameter_count:
printerr('Function %s expected %d parameters but got %d instead' %[functionName,
expected_parameter_count,actual_parameter_count])
if expected_parameter_count > 0 \
and expected_parameter_count != actual_parameter_count:
var error_data = [function_name, expected_parameter_count, actual_parameter_count]
printerr('Function "%s" expected %d parameters but got %d instead' % error_data)
return false
var result
@ -236,85 +195,71 @@ func run_instruction(instruction)->bool:
if actual_parameter_count == 0:
result = function.invoke()
else:
var params : Array = []#value
var params = []
for _i in range(actual_parameter_count):
params.push_front(_state.pop_value())
params.push_front(state.pop_value())
result = function.invoke(params)
if function.returnsValue:
_state.push_value(result)
if function.returns_value:
state.push_value(result)
Constants.ByteCode.PushVariable:
#get content of variable and push to stack
var name : String = instruction.operands[0].value
# TODO: Reimplement variable storage
var name = instruction.operands[0].value
var loaded = dialogue.variable_storage.get_value(name)
_state.push_value(loaded)
state.push_value(loaded)
Constants.ByteCode.StoreVariable:
#store top stack value to variable
var top = _state.peek_value()
var destination : String = instruction.operands[0].value
dialogue.variable_storage.set_value(destination,top)
var top = state.peek_value()
var destination = instruction.operands[0].value
dialogue.variable_storage.set_value(destination, top)
Constants.ByteCode.Stop:
#stop execution and repost it
node_finished_handler.call_func(_currentNode.name)
node_finished_handler.call_func(current_node.name)
dialogue_finished_handler.call_func()
execution_state = Constants.ExecutionState.Stopped
reset()
Constants.ByteCode.RunNode:
#run a node
var name : String
if (instruction.operands.size() == 0 || instruction.operands[0].value.empty()):
#get string from stack and jump to node with that name
name = _state.peek_value().value()
else :
var name = ''
if instruction.operands.size() == 0 or instruction.operands[0].value.empty():
name = state.peek_value().value()
else:
name = instruction.operands[0].value
var pause = node_finished_handler.call_func(_currentNode.name)
var return_state = node_finished_handler.call_func(current_node.name)
set_node(name)
_state.programCounter-=1
if pause == Constants.HandlerState.PauseExecution:
state.programCounter -= 1
if return_state == Constants.HandlerState.PauseExecution:
execution_state = Constants.ExecutionState.Suspended
Constants.ByteCode.AddOption:
# add an option to current state
var key = instruction.operands[0].value
var line = program.strings[key]
# TODO: Add format functions supportk
if instruction.operands.size() > 2:
pass #formated text options
pass
# line to show and node name
_state.currentOptions.append(SimpleEntry.new(line, instruction.operands[1].value))
state.current_options.append([line, instruction.operands[1].value])
Constants.ByteCode.ShowOptions:
#show options - stop if none
if _state.currentOptions.size() == 0:
if state.current_options.size() == 0:
execution_state = Constants.ExecutionState.Stopped
reset()
dialogue_finished_handler.call_func()
return false
#present list of options
var choices : Array = []#Option
for optionIndex in range(_state.currentOptions.size()):
var option : SimpleEntry = _state.currentOptions[optionIndex]
choices.append(Program.Option.new(option.key, optionIndex, option.value))
var choices = []
for option_index in range(state.current_options.size()):
var option = state.current_options[option_index]
var line = option[0]
var destination = option[1]
choices.append(Program.Option.new(line, option_index, destination))
#we cant continue until option chosen
execution_state = Constants.ExecutionState.WaitingForOption
#pass the options to the client
#delegate for them to call
#when user makes selection
options_handler.call_func(choices)
_:
execution_state = Constants.ExecutionState.Stopped
reset()
@ -324,12 +269,10 @@ func run_instruction(instruction)->bool:
return true
class VmState:
var Value = load('res://addons/Wol/core/value.gd')
var currentNodeName : String
var programCounter : int = 0
var currentOptions : Array = []#SimpleEntry
var stack : Array = [] #Value
var current_node_name = ''
var programCounter = 0
var current_options = []
var stack = []
func push_value(value)->void:
if value is Value:
@ -345,11 +288,3 @@ class VmState:
func clear_stack():
stack.clear()
class SimpleEntry:
var key
var value
func _init(_key, _value):
key = _key
value = _value