From cda5fe84067d0c551062dae2dc7823873521b3e1 Mon Sep 17 00:00:00 2001 From: Bram Dingelstad Date: Sat, 20 Nov 2021 22:29:24 +0100 Subject: [PATCH] improved a bunch of things (again) --- Dialogue.gd | 25 ++- Dialogue.tscn | 1 + addons/Wol/Wol.gd | 36 ++-- addons/Wol/core/compiler/compiler.gd | 52 ++--- addons/Wol/core/compiler/lexer.gd | 190 +++++++++++-------- addons/Wol/core/compiler/parser.gd | 199 +++++++++++--------- addons/Wol/core/constants.gd | 6 + addons/Wol/core/dialogue.gd | 35 ++-- addons/Wol/core/dialogue/command.gd | 6 - addons/Wol/core/dialogue/format_function.gd | 16 -- addons/Wol/core/dialogue/line.gd | 8 - addons/Wol/core/dialogue/option.gd | 13 -- addons/Wol/core/libraries/standard.gd | 74 ++++---- addons/Wol/core/program.gd | 16 ++ addons/Wol/core/virtual_machine.gd | 65 +++---- dialogue.yarn | 95 +++------- project.godot | 2 +- 17 files changed, 409 insertions(+), 430 deletions(-) delete mode 100644 addons/Wol/core/dialogue/command.gd delete mode 100644 addons/Wol/core/dialogue/format_function.gd delete mode 100644 addons/Wol/core/dialogue/line.gd delete mode 100644 addons/Wol/core/dialogue/option.gd diff --git a/Dialogue.gd b/Dialogue.gd index a899791..083a030 100644 --- a/Dialogue.gd +++ b/Dialogue.gd @@ -1,7 +1,7 @@ extends Control func _ready(): - pass + $VBoxContainer/ButtonTemplate.hide() func continue_dialogue(): if $Tween.is_active(): @@ -12,7 +12,6 @@ func continue_dialogue(): $Wol.resume() func _on_Wol_line(line): - print(var2str(line)) $RichTextLabel.text = line.text $Tween.remove_all() @@ -27,9 +26,27 @@ func _on_Wol_line(line): $Tween.start() func _on_Wol_options(options): - prints('got some options', options) + var button_template = $VBoxContainer/ButtonTemplate + + for option in options: + var button = button_template.duplicate() + button.text = option.line.text + button.name = 'Option%d' % option.id + + $VBoxContainer.add_child(button) + button.connect('pressed', self, '_on_option_selected', [option]) + button.show() + +func _on_option_selected(option): + $Wol.select_option(option.id) + + for child in $VBoxContainer.get_children(): + if not 'Template' in child.name: + child.queue_free() + +func _on_Wol_finished(): + $RichTextLabel.text = '' func _input(event): if event is InputEventKey and event.scancode == KEY_ENTER and event.pressed: - print('Pressed enter!') continue_dialogue() diff --git a/Dialogue.tscn b/Dialogue.tscn index 30b4cff..66c83bb 100644 --- a/Dialogue.tscn +++ b/Dialogue.tscn @@ -50,5 +50,6 @@ text = "This is a dialogue option" [node name="Tween" type="Tween" parent="."] +[connection signal="finished" from="Wol" to="." method="_on_Wol_finished"] [connection signal="line" from="Wol" to="." method="_on_Wol_line"] [connection signal="options" from="Wol" to="." method="_on_Wol_options"] diff --git a/addons/Wol/Wol.gd b/addons/Wol/Wol.gd index 623f011..a07398f 100644 --- a/addons/Wol/Wol.gd +++ b/addons/Wol/Wol.gd @@ -19,13 +19,13 @@ export(String, FILE, '*.wol,*.yarn') var path setget set_path export(String) var start_node = 'Start' export(bool) var auto_start = false export(NodePath) var variable_storage_path +export var auto_show_options = true onready var variable_storage = get_node(variable_storage_path) var program var dialogue -var running = false func _ready(): if Engine.editor_hint: @@ -62,18 +62,21 @@ func init_dialogue(): dialogue.set_program(program) func set_path(_path): - var file = File.new() - file.open(_path, File.READ) - var source = file.get_as_text() - file.close() - program = WolCompiler.compile_string(source, _path) + if not Engine.editor_hint: + var file = File.new() + file.open(_path, File.READ) + var source = file.get_as_text() + file.close() + program = WolCompiler.compile_string(source, _path) path = _path func _handle_line(line): - var id = line.id - var string = program.strings[id] - call_deferred('emit_signal', 'line', string) - return Constants.HandlerState.PauseExecution + call_deferred('emit_signal', 'line', line) + if auto_show_options \ + and dialogue.get_vm().get_next_instruction().operation == Constants.ByteCode.AddOption: + return Constants.HandlerState.ContinueExecution + else: + return Constants.HandlerState.PauseExecution func _handle_command(command): call_deferred('emit_signal', 'command', command) @@ -84,16 +87,14 @@ func _handle_command(command): return Constants.HandlerState.ContinueExecution func _handle_options(options): - call_deferred('emit_signal' ,'options', options) + call_deferred('emit_signal', 'options', options) return Constants.HandlerState.PauseExecution func _handle_dialogue_complete(): emit_signal('finished') - running = false func _handle_node_start(node): emit_signal('node_started', node) - print('node started') dialogue.resume() if !dialogue._visitedNodeCount.has(node): @@ -101,28 +102,23 @@ func _handle_node_start(node): else: dialogue._visitedNodeCount[node] += 1 - print(dialogue._visitedNodeCount) - func _handle_node_complete(node): emit_signal('node_completed', node) - running = false return Constants.HandlerState.ContinueExecution func select_option(id): dialogue.get_vm().set_selected_option(id) + resume() func pause(): dialogue.call_deferred('pause') func start(node = start_node): - if running: - return - init_dialogue() emit_signal('started') - running = true dialogue.set_node(node) + dialogue.start() func resume(): dialogue.call_deferred('resume') diff --git a/addons/Wol/core/compiler/compiler.gd b/addons/Wol/core/compiler/compiler.gd index c32215f..575e4b8 100644 --- a/addons/Wol/core/compiler/compiler.gd +++ b/addons/Wol/core/compiler/compiler.gd @@ -72,6 +72,7 @@ static func compile_string(source: String, filename: String): var value : String = result.get_string('value') if field == 'title': + assert(not ' ' in value, 'No space allowed in title "%s", correct to "%s"' % [value, value.replace(' ','')]) title = value if(line_number >= source_lines.size() || source_lines[line_number] == '---'): @@ -83,7 +84,7 @@ static func compile_string(source: String, filename: String): #past header var body_lines : PoolStringArray = [] - while line_number < source_lines.size() && source_lines[line_number]!='===': + while line_number < source_lines.size() and source_lines[line_number]!='===': body_lines.append(source_lines[line_number]) line_number+=1 @@ -91,10 +92,9 @@ static func compile_string(source: String, filename: String): body = body_lines.join('\n') var lexer = Lexer.new() + var tokens = lexer.tokenize(body, title, filename) - var tokens : Array = lexer.tokenize(body) - var parser = Parser.new(tokens) - + var parser = Parser.new(tokens, title) var parser_node = parser.parse_node() parser_node.name = title @@ -150,7 +150,7 @@ func compile_node(program, parsed_node): if dangling_options: emit(Constants.ByteCode.ShowOptions, node_compiled) - emit(Constants.ByteCode.Run_node, node_compiled) + emit(Constants.ByteCode.RunNode, node_compiled) else: emit(Constants.ByteCode.Stop, node_compiled) @@ -269,11 +269,15 @@ func generate_shortcut_group(node,shortcut_group): if option.condition != null : endof_clause = register_label('conditional_%s'%option_count) generate_expression(node,option.condition) - emit(Constants.ByteCode.Jump_if_false,node,[Program.Operand.new(endof_clause)]) + emit(Constants.ByteCode.JumpIfFalse, node, [Program.Operand.new(endof_clause)]) - var label_line_id : String = ''#no tag TODO: ADD TAG SUPPORT - var label_string_id : String = register_string(option.label,node.node_name, - label_line_id,option.line_number,[]) + var label_line_id = '' #TODO: Add tag support + var label_string_id = register_string( + option.label, + node.name, + label_line_id,option.line_number, + [] + ) emit(Constants.ByteCode.AddOption,node,[Program.Operand.new(label_string_id),Program.Operand.new(op_destination)]) @@ -293,7 +297,7 @@ func generate_shortcut_group(node,shortcut_group): if option.node != null : generate_block(node,option.node.statements) - emit(Constants.ByteCode.Jump_to,node,[Program.Operand.new(end)]) + emit(Constants.ByteCode.JumpTo,node,[Program.Operand.new(end)]) option_count+=1 #end of option group @@ -323,10 +327,10 @@ func generate_if(node,if_statement): if clause.expression!=null: generate_expression(node,clause.expression) - emit(Constants.ByteCode.Jump_if_false,node,[Program.Operand.new(end_clause)]) + emit(Constants.ByteCode.JumpIfFalse,node,[Program.Operand.new(end_clause)]) generate_block(node,clause.statements) - emit(Constants.ByteCode.Jump_to,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)]) @@ -348,7 +352,7 @@ func generate_option(node,option): 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.node_name,line_iD,option.line_number,[]) + 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)]) @@ -357,13 +361,13 @@ func generate_option(node,option): func generate_assignment(node,assignment): # print('generating assign') #assignment - if assignment.operation == Constants.TokenType.Equal_to_or_assign: + if assignment.operation == Constants.TokenType.EqualToOrAssign: #evaluate the expression to a value for the stack generate_expression(node,assignment.value) else : #this is combined op #get value of var - emit(Constants.ByteCode.Push_variable,node,[assignment.destination]) + emit(Constants.ByteCode.PushVariable,node,[assignment.destination]) #evaluate the expression and push value to stack generate_expression(node,assignment.value) @@ -371,16 +375,16 @@ func generate_assignment(node,assignment): #stack contains oldvalue and result match assignment.operation: - Constants.TokenType.Add_assign: + Constants.TokenType.AddAssign: emit(Constants.ByteCode.CallFunc,node, [Program.Operand.new(Constants.token_type_name(Constants.TokenType.Add))]) - Constants.TokenType.Minus_assign: + Constants.TokenType.MinusAssign: emit(Constants.ByteCode.CallFunc,node, [Program.Operand.new(Constants.token_type_name(Constants.TokenType.Minus))]) - Constants.TokenType.Multiply_assign: + Constants.TokenType.MultiplyAssign: emit(Constants.ByteCode.CallFunc,node, [Program.Operand.new(Constants.token_type_name(Constants.TokenType.MultiplyAssign))]) - Constants.TokenType.Divide_assign: + Constants.TokenType.DivideAssign: emit(Constants.ByteCode.CallFunc,node, [Program.Operand.new(Constants.token_type_name(Constants.TokenType.DivideAssign))]) _: @@ -388,7 +392,7 @@ func generate_assignment(node,assignment): #stack contains destination value #store the top of the stack in variable - emit(Constants.ByteCode.Store_variable,node,[Program.Operand.new(assignment.destination)]) + emit(Constants.ByteCode.StoreVariable,node,[Program.Operand.new(assignment.destination)]) #clean stack emit(Constants.ByteCode.Pop,node) @@ -399,9 +403,9 @@ func generate_expression(node,expression): # print('generating expression') #expression = value || func call match expression.type: - Constants.Expression_type.Value: + Constants.ExpressionType.Value: generate_value(node,expression.value) - Constants.Expression_type.Function_call: + Constants.ExpressionType.FunctionCall: #eval all parameters for param in expression.params: generate_expression(node,param) @@ -422,8 +426,8 @@ func generate_value(node,value): Constants.ValueType.Number: emit(Constants.ByteCode.PushNumber,node,[Program.Operand.new(value.value.as_number())]) Constants.ValueType.Str: - var id : String = register_string(value.value.as_string(), - node.node_name,'',value.line_number,[]) + 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())]) diff --git a/addons/Wol/core/compiler/lexer.gd b/addons/Wol/core/compiler/lexer.gd index 6c0055d..13412bb 100644 --- a/addons/Wol/core/compiler/lexer.gd +++ b/addons/Wol/core/compiler/lexer.gd @@ -35,50 +35,50 @@ func _init(): func create_states(): var patterns : Dictionary = {} - patterns[Constants.TokenType.Text] = '.*' + patterns[Constants.TokenType.Text] = ['.*', 'any text'] - patterns[Constants.TokenType.Number] = '\\-?[0-9]+(\\.[0-9+])?' - patterns[Constants.TokenType.Str] = '\'([^\'\\\\]*(?:\\.[^\'\\\\]*)*)\'' - patterns[Constants.TokenType.TagMarker] = '\\#' - patterns[Constants.TokenType.LeftParen] = '\\(' - patterns[Constants.TokenType.RightParen] = '\\)' - patterns[Constants.TokenType.EqualTo] = '(==|is(?!\\w)|eq(?!\\w))' - patterns[Constants.TokenType.EqualToOrAssign] = '(=|to(?!\\w))' - patterns[Constants.TokenType.NotEqualTo] = '(\\!=|neq(?!\\w))' - patterns[Constants.TokenType.GreaterThanOrEqualTo] = '(\\>=|gte(?!\\w))' - patterns[Constants.TokenType.GreaterThan] = '(\\>|gt(?!\\w))' - patterns[Constants.TokenType.LessThanOrEqualTo] = '(\\<=|lte(?!\\w))' - patterns[Constants.TokenType.LessThan] = '(\\<|lt(?!\\w))' - patterns[Constants.TokenType.AddAssign] = '\\+=' - patterns[Constants.TokenType.MinusAssign] = '\\-=' - patterns[Constants.TokenType.MultiplyAssign] = '\\*=' - patterns[Constants.TokenType.DivideAssign] = '\\/=' - patterns[Constants.TokenType.Add] = '\\+' - patterns[Constants.TokenType.Minus] = '\\-' - patterns[Constants.TokenType.Multiply] = '\\*' - patterns[Constants.TokenType.Divide] = '\\/' - patterns[Constants.TokenType.Modulo] = '\\%' - patterns[Constants.TokenType.And] = '(\\&\\&|and(?!\\w))' - patterns[Constants.TokenType.Or] = '(\\|\\||or(?!\\w))' - patterns[Constants.TokenType.Xor] = '(\\^|xor(?!\\w))' - patterns[Constants.TokenType.Not] = '(\\!|not(?!\\w))' - patterns[Constants.TokenType.Variable] = '\\$([A-Za-z0-9_\\.])+' - patterns[Constants.TokenType.Comma] = '\\,' - patterns[Constants.TokenType.TrueToken] = 'true(?!\\w)' - patterns[Constants.TokenType.FalseToken] = 'false(?!\\w)' - patterns[Constants.TokenType.NullToken] = 'null(?!\\w)' - patterns[Constants.TokenType.BeginCommand] = '\\<\\<' - patterns[Constants.TokenType.EndCommand] = '\\>\\>' - patterns[Constants.TokenType.OptionStart] = '\\[\\[' - patterns[Constants.TokenType.OptionEnd] = '\\]\\]' - patterns[Constants.TokenType.OptionDelimit] = '\\|' - patterns[Constants.TokenType.Identifier] = '[a-zA-Z0-9_:\\.]+' - patterns[Constants.TokenType.IfToken] = 'if(?!\\w)' - patterns[Constants.TokenType.ElseToken] = 'else(?!\\w)' - patterns[Constants.TokenType.ElseIf] = 'elseif(?!\\w)' - patterns[Constants.TokenType.EndIf] = 'endif(?!\\w)' - patterns[Constants.TokenType.Set] = 'set(?!\\w)' - patterns[Constants.TokenType.ShortcutOption] = '\\-\\>\\s*' + patterns[Constants.TokenType.Number] = ['\\-?[0-9]+(\\.[0-9+])?', 'any number'] + patterns[Constants.TokenType.Str] = ['\'([^\'\\\\]*(?:\\.[^\'\\\\]*)*)\'', 'any text'] + patterns[Constants.TokenType.TagMarker] = ['\\#', 'a tag #'] + patterns[Constants.TokenType.LeftParen] = ['\\(', 'left parenthesis ('] + patterns[Constants.TokenType.RightParen] = ['\\)', 'right parenthesis )'] + patterns[Constants.TokenType.EqualTo] = ['(==|is(?!\\w)|eq(?!\\w))', '"=", "is" or "eq"'] + patterns[Constants.TokenType.EqualToOrAssign] = ['(=|to(?!\\w))', 'equal to "=" or assign "="'] + patterns[Constants.TokenType.NotEqualTo] = ['(\\!=|neq(?!\\w))', '"!=" or "neq"'] + patterns[Constants.TokenType.GreaterThanOrEqualTo] = ['(\\>=|gte(?!\\w))', '">=" or "gte"'] + patterns[Constants.TokenType.GreaterThan] = ['(\\>|gt(?!\\w))', '">" or "gt"'] + patterns[Constants.TokenType.LessThanOrEqualTo] = ['(\\<=|lte(?!\\w))', '"<=" or "lte"'] + patterns[Constants.TokenType.LessThan] = ['(\\<|lt(?!\\w))', '"<" or "lt"'] + patterns[Constants.TokenType.AddAssign] = ['\\+=', '"+="'] + patterns[Constants.TokenType.MinusAssign] = ['\\-=', '"-="'] + patterns[Constants.TokenType.MultiplyAssign] = ['\\*=', '"*="'] + patterns[Constants.TokenType.DivideAssign] = ['\\/=', '"/="'] + patterns[Constants.TokenType.Add] = ['\\+', '"+"'] + patterns[Constants.TokenType.Minus] = ['\\-', '"-"'] + patterns[Constants.TokenType.Multiply] = ['\\*', '"*"'] + patterns[Constants.TokenType.Divide] = ['\\/', '"/"'] + patterns[Constants.TokenType.Modulo] = ['\\%', '"%"'] + patterns[Constants.TokenType.And] = ['(\\&\\&|and(?!\\w))', '"&&" or "and"'] + patterns[Constants.TokenType.Or] = ['(\\|\\||or(?!\\w))', '"||" or "or"'] + patterns[Constants.TokenType.Xor] = ['(\\^|xor(?!\\w))', '"^" or "xor"'] + patterns[Constants.TokenType.Not] = ['(\\!|not(?!\\w))', '"!" or "not"'] + patterns[Constants.TokenType.Variable] = ['\\$([A-Za-z0-9_\\.])+', 'any variable'] + patterns[Constants.TokenType.Comma] = ['\\,', '","'] + patterns[Constants.TokenType.TrueToken] = ['true(?!\\w)', '"true"'] + patterns[Constants.TokenType.FalseToken] = ['false(?!\\w)', '"false"'] + patterns[Constants.TokenType.NullToken] = ['null(?!\\w)', '"null"'] + patterns[Constants.TokenType.BeginCommand] = ['\\<\\<', 'beginning of a command "<<"'] + patterns[Constants.TokenType.EndCommand] = ['\\>\\>', 'ending of a command ">>"'] + patterns[Constants.TokenType.OptionStart] = ['\\[\\[', 'start of an option "[["'] + patterns[Constants.TokenType.OptionEnd] = ['\\]\\]', 'end of an option "]]"'] + patterns[Constants.TokenType.OptionDelimit] = ['\\|', 'middle of an option "|"'] + patterns[Constants.TokenType.Identifier] = ['[a-zA-Z0-9_:\\.]+', 'any reference to another node'] + patterns[Constants.TokenType.IfToken] = ['if(?!\\w)', '"if"'] + patterns[Constants.TokenType.ElseToken] = ['else(?!\\w)', '"else"'] + patterns[Constants.TokenType.ElseIf] = ['elseif(?!\\w)', '"elseif"'] + patterns[Constants.TokenType.EndIf] = ['endif(?!\\w)', '"endif"'] + patterns[Constants.TokenType.Set] = ['set(?!\\w)', '"set"'] + patterns[Constants.TokenType.ShortcutOption] = ['\\-\\>\\s*', '"->"'] #compound states var shortcut_option : String= SHORTCUT + DASH + OPTION @@ -173,10 +173,7 @@ func create_states(): for stateKey in _states.keys(): _states[stateKey].stateName = stateKey - pass - -func tokenize(text:String)->Array: - +func tokenize(text, title, filename): _indentStack.clear() _indentStack.push_front(IntBoolPair.new(0,false)) _shouldTrackIndent = false @@ -191,17 +188,20 @@ func tokenize(text:String)->Array: var line_number : int = 1 for line in lines: - tokens+=tokenize_line(line,line_number) - line_number+=1 + tokens += tokenize_line(line, line_number, title, filename) + line_number += 1 - var endOfInput : Token = Token.new(Constants.TokenType.EndOfInput,_currentState,line_number,0) + var endOfInput = Token.new( + Constants.TokenType.EndOfInput, + _currentState, + line_number, + 0 + ) tokens.append(endOfInput) - # print(tokens) - return tokens -func tokenize_line(line:String, line_number : int)->Array: +func tokenize_line(line, line_number, title, filename): var tokenStack : Array = [] var freshLine = line.replace('\t',' ').replace('\r','') @@ -214,7 +214,13 @@ func tokenize_line(line:String, line_number : int)->Array: #we add an indenation token to record indent level _indentStack.push_front(IntBoolPair.new(indentation,true)) - var indent : Token = Token.new(Constants.TokenType.Indent,_currentState,line_number,prevIndentation.key) + var indent : Token = Token.new( + Constants.TokenType.Indent, + _currentState, + filename, + line_number, + prevIndentation.key + ) indent.value = '%*s' % [indentation - prevIndentation.key,''] _shouldTrackIndent = false @@ -246,7 +252,7 @@ func tokenize_line(line:String, line_number : int)->Array: var matched : bool = false for rule in _currentState.rules: - var found : RegExMatch = rule.regex.search(freshLine, column) + var found = rule.regex.search(freshLine, column) if !found: continue @@ -273,9 +279,9 @@ func tokenize_line(line:String, line_number : int)->Array: # column = startIndex - var endIndex : int = found.get_start() + found.get_string().length() + var end_index = found.get_start() + found.get_string().length() - tokenText = freshLine.substr(startIndex,endIndex-startIndex) + tokenText = freshLine.substr(startIndex, end_index - startIndex) else: tokenText = found.get_string() @@ -284,36 +290,51 @@ func tokenize_line(line:String, line_number : int)->Array: #pre-proccess string if rule.tokenType == Constants.TokenType.Str: - tokenText = tokenText.substr(1,tokenText.length() - 2) + tokenText = tokenText.substr(1, tokenText.length() - 2) tokenText = tokenText.replace('\\\\', '\\') tokenText = tokenText.replace('\\\'','\'') - var token : Token = Token.new(rule.tokenType,_currentState,line_number,column,tokenText) + var token = Token.new( + rule.tokenType, + _currentState, + filename, + line_number, + column, + tokenText + ) token.delimitsText = rule.delimitsText tokenStack.push_front(token) - if rule.enterState != null && rule.enterState.length() > 0: - - if !_states.has(rule.enterState): - printerr('State[%s] not known - line(%s) col(%s)'%[rule.enterState,line_number,column]) + if rule.enterState != null and rule.enterState.length() > 0: + if not _states.has(rule.enterState): + printerr('State[%s] not known - line(%s) col(%s)' % [rule.enterState, line_number, column]) return [] enter_state(_states[rule.enterState]) if _shouldTrackIndent: if _indentStack.front().key < indentation: - _indentStack.append(IntBoolPair.new(indentation,false)) + _indentStack.append(IntBoolPair.new(indentation, false)) matched = true break - if !matched: - # TODO: Send out some helpful messages - printerr('expectedTokens [%s] - line(%s) col(%s)'%['refineErrors.Lexer.tokenize_line',line_number,column]) - return [] + if not matched: + var rules = [] + for rule in _currentState.rules: + rules.append('"%s" (%s)' % [Constants.token_type_name(rule.tokenType), rule.human_readable_identifier]) - var lastWhiteSpace : RegExMatch = whitespace.search(line,column) + var error_data = [ + PoolStringArray(rules).join(', ') if rules.size() == 1 else PoolStringArray(rules.slice(0, rules.size() - 2)).join(', ') + ' or %s' % rules[-1], + filename, + title, + line_number, + column + ] + assert(false, 'Expected %s in file %s in node "%s" on line #%d (column #%d)' % error_data) + + var lastWhiteSpace = whitespace.search(line, column) if lastWhiteSpace: column += lastWhiteSpace.get_string().length() @@ -339,20 +360,22 @@ func enter_state(state:LexerState): _shouldTrackIndent = true class Token: - var type : int - var value : String + var type = -1 + var value = '' - var line_number : int - var column : int - var text : String + var filename = '' + var line_number = -1 + var column = -1 + var text = '' - var delimitsText : bool= false - var paramCount : int - var lexerState : String + var delimitsText = false + var paramCount = -1 + var lexerState = '' - func _init(type:int,state: LexerState, line_number:int = -1,column:int = -1,value:String =''): + func _init(type, state, filename, line_number = -1, column = -1, value = ''): self.type = type self.lexerState = state.stateName + self.filename = filename self.line_number = line_number self.column = column self.value = value @@ -360,7 +383,6 @@ class Token: func _to_string(): return '%s (%s) at %s:%s (state: %s)' % [Constants.token_type_name(type),value,line_number,column,lexerState] - class LexerState: var stateName : String @@ -372,9 +394,9 @@ class LexerState: self.patterns = patterns func add_transition(type : int, state : String = '',delimitText : bool = false)->Rule: - var pattern = '\\G%s' % patterns[type] + var pattern = '\\G%s' % patterns[type][0] # print('pattern = %s' % pattern) - var rule = Rule.new(type,pattern,state,delimitText) + var rule = Rule.new(type, pattern, patterns[type][1], state, delimitText) rules.append(rule) return rule @@ -409,16 +431,18 @@ class Rule: var tokenType : int var isTextRule : bool var delimitsText : bool + var human_readable_identifier = '' - func _init(type : int , regex : String, enterState : String, delimitsText:bool): + func _init(type : int , regex : String, human_readable_identifier, enterState : String, delimitsText:bool): self.tokenType = type self.regex = RegEx.new() self.regex.compile(regex) + self.human_readable_identifier = human_readable_identifier self.enterState = enterState self.delimitsText = delimitsText func _to_string(): - return '[Rule : %s - %s]' % [Constants.token_type_name(tokenType),regex] + return '[Rule : %s (%s) - %s]' % [Constants.token_type_name(tokenType), human_readable_identifier, regex] class IntBoolPair: var key : int diff --git a/addons/Wol/core/compiler/parser.gd b/addons/Wol/core/compiler/parser.gd index 31ac190..52aa620 100644 --- a/addons/Wol/core/compiler/parser.gd +++ b/addons/Wol/core/compiler/parser.gd @@ -4,9 +4,11 @@ const Constants = preload('res://addons/Wol/core/constants.gd') const Lexer = preload('res://addons/Wol/core/compiler/lexer.gd') var _tokens = [] +var title = '' -func _init(tokens): +func _init(tokens, title): self._tokens = tokens + self.title = title enum Associativity { Left, @@ -37,7 +39,7 @@ func expect_symbol(token_types = []): if token_types.size() == 0: if token.type == Constants.TokenType.EndOfInput: - printerr('Unexpected end of input.') + assert(false, 'Unexpected end of input') return null return token @@ -45,8 +47,29 @@ func expect_symbol(token_types = []): if token.type == type: return token - printerr('Unexpexted token: expected[ %s ] but got [ %s ]' % token_types + [token.type]) - return + var token_names = [] + for type in token_types: + token_names.append(Constants.token_type_name(type)) + + + var error_guess = '\n' + + if Constants.token_type_name(token.type) == 'Identifier' \ + and Constants.token_type_name(token_types[0]) == 'OptionEnd': + error_guess += 'Does the node your refer to have a space in it?' + else: + error_guess = '' + + var error_data = [ + token.filename, + title, + token.line_number, + token.column, + PoolStringArray(token_names).join(', '), + Constants.token_type_name(token.type), + error_guess + ] + assert(false, '[%s|%s:%d:%d]:\nExpected token "%s" but got "%s"%s' % error_data) static func tab(indent_level, input, newline = true): return '%*s| %s%s' % [indent_level * 2, '', input, '' if not newline else '\n'] @@ -55,12 +78,15 @@ func tokens(): return _tokens class ParseNode: + var name = '' + var parent var line_number = -1 var tags = [] func _init(parent, parser): self.parent = parent + var tokens = parser.tokens() as Array if tokens.size() > 0: line_number = tokens.front().line_number @@ -77,7 +103,7 @@ class ParseNode: func get_node_parent(): var node = self while node != null: - if node.has_method('wol_node'): + if node is ParseNode: return node as WolNode node = node.parent return null @@ -90,7 +116,6 @@ class ParseNode: #this is a Wol Node - contains all the text class WolNode extends ParseNode: - var name = '' var source = '' var editor_node_tags = [] @@ -140,7 +165,7 @@ class Statement extends ParseNode: elif Assignment.can_parse(parser): assignment = Assignment.new(self, parser) - type = Type.Assignment_statement + type = Type.AssignmentStatement elif ShortcutOptionGroup.can_parse(parser): shortcut_option_group = ShortcutOptionGroup.new(self, parser) @@ -175,7 +200,7 @@ class Statement extends ParseNode: info.append(block.tree_string(indent_level)) Type.IfStatement: info.append(if_statement.tree_string(indent_level)) - Type.Assignment_statement: + Type.AssignmentStatement: info.append(assignment.tree_string(indent_level)) Type.OptionStatement: info.append(option_statement.tree_string(indent_level)) @@ -194,7 +219,7 @@ class CustomCommand extends ParseNode: enum Type { Expression, - Client_command + ClientCommand } var type = -1 @@ -215,21 +240,21 @@ 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.Left_paren): + && command_tokens[1].type == Constants.TokenType.LeftParen): var p = get_script().new(command_tokens, parser.library) - var expression = Expression_node.parse(self, p) + var expression = ExpressionNode.parse(self, p) type = Type.Expression self.expression = expression else: #otherwise evaluuate command - type = Type.Client_command + type = Type.ClientCommand self.client_command = command_tokens[0].value func tree_string(indent_level): match type: Type.Expression: return tab(indent_level,'Expression: %s'% expression.tree_string(indent_level+1)) - Type.Client_command: + Type.ClientCommand: return tab(indent_level,'Command: %s' % client_command) return '' @@ -246,10 +271,10 @@ class ShortcutOptionGroup extends ParseNode: # expect one otherwise invalid var index = 1 - options.append(Short_cut_option.new(index, self, parser)) + options.append(ShortCutOption.new(index, self, parser)) index += 1 while parser.next_symbol_is([Constants.TokenType.ShortcutOption]): - options.append(Short_cut_option.new(index, self, parser)) + options.append(ShortCutOption.new(index, self, parser)) index += 1 func tree_string(indent_level): @@ -267,7 +292,7 @@ class ShortcutOptionGroup extends ParseNode: static func can_parse(parser): return parser.next_symbol_is([Constants.TokenType.ShortcutOption]) -class Short_cut_option extends ParseNode: +class ShortCutOption extends ParseNode: var label = '' var condition var node @@ -285,7 +310,7 @@ class Short_cut_option extends ParseNode: if parser.next_symbols_are([Constants.TokenType.BeginCommand, Constants.TokenType.IfToken]): parser.expect_symbol([Constants.TokenType.BeginCommand]) parser.expect_symbol([Constants.TokenType.IfToken]) - condition = Expression_node.parse(self, parser) + condition = ExpressionNode.parse(self, parser) parser.expect_symbol([Constants.TokenType.EndCommand]) elif parser.next_symbol_is([Constants.TokenType.TagMarker]): @@ -299,7 +324,7 @@ class Short_cut_option extends ParseNode: if parser.next_symbol_is([Constants.TokenType.Indent]): parser.expect_symbol([Constants.TokenType.Indent]) - node = WolNode.new('%s.%s' %[self.get_node_parent().name , index], self, parser) + node = WolNode.new('%s.%s' % [parent.name, index], self, parser) parser.expect_symbol([Constants.TokenType.Dedent]) @@ -359,7 +384,6 @@ class OptionStatement extends ParseNode: var label = '' func _init(parent, parser).(parent, parser): - var strings = [] # NOTE: parse [[LABEL @@ -397,14 +421,14 @@ class IfStatement extends ParseNode: parser.expect_symbol([Constants.TokenType.BeginCommand]) parser.expect_symbol([Constants.TokenType.IfToken]) - prime.expression = Expression_node.parse(self, parser) + prime.expression = ExpressionNode.parse(self, parser) parser.expect_symbol([Constants.TokenType.EndCommand]) #read statements until 'endif' or 'else' or 'else if' var statements = []#statement - while not parser.next_symbols_are([Constants.TokenType.BeginCommand, Constants.TokenType.End_if]) \ - and not parser.next_symbols_are([Constants.TokenType.BeginCommand, Constants.TokenType.Else_token]) \ - and not parser.next_symbols_are([Constants.TokenType.BeginCommand, Constants.TokenType.Else_if]): + while not parser.next_symbols_are([Constants.TokenType.BeginCommand, Constants.TokenType.EndIf]) \ + and not parser.next_symbols_are([Constants.TokenType.BeginCommand, Constants.TokenType.ElseToken]) \ + and not parser.next_symbols_are([Constants.TokenType.BeginCommand, Constants.TokenType.ElseIf]): statements.append(Statement.new(self, parser)) @@ -416,20 +440,20 @@ class IfStatement extends ParseNode: clauses.append(prime) #handle all else if - while parser.next_symbols_are([Constants.TokenType.BeginCommand, Constants.TokenType.Else_if]): + while parser.next_symbols_are([Constants.TokenType.BeginCommand, Constants.TokenType.ElseIf]): var clause_elif = Clause.new() #parse condition syntax parser.expect_symbol([Constants.TokenType.BeginCommand]) - parser.expect_symbol([Constants.TokenType.Else_if]) - clause_elif.expression = Expression_node.parse(self, parser) + parser.expect_symbol([Constants.TokenType.ElseIf]) + clause_elif.expression = ExpressionNode.parse(self, parser) parser.expect_symbol([Constants.TokenType.EndCommand]) var elif_statements = []#statement - while not parser.next_symbols_are([Constants.TokenType.BeginCommand, Constants.TokenType.End_if]) \ - and not parser.next_symbols_are([Constants.TokenType.BeginCommand, Constants.TokenType.Else_token]) \ - and not parser.next_symbols_are([Constants.TokenType.BeginCommand, Constants.TokenType.Else_if]): + while not parser.next_symbols_are([Constants.TokenType.BeginCommand, Constants.TokenType.EndIf]) \ + and not parser.next_symbols_are([Constants.TokenType.BeginCommand, Constants.TokenType.ElseToken]) \ + and not parser.next_symbols_are([Constants.TokenType.BeginCommand, Constants.TokenType.ElseIf]): elif_statements.append(Statement.new(self, parser)) @@ -442,17 +466,17 @@ class IfStatement extends ParseNode: #handle else if exists if (parser.next_symbols_are([Constants.TokenType.BeginCommand, - Constants.TokenType.Else_token, Constants.TokenType.EndCommand])): + Constants.TokenType.ElseToken, Constants.TokenType.EndCommand])): #expect no expression - just <> parser.expect_symbol([Constants.TokenType.BeginCommand]) - parser.expect_symbol([Constants.TokenType.Else_token]) + parser.expect_symbol([Constants.TokenType.ElseToken]) parser.expect_symbol([Constants.TokenType.EndCommand]) #parse until hit endif var clause_else = Clause.new() var el_statements = []#statement - while !parser.next_symbols_are([Constants.TokenType.BeginCommand, Constants.TokenType.End_if]): + while not parser.next_symbols_are([Constants.TokenType.BeginCommand, Constants.TokenType.EndIf]): el_statements.append(Statement.new(self, parser)) clause_else.statements = el_statements @@ -464,7 +488,7 @@ class IfStatement extends ParseNode: #finish parser.expect_symbol([Constants.TokenType.BeginCommand]) - parser.expect_symbol([Constants.TokenType.End_if]) + parser.expect_symbol([Constants.TokenType.EndIf]) parser.expect_symbol([Constants.TokenType.EndCommand]) @@ -488,7 +512,7 @@ class IfStatement extends ParseNode: return parser.next_symbols_are([Constants.TokenType.BeginCommand, Constants.TokenType.IfToken]) pass -class Value_node extends ParseNode: +class ValueNode extends ParseNode: const Value = preload('res://addons/Wol/core/value.gd') const Lexer = preload('res://addons/Wol/core/compiler/lexer.gd') var value @@ -508,15 +532,15 @@ class Value_node extends ParseNode: value = Value.new(float(token.value)) Constants.TokenType.Str: value = Value.new(token.value) - Constants.TokenType.False_token: + Constants.TokenType.FalseToken: value = Value.new(false) - Constants.TokenType.True_token: + Constants.TokenType.TrueToken: value = Value.new(true) Constants.TokenType.Variable: value = Value.new(null) - value.type = Constants.Value_type.Variable + value.type = Constants.ValueType.Variable value.variable = token.value - Constants.TokenType.Null_token: + Constants.TokenType.NullToken: value = Value.new(null) _: printerr('%s, Invalid token type' % token.name) @@ -529,33 +553,32 @@ class Value_node extends ParseNode: # math (1 + 2 - 5 * 3 / 10 % 2) # Identifiers # Values -class Expression_node extends ParseNode: +class ExpressionNode extends ParseNode: var type var value var function - var params = []#Expression_node + var params = []#ExpressionNode func _init(parent, parser, value, function = '', params = []).(parent, parser): #no function - means value if value != null: - self.type = Constants.Expression_type.Value + self.type = Constants.ExpressionType.Value self.value = value else:#function - self.type = Constants.Expression_type.Function_call + self.type = Constants.ExpressionType.FunctionCall self.function = function self.params = params func tree_string(indent_level): var info = [] match type: - Constants.Expression_type.Value: + Constants.ExpressionType.Value: return value.tree_string(indent_level) - Constants.Expression_type.Function_call: + Constants.ExpressionType.FunctionCall: info.append(tab(indent_level,'Func[%s - params(%s)]:{'%[function, params.size()])) for param in params: - #print('----> %s param_size:%s'%[(function) , params.size()]) info.append(param.tree_string(indent_level+1)) info.append(tab(indent_level,'}')) @@ -575,13 +598,13 @@ class Expression_node extends ParseNode: Constants.TokenType.Number, Constants.TokenType.Variable, Constants.TokenType.Str, - Constants.TokenType.Left_paren, - Constants.TokenType.Right_paren, + Constants.TokenType.LeftParen, + Constants.TokenType.RightParen, Constants.TokenType.Identifier, Constants.TokenType.Comma, - Constants.TokenType.True_token, - Constants.TokenType.False_token, - Constants.TokenType.Null_token + Constants.TokenType.TrueToken, + Constants.TokenType.FalseToken, + Constants.TokenType.NullToken ] valid_types += Operator.op_types() valid_types.invert() @@ -595,9 +618,9 @@ class Expression_node extends ParseNode: if next.type == Constants.TokenType.Variable \ or next.type == Constants.TokenType.Number \ or next.type == Constants.TokenType.Str \ - or next.type == Constants.TokenType.False_token \ - or next.type == Constants.TokenType.True_token \ - or next.type == Constants.TokenType.Null_token: + or next.type == Constants.TokenType.FalseToken \ + or next.type == Constants.TokenType.TrueToken \ + or next.type == Constants.TokenType.NullToken: #output primitives rpn.append(next) @@ -606,12 +629,12 @@ class Expression_node extends ParseNode: func_stack.push_back(next) #next token is parent - left - next = parser.expect_symbol([Constants.TokenType.Left_paren]) + next = parser.expect_symbol([Constants.TokenType.LeftParen]) op_stack.push_back(next) elif next.type == Constants.TokenType.Comma: #resolve sub expression before moving on - while op_stack.back().type != Constants.TokenType.Left_paren: + while op_stack.back().type != Constants.TokenType.LeftParen: var p = op_stack.pop_back() if p == null: printerr('unbalanced parenthesis %s ' % next.name) @@ -621,7 +644,7 @@ class Expression_node extends ParseNode: #next token in op_stack left paren # next parser token not allowed to be right paren or comma - if parser.next_symbol_is([Constants.TokenType.Right_paren, + if parser.next_symbol_is([Constants.TokenType.RightParen, Constants.TokenType.Comma]): printerr('Expected Expression : %s' % parser.tokens().front().name) @@ -643,34 +666,33 @@ class Expression_node extends ParseNode: if next.type == Constants.TokenType.Minus: if last == null \ - or last.type == Constants.TokenType.Left_paren \ + or last.type == Constants.TokenType.LeftParen \ or Operator.is_op(last.type): #unary minus - next.type = Constants.TokenType.Unary_minus + next.type = Constants.TokenType.UnaryMinus #cannot assign inside expression # x = a is the same as x == a if next.type == Constants.TokenType.EqualToOrAssign: next.type = Constants.TokenType.EqualTo - #operator precedence - while (Expression_node.is_apply_precedence(next.type, op_stack)): + while (ExpressionNode.is_apply_precedence(next.type, op_stack)): var op = op_stack.pop_back() rpn.append(op) op_stack.push_back(next) - elif next.type == Constants.TokenType.Left_paren: + elif next.type == Constants.TokenType.LeftParen: #entered parenthesis sub expression op_stack.push_back(next) - elif next.type == Constants.TokenType.Right_paren: + elif next.type == Constants.TokenType.RightParen: #leaving sub expression # resolve order of operations - while op_stack.back().type != Constants.TokenType.Left_paren: + while op_stack.back().type != Constants.TokenType.LeftParen: rpn.append(op_stack.pop_back()) if op_stack.back() == null: - printerr('Unbalanced parenthasis #Right_paren. Parser.Expression_node') + printerr('Unbalanced parenthasis #RightParen. Parser.ExpressionNode') op_stack.pop_back() @@ -679,7 +701,7 @@ class Expression_node extends ParseNode: #last token == left paren this == no params #else #we have more than 1 param - if last.type != Constants.TokenType.Left_paren: + if last.type != Constants.TokenType.LeftParen: func_stack.back().param_count+=1 rpn.append(op_stack.pop_back()) @@ -698,10 +720,9 @@ class Expression_node extends ParseNode: #build expression tree var first = rpn.front() - var eval_stack = []#Expression_node + var eval_stack = []#ExpressionNode while rpn.size() > 0: - var next = rpn.pop_front() if Operator.is_op(next.type): #operation @@ -710,7 +731,7 @@ class Expression_node extends ParseNode: if eval_stack.size() < info.arguments: printerr('Error parsing : Not enough arguments for %s [ got %s expected - was %s]'%[Constants.token_type_name(next.type), eval_stack.size(), info.arguments]) - var params = []#Expression_node + var params = []#ExpressionNode for i in range(info.arguments): params.append(eval_stack.pop_back()) @@ -718,7 +739,7 @@ class Expression_node extends ParseNode: var function = get_func_name(next.type) - var expression = Expression_node.new(parent, parser, null, function, params) + var expression = ExpressionNode.new(parent, parser, null, function, params) eval_stack.append(expression) @@ -727,19 +748,19 @@ class Expression_node extends ParseNode: var function = next.value - var params = []#Expression_node + var params = []#ExpressionNode for i in range(next.param_count): params.append(eval_stack.pop_back()) params.invert() - var expression = Expression_node.new(parent, parser, null, function, params) + var expression = ExpressionNode.new(parent, parser, null, function, params) eval_stack.append(expression) else: #raw value - var value = Value_node.new(parent, parser, next) - var expression = Expression_node.new(parent, parser, value) + var value = ValueNode.new(parent, parser, next) + var expression = ExpressionNode.new(parent, parser, value) eval_stack.append(expression) @@ -796,7 +817,7 @@ class Assignment extends ParseNode: parser.expect_symbol([Constants.TokenType.Set]) destination = parser.expect_symbol([Constants.TokenType.Variable]).value operation = parser.expect_symbol(Assignment.valid_ops()).type - value = Expression_node.parse(self, parser) + value = ExpressionNode.parse(self, parser) parser.expect_symbol([Constants.TokenType.EndCommand]) func tree_string(indent_level): @@ -817,10 +838,10 @@ class Assignment extends ParseNode: static func valid_ops(): return [ Constants.TokenType.EqualToOrAssign, - Constants.TokenType.Add_assign, - Constants.TokenType.Minus_assign, - Constants.TokenType.Divide_assign, - Constants.TokenType.Multiply_assign + Constants.TokenType.AddAssign, + Constants.TokenType.MinusAssign, + Constants.TokenType.DivideAssign, + Constants.TokenType.MultiplyAssign ] class Operator extends ParseNode: @@ -848,22 +869,22 @@ class Operator extends ParseNode: var TokenType = Constants.TokenType match op: - TokenType.Not, TokenType.Unary_minus: - return Operator_info.new(Associativity.Right, 30, 1) + TokenType.Not, TokenType.UnaryMinus: + return OperatorInfo.new(Associativity.Right, 30, 1) TokenType.Multiply, TokenType.Divide, TokenType.Modulo: - return Operator_info.new(Associativity.Left, 20, 2) + return OperatorInfo.new(Associativity.Left, 20, 2) TokenType.Add, TokenType.Minus: - return Operator_info.new(Associativity.Left, 15, 2) + return OperatorInfo.new(Associativity.Left, 15, 2) TokenType.GreaterThan, TokenType.LessThan, TokenType.GreaterThanOrEqualTo, TokenType.LessThanOrEqualTo: - return Operator_info.new(Associativity.Left, 10, 2) + return OperatorInfo.new(Associativity.Left, 10, 2) TokenType.EqualTo, TokenType.EqualToOrAssign, TokenType.NotEqualTo: - return Operator_info.new(Associativity.Left, 5, 2) + return OperatorInfo.new(Associativity.Left, 5, 2) TokenType.And: - return Operator_info.new(Associativity.Left, 4, 2) + return OperatorInfo.new(Associativity.Left, 4, 2) TokenType.Or: - return Operator_info.new(Associativity.Left, 3, 2) + return OperatorInfo.new(Associativity.Left, 3, 2) TokenType.Xor: - return Operator_info.new(Associativity.Left, 2, 2) + return OperatorInfo.new(Associativity.Left, 2, 2) _: printerr('Unknown operator: %s' % op.name) return null @@ -874,7 +895,7 @@ class Operator extends ParseNode: static func op_types(): return [ Constants.TokenType.Not, - Constants.TokenType.Unary_minus, + Constants.TokenType.UnaryMinus, Constants.TokenType.Add, Constants.TokenType.Minus, @@ -897,7 +918,7 @@ class Operator extends ParseNode: ] -class Operator_info: +class OperatorInfo: var associativity var precedence = -1 var arguments = -1 diff --git a/addons/Wol/core/constants.gd b/addons/Wol/core/constants.gd index 12f374d..9eb7725 100644 --- a/addons/Wol/core/constants.gd +++ b/addons/Wol/core/constants.gd @@ -198,3 +198,9 @@ static func bytecode_name(bytecode): 'Stop', 'RunNode' ][bytecode] + +static func token_name(type): + for key in TokenType.keys(): + if TokenType[key] == type: + return key + return '' diff --git a/addons/Wol/core/dialogue.gd b/addons/Wol/core/dialogue.gd index bcd92a8..b46f461 100644 --- a/addons/Wol/core/dialogue.gd +++ b/addons/Wol/core/dialogue.gd @@ -1,7 +1,6 @@ extends Node const DEFAULT_START = 'Start' -const FMF_PLACEHOLDE = '' const Constants = preload('res://addons/Wol/core/constants.gd') const StandardLibrary = preload('res://addons/Wol/core/libraries/standard.gd') @@ -11,24 +10,19 @@ const Value = preload('res://addons/Wol/core/value.gd') var _variableStorage -var _debugLog -var _errLog - var _program var library -var _vm : VirtualMachine +var _vm -var _visitedNodeCount : Dictionary = {} +var _visitedNodeCount = {} -var executionComplete : bool +var executionComplete func _init(variableStorage): _variableStorage = variableStorage _vm = VirtualMachine.new(self) library = WolLibrary.new() - _debugLog = funcref(self, 'dlog') - _errLog = funcref(self, 'elog') executionComplete = false # import the standard library @@ -41,13 +35,6 @@ func _init(variableStorage): #add function to lib that gets the node visit count library.register_function('visit_count', -1, funcref(self, 'node_visit_count'), true) - -func dlog(message:String): - print('YARN_DEBUG : %s' % message) - -func elog(message:String): - print('YARN_ERROR : %s' % message) - func is_active(): return get_exec_state() != Constants.ExecutionState.Stopped @@ -55,15 +42,19 @@ func is_active(): func get_exec_state(): return _vm.executionState -func set_selected_option(option:int): +func set_selected_option(option): _vm.set_selected_option(option) -func set_node(name:String = DEFAULT_START): +func set_node(name = DEFAULT_START): _vm.set_node(name) +func start(): + if _vm.executionState == Constants.ExecutionState.Stopped: + _vm.resume() + func resume(): - if _vm.executionState == Constants.ExecutionState.Running: - print('BLOCKED') + if _vm.executionState == Constants.ExecutionState.Running \ + or _vm.executionState == Constants.ExecutionState.Stopped: return _vm.resume() @@ -81,12 +72,10 @@ func current_node(): func get_node_id(name): if _program.nodes.size() == 0: - _errLog.call_func('No nodes loaded') return '' if _program.nodes.has(name): return 'id:'+name else: - _errLog.call_func('No node named [%s] exists' % name) return '' func unloadAll(clear_visited:bool = true): @@ -116,7 +105,7 @@ func is_node_visited(node = _vm.current_node_name()): func node_visit_count(node = _vm.current_node_name()): if node is Value: - node = _program.wolStrings[node.value()].text + node = _program.strings[node.value()].text var visitCount : int = 0 if _visitedNodeCount.has(node): diff --git a/addons/Wol/core/dialogue/command.gd b/addons/Wol/core/dialogue/command.gd deleted file mode 100644 index 2839281..0000000 --- a/addons/Wol/core/dialogue/command.gd +++ /dev/null @@ -1,6 +0,0 @@ -extends Object - -var command : String - -func _init(command : String): - self.command = command \ No newline at end of file diff --git a/addons/Wol/core/dialogue/format_function.gd b/addons/Wol/core/dialogue/format_function.gd deleted file mode 100644 index 1eccaec..0000000 --- a/addons/Wol/core/dialogue/format_function.gd +++ /dev/null @@ -1,16 +0,0 @@ -extends Node - - -# Declare member variables here. Examples: -# var a = 2 -# var b = "text" - - -# Called when the node enters the scene tree for the first time. -func _ready(): - pass # Replace with function body. - - -# Called every frame. 'delta' is the elapsed time since the previous frame. -#func _process(delta): -# pass diff --git a/addons/Wol/core/dialogue/line.gd b/addons/Wol/core/dialogue/line.gd deleted file mode 100644 index b18b471..0000000 --- a/addons/Wol/core/dialogue/line.gd +++ /dev/null @@ -1,8 +0,0 @@ -extends Object -# class_name DialogueLine - -var id = '' -var substitutions = [] - -func _init(id): - self.id = id diff --git a/addons/Wol/core/dialogue/option.gd b/addons/Wol/core/dialogue/option.gd deleted file mode 100644 index 86d3333..0000000 --- a/addons/Wol/core/dialogue/option.gd +++ /dev/null @@ -1,13 +0,0 @@ -extends Object - -const Line = preload("res://addons/Wol/core/dialogue/line.gd") - -var line : Line -var id : int -var destination : String - -func _init(line : Line,id : int, destination: String): - self.line = line - self.id = id - self.destination = destination - diff --git a/addons/Wol/core/libraries/standard.gd b/addons/Wol/core/libraries/standard.gd index 9f11f8b..fc89bc1 100644 --- a/addons/Wol/core/libraries/standard.gd +++ b/addons/Wol/core/libraries/standard.gd @@ -1,69 +1,69 @@ -extends "res://addons/Wol/core/library.gd" +extends 'res://addons/Wol/core/library.gd' -const Value = preload("res://addons/Wol/core/value.gd") +const Value = preload('res://addons/Wol/core/value.gd') func _init(): - register_function("Add",2,funcref(self,"add"),true) - register_function("Minus",2,funcref(self,"sub"),true) - register_function("UnaryMinus",1,funcref(self,"unary_minus"),true) - register_function("Divide",2,funcref(self,"div"),true) - register_function("Multiply",2,funcref(self,"mul"),true) - register_function("Modulo",2,funcref(self,"mod"),true) - register_function("EqualTo",2,funcref(self,"equal"),true) - register_function("NotEqualTo",2,funcref(self,"noteq"),true) - register_function("GreaterThan",2,funcref(self,"ge"),true) - register_function("GreaterThanOrEqualTo",2,funcref(self,"geq"),true) - register_function("LessThan",2,funcref(self,"le"),true) - register_function("LessThanOrEqualTo",2,funcref(self,"leq"),true) - register_function("And",2,funcref(self,"land"),true) - register_function("Or",2,funcref(self,"lor"),true) - register_function("Xor",2,funcref(self,"xor"),true) - register_function("Not",1,funcref(self,"lnot"),true) + register_function('Add', 2, funcref(self, 'add'), true) + register_function('Minus', 2, funcref(self, 'sub'), true) + register_function('UnaryMinus', 1, funcref(self, 'unary_minus'), true) + register_function('Divide', 2, funcref(self, 'div'), true) + register_function('Multiply', 2, funcref(self, 'mul'), true) + register_function('Modulo', 2, funcref(self, 'mod'), true) + register_function('EqualTo', 2, funcref(self, 'equal'), true) + register_function('NotEqualTo', 2, funcref(self, 'noteq'), true) + register_function('GreaterThan', 2, funcref(self, 'ge'), true) + register_function('GreaterThanOrEqualTo', 2, funcref(self, 'geq'), true) + register_function('LessThan', 2, funcref(self, 'le'), true) + register_function('LessThanOrEqualTo', 2, funcref(self, 'leq'), true) + register_function('And', 2, funcref(self, 'land'), true) + register_function('Or', 2, funcref(self, 'lor'), true) + register_function('Xor', 2, funcref(self, 'xor'), true) + register_function('Not', 1, funcref(self, 'lnot'), true) -func add(param1:Value,param2:Value): +func add(param1, param2): return param1.add(param2) -func sub(param1:Value,param2:Value): +func sub(param1, param2): return param1.sub(param2) -func unary_minus(param1:Value): +func unary_minus(param1): return param1.negative() -func div(param1:Value,param2:Value): +func div(param1, param2): return param1.div(param2) -func mul(param1:Value,param2:Value): +func mul(param1, param2): return param1.mult(param2) -func mod(param1:Value,param2:Value): +func mod(param1, param2): return param1.mod(param2) -func equal(param1:Value,param2:Value): +func equal(param1, param2): return param1.equals(param2) -func noteq(param1:Value,param2:Value): +func noteq(param1, param2): return !param1.equals(param2) -func ge(param1:Value,param2:Value): +func ge(param1, param2): return param1.greater(param2) -func geq(param1:Value,param2:Value): +func geq(param1, param2): return param1.geq(param2) -func le(param1:Value,param2:Value): +func le(param1, param2): return param1.less(param2) -func leq(param1:Value,param2:Value): +func leq(param1, param2): return param1.leq(param2) -func land(param1:Value,param2:Value): - return param1.as_bool() && param2.as_bool() +func land(param1, param2): + return param1.as_bool() and param2.as_bool() -func lor(param1:Value,param2:Value): - return param1.as_bool() || param2.as_bool() +func lor(param1, param2): + return param1.as_bool() or param2.as_bool() -func xor(param1:Value,param2:Value): +func xor(param1, param2): return param1.as_bool() != param2.as_bool() -func lnot(param1:Value): - return !param1.as_bool() \ No newline at end of file +func lnot(param1): + return not param1.as_bool() diff --git a/addons/Wol/core/program.gd b/addons/Wol/core/program.gd index 4ac6ab6..8191ad3 100644 --- a/addons/Wol/core/program.gd +++ b/addons/Wol/core/program.gd @@ -22,6 +22,22 @@ class Line: self.implicit = implicit self.meta = meta +class Option: + var line + var id = -1 + var destination = '' + + func _init(line, id, destination): + self.line = line + self.id = id + self.destination = destination + +class Command: + var command = '' + + func _init(command): + self.command = command + class WolNode: var name = '' var instructions = [] diff --git a/addons/Wol/core/virtual_machine.gd b/addons/Wol/core/virtual_machine.gd index ecf7f0f..b0eacc9 100644 --- a/addons/Wol/core/virtual_machine.gd +++ b/addons/Wol/core/virtual_machine.gd @@ -1,15 +1,7 @@ extends Node const Constants = preload('res://addons/Wol/core/constants.gd') - -var FunctionInfo = load('res://addons/Wol/core/function_info.gd') var Value = load('res://addons/Wol/core/value.gd') -var WolProgram = load('res://addons/Wol/core/program/program.gd') -var WolNode = load('res://addons/Wol/core/program/wol_node.gd') -var Instruction = load('res://addons/Wol/core/program/instruction.gd') -var Line = load('res://addons/Wol/core/dialogue/line.gd') -var Command = load('res://addons/Wol/core/dialogue/command.gd') -var Option = load('res://addons/Wol/core/dialogue/option.gd') const EXECUTION_COMPLETE : String = 'execution_complete_command' @@ -54,15 +46,12 @@ func set_node(name:String) -> bool: printerr('No node named %s has been loaded' % name) return false - _dialogue.dlog('Running node %s' % name) - _currentNode = _program.nodes[name] reset() _state.currentNodeName = name nodeStartHandler.call_func(name) return true - func current_node_name()->String: return _currentNode.nodeName @@ -86,8 +75,8 @@ func set_selected_option(id): printerr('Unable to select option when dialogue not waiting for option') return false - if id < 0 || id >= _state.currentOptions.size(): - printerr('%d is not a valid option '%id) + if id < 0 or id >= _state.currentOptions.size(): + printerr('%d is not a valid option ' % id) return false var destination = _state.currentOptions[id].value @@ -99,40 +88,39 @@ func set_selected_option(id): return true -func has_options()->bool: - return _state.currentOptions.size() > 0 - func reset(): _state = VmState.new() -#continue execution -func resume()->bool: - if _currentNode == null : +func get_next_instruction(): + return null if _currentNode.instructions.size() - 1 <= _state.programCounter else _currentNode.instructions[_state.programCounter + 1] + +func resume(): + if _currentNode == null: printerr('Cannot run dialogue with no node selected') return false + if executionState == Constants.ExecutionState.WaitingForOption: printerr('Cannot run while waiting for option') return false - if lineHandler == null : + if lineHandler == null: printerr('Cannot run without a lineHandler') return false - if optionsHandler == null : + if optionsHandler == null: printerr('Cannot run without an optionsHandler') return false - if commandHandler == null : + if commandHandler == null: printerr('Cannot run without an commandHandler') return false - if nodeStartHandler == null : + if nodeStartHandler == null: printerr('Cannot run without a nodeStartHandler') return false - if nodeCompleteHandler == null : + if nodeCompleteHandler == null: printerr('Cannot run without an nodeCompleteHandler') return false - executionState = Constants.ExecutionState.Running #execute instruction until something cool happens @@ -140,14 +128,13 @@ func resume()->bool: var currentInstruction = _currentNode.instructions[_state.programCounter] run_instruction(currentInstruction) - _state.programCounter+=1 + _state.programCounter += 1 if _state.programCounter >= _currentNode.instructions.size(): nodeCompleteHandler.call_func(_currentNode.nodeName) executionState = Constants.ExecutionState.Stopped reset() dialogueCompleteHandler.call_func() - _dialogue.dlog('Run Complete') return true @@ -170,8 +157,8 @@ func run_instruction(instruction)->bool: #look up string from string table #pass it to client as line var key = instruction.operands[0].value - - var line = Line.new(key) + + var line = _program.strings[key] #the second operand is the expression count # of format function @@ -180,7 +167,6 @@ func run_instruction(instruction)->bool: var pause : int = lineHandler.call_func(line) - if pause == Constants.HandlerState.PauseExecution: executionState = Constants.ExecutionState.Suspended @@ -190,7 +176,7 @@ func run_instruction(instruction)->bool: if instruction.operands.size() > 1: pass#add format function - var command = Command.new(commandText) + var command = Program.Command.new(commandText) var pause = commandHandler.call_func(command) as int if pause == Constants.HandlerState.PauseExecution: @@ -275,7 +261,7 @@ func run_instruction(instruction)->bool: Constants.ByteCode.Stop: #stop execution and repost it - nodeCompleteHandler.call_func(_currentNode.nodeName) + nodeCompleteHandler.call_func(_currentNode.name) dialogueCompleteHandler.call_func() executionState = Constants.ExecutionState.Stopped reset() @@ -290,7 +276,7 @@ func run_instruction(instruction)->bool: else : name = instruction.operands[0].value - var pause = nodeCompleteHandler.call_func(_currentNode.nodeName) + var pause = nodeCompleteHandler.call_func(_currentNode.name) set_node(name) _state.programCounter-=1 if pause == Constants.HandlerState.PauseExecution: @@ -300,13 +286,13 @@ func run_instruction(instruction)->bool: # add an option to current state var key = instruction.operands[0].value - var line = Line.new(key) + var line = _program.strings[key] if instruction.operands.size() > 2: pass #formated text options # line to show and node name - _state.currentOptions.append(SimpleEntry.new(line,instruction.operands[1].value)) + _state.currentOptions.append(SimpleEntry.new(line, instruction.operands[1].value)) Constants.ByteCode.ShowOptions: #show options - stop if none @@ -320,7 +306,7 @@ func run_instruction(instruction)->bool: var choices : Array = []#Option for optionIndex in range(_state.currentOptions.size()): var option : SimpleEntry = _state.currentOptions[optionIndex] - choices.append(Option.new(option.key, optionIndex, option.value)) + choices.append(Program.Option.new(option.key, optionIndex, option.value)) #we cant continue until option chosen executionState = Constants.ExecutionState.WaitingForOption @@ -334,7 +320,7 @@ func run_instruction(instruction)->bool: #bytecode messed up woopsise executionState = Constants.ExecutionState.Stopped reset() - printerr('Unknown Bytecode %s '%instruction.operation) + printerr('Unknown Bytecode %s' % instruction.operation) return false return true @@ -353,7 +339,6 @@ class VmState: else: stack.push_back(Value.new(value)) - func pop_value(): return stack.pop_back() @@ -365,8 +350,8 @@ class VmState: class SimpleEntry: var key - var value : String + var value - func _init(key,value:String): + func _init(key, value): self.key = key self.value = value diff --git a/dialogue.yarn b/dialogue.yarn index 9c1cb41..be89d6c 100644 --- a/dialogue.yarn +++ b/dialogue.yarn @@ -1,78 +1,41 @@ title: Start tags: colorID: 0 -position: -1892,-1013 +position: 0, 0 --- -<> -<> +<> +<> -Masami: Gina, this way! #line:5d7a7c -Gina: Patience! I’m coming. ->Still, she didn’t pick up her pace and kept hesitantly looking around.< -Masami: Everything should be fine, let’s just go. We won. -Masami: Unless you are planning on backstabbing me at the finish line, that is. -Gina: How funny. You are not that special. -Masami: Har har. Now start walking or I’ll carry you out the front door. -Masami: I don’t want to give Monokuma the time to come up with a new death game for two while we are trying to get through the exit. -Gina: ... +// remove "to" to trigger error +<> +<> -<> +// Implement inline expressions +Bob: Theresa, {$direction} way! #line:5d7a7c +Theresa: Did you know one + one equals {$one + $one}? +Bob: You wanna go somewhere? -Masami: Wait, the vault door… It’s not open. -Gina: So it seems. -Masami: But that doesn’t make sense! So far we have opened one lock per two deceased. All seven locks should be open by now. ->In desperation I tried the vault handle. I wasn’t exceptionally strong, but I doubted even a weightlifter could open this door whether it was held shut by one or dozen locks.< -Gina: Rather than using your energy by tearing at the 700kg door- -Masami: I wasn’t trying to brute force it. -Gina: Anyway, I would suggest you spare a glance at the reception desk. It appears the staff has given us one last gift. -Masami: The reception desk? - -<> - -Masami: A paper and... -Masami: ... That’s a gun. -Gina: A revolver, in fact. Such an old fashioned choice of firearm. Though, I appreciate the aesthetic. How pretty. -Masami: I don’t like this. Why is it here? ->I took the paper from the table and briskly unfolded it.< -- Congratulations on reaching the final part of the game!- -- Unfortunately, no one remembers a silver medalist.- -- Let’s round up the game with a match of our favorite luck game! Russian roulette should be simple enough.- -- Now then, ladies first!- --Staff- -Masami: Russian roulette… Are you kidding me? -Masami: This can’t be part of the game. This wasn’t in the rules! -Masami: I know the winning condition hasn’t been met but- -Gina: It’s rigged. -Masami: What? ->Gina had picked up the revolver from the table and without better judgement, opened the cylinder for inspection.< -Gina: It’s rigged. -Masami: ... “Ladies first.” -Masami: The cylinder is full, isn't it? ->Gina let out a tired sigh. Her shoulders slumping heavily from disappointment.< -Gina: I got so far. -Gina: I don’t know what led me to believe the outcome would be any different. -Gina: This is such a waste of resources as well. -Masami: Gina, put the revolver down. If the game is rigged, you don’t have to play. The rules won’t matter then. -Gina: Let it rest. -Gina: Like any of it matters. -Masami: What do you mean? ->She didn’t answer.< ->Instead, she absentmindedly fiddled with the cylinder before locking it in place.< ->Then she spun the cylinder for the hell of it before raising the revolver’s barrel against her very own temple.< ->She looked resigned. Though I couldn’t help but notice the slight glint of fear in her eyes.< ->None of us wanted to die.< -Masami: Gina, please. -Gina: Congratulations. -Masami: Gina! -<> -<> - -[[Waking up]] +[[Go to the store|TheStore]] +[[Lets stay here and talk|Talk]] === -title: Waking up +title: TheStore tags: colorID: 0 -position: -1527,-881 +position: 0, 200 --- -You: *waking up noices* +Clerk: Welcome to the store. +Clerk: Can I help you with anything? +[[Go home|Start]] +=== +title: Talk +tags: +colorID: 0 +position: 0, 400 +--- +Bob: So how are you really? +Theresa: I'm good! +Bob: Do you want to continue talking? +-> Yes + [[Start]] +-> No === diff --git a/project.godot b/project.godot index 15a15df..7b73ad5 100644 --- a/project.godot +++ b/project.godot @@ -38,7 +38,7 @@ _global_script_class_icons={ [application] -config/name="YarnSpinner" +config/name="Wol" run/main_scene="res://Dialogue.tscn" config/icon="res://icon.png"