diff --git a/addons/Wol/Wol.gd b/addons/Wol/Wol.gd index b3aa8e0..151ef6e 100644 --- a/addons/Wol/Wol.gd +++ b/addons/Wol/Wol.gd @@ -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) diff --git a/addons/Wol/core/compiler/compiler.gd b/addons/Wol/core/compiler/compiler.gd index aa6b17c..f2df47f 100644 --- a/addons/Wol/core/compiler/compiler.gd +++ b/addons/Wol/core/compiler/compiler.gd @@ -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('')) diff --git a/addons/Wol/core/compiler/lexer.gd b/addons/Wol/core/compiler/lexer.gd index 4f84176..405ee8a 100644 --- a/addons/Wol/core/compiler/lexer.gd +++ b/addons/Wol/core/compiler/lexer.gd @@ -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 diff --git a/addons/Wol/core/virtual_machine.gd b/addons/Wol/core/virtual_machine.gd index 291efe1..7022c76 100644 --- a/addons/Wol/core/virtual_machine.gd +++ b/addons/Wol/core/virtual_machine.gd @@ -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,192 +39,155 @@ 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 if execution_state == Constants.ExecutionState.WaitingForOption: 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