went through all warnings and corrected them

This commit is contained in:
Bram Dingelstad 2021-11-21 11:31:04 +01:00
parent cda5fe8406
commit 7822c7e6b0
9 changed files with 319 additions and 360 deletions

View file

@ -3,10 +3,15 @@ extends Node
class_name Wol class_name Wol
signal node_started(node) signal node_started(node)
signal node_finished(node)
# NOTE: Warning is ignored because they get call_deferred
# warning-ignore:unused_signal
signal line(line) signal line(line)
# warning-ignore:unused_signal
signal options(options) signal options(options)
# warning-ignore:unused_signal
signal command(command) signal command(command)
signal node_completed(node)
signal started signal started
signal finished signal finished
@ -62,14 +67,12 @@ func init_dialogue():
dialogue.set_program(program) dialogue.set_program(program)
func set_path(_path): func set_path(_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 path = _path
if not Engine.editor_hint:
var compiler = WolCompiler.new(path)
program = compiler.compile()
func _handle_line(line): func _handle_line(line):
call_deferred('emit_signal', 'line', line) call_deferred('emit_signal', 'line', line)
if auto_show_options \ if auto_show_options \
@ -103,7 +106,7 @@ func _handle_node_start(node):
dialogue._visitedNodeCount[node] += 1 dialogue._visitedNodeCount[node] += 1
func _handle_node_complete(node): func _handle_node_complete(node):
emit_signal('node_completed', node) emit_signal('node_finished', node)
return Constants.HandlerState.ContinueExecution return Constants.HandlerState.ContinueExecution
func select_option(id): func select_option(id):

View file

@ -4,145 +4,140 @@ class_name Compiler
const Constants = preload('res://addons/Wol/core/constants.gd') const Constants = preload('res://addons/Wol/core/constants.gd')
const Lexer = preload('res://addons/Wol/core/compiler/lexer.gd') const Lexer = preload('res://addons/Wol/core/compiler/lexer.gd')
const Program = preload('res://addons/Wol/core/program.gd') const Program = preload('res://addons/Wol/core/program.gd')
const Parser = preload('res://addons/Wol/core/compiler/parser.gd')
#patterns const INVALID_TITLE = '[\\[<>\\]{}\\|:\\s#\\$]'
const INVALIDTITLENAME = '[\\[<>\\]{}\\|:\\s#\\$]'
#ERROR Codes
const NO_ERROR = 0x00 const NO_ERROR = 0x00
const LEXER_FAILURE = 0x01 const LEXER_FAILURE = 0x01
const PARSER_FAILURE = 0x02 const PARSER_FAILURE = 0x02
const INVALID_HEADER = 0x04 const INVALID_HEADER = 0x04
const DUPLICATE_NODES_IN_PROGRAM = 0x08
const ERR_COMPILATION_FAILED = 0x10 const ERR_COMPILATION_FAILED = 0x10
var _errors : int var source = ''
var _last_error : int var filename = ''
#-----Class vars var _current_node
var _current_node : Program.WolNode var _contains_implicit_string_tags = false
var _raw_text : bool var _label_count = 0
var _file_name : String
var _contains_implicit_string_tags : bool
var _label_count : int = 0
#<String, Program.Line> var _string_table = {}
var _string_table : Dictionary = {} var _string_count = 0
var _string_count : int = 0
#<int, Constants.TokenType>
var _tokens : Dictionary = {}
static func compile_string(source: String, filename: String): func _init(_filename, _source = null):
var Parser = load('res://addons/Wol/core/compiler/parser.gd') filename = _filename
var Compiler = load('res://addons/Wol/core/compiler/compiler.gd')
var compiler = Compiler.new() if not _filename and _source:
compiler._file_name = filename self.source = _source
else:
var file = File.new()
file.open(_filename, File.READ)
self.source = file.get_as_text()
file.close()
#--------------Nodes func compile():
var header_sep : RegEx = RegEx.new() var header_sep = RegEx.new()
var header_property = RegEx.new()
header_sep.compile('---(\r\n|\r|\n)') header_sep.compile('---(\r\n|\r|\n)')
var header_property : RegEx = RegEx.new()
header_property.compile('(?<field>.*): *(?<value>.*)') header_property.compile('(?<field>.*): *(?<value>.*)')
assert(not not header_sep.search(source), 'No headers found') assert(header_sep.search(source), 'No headers found!')
var line_number: int = 0 var line_number = 0
var source_lines : Array = source.split('\n',false) var source_lines = source.split('\n', false)
for i in range(source_lines.size()): for i in range(source_lines.size()):
source_lines[i] = source_lines[i].strip_edges(false,true) source_lines[i] = source_lines[i].strip_edges(false, true)
var parsed_nodes : Array = [] var parsed_nodes = []
while line_number < source_lines.size(): while line_number < source_lines.size():
var title = ''
var body = ''
var title : String # Parse header
var body : String
#get title
while true: while true:
var line : String = source_lines[line_number] var line = source_lines[line_number]
line_number+=1 line_number += 1
if !line.empty(): if not line.empty():
var result = header_property.search(line) var result = header_property.search(line)
if result != null : if result != null:
var field : String = result.get_string('field') var field = result.get_string('field')
var value : String = result.get_string('value') var value = result.get_string('value')
if field == 'title': if field == 'title':
assert(not ' ' in value, 'No space allowed in title "%s", correct to "%s"' % [value, value.replace(' ','')]) var regex = RegEx.new()
title = value regex.compile(INVALID_TITLE)
assert(not regex.search(value), 'Invalid characters in title "%s", correct to "%s"' % [value, regex.sub(value, '', true)])
if(line_number >= source_lines.size() || source_lines[line_number] == '---'): title = value
# TODO: Implement position, color and tags
if line_number >= source_lines.size() or line == '---':
break break
line_number += 1
line_number+=1 # past header
var body_lines = []
#past header while line_number < source_lines.size() and source_lines[line_number] != '===':
var body_lines : PoolStringArray = []
while line_number < source_lines.size() and source_lines[line_number]!='===':
body_lines.append(source_lines[line_number]) body_lines.append(source_lines[line_number])
line_number+=1 line_number += 1
line_number+=1 line_number += 1
body = body_lines.join('\n') body = PoolStringArray(body_lines).join('\n')
var lexer = Lexer.new()
var tokens = lexer.tokenize(body, title, filename)
var parser = Parser.new(tokens, title) var lexer = Lexer.new(filename, title, body)
var tokens = lexer.tokenize()
var parser = Parser.new(title, tokens)
var parser_node = parser.parse_node() var parser_node = parser.parse_node()
parser_node.name = title parser_node.name = title
# parser_node.tags = title
parsed_nodes.append(parser_node) parsed_nodes.append(parser_node)
while line_number < source_lines.size() && source_lines[line_number].empty():
line_number+=1
#--- End parsing nodes--- while line_number < source_lines.size() and source_lines[line_number].empty():
line_number += 1
var program = Program.new() var program = Program.new()
#compile nodes
for node in parsed_nodes: for node in parsed_nodes:
compiler.compile_node(program, node) compile_node(program, node)
for key in compiler._string_table: for key in _string_table:
program.strings[key] = compiler._string_table[key] program.strings[key] = _string_table[key]
return program return program
func compile_node(program, parsed_node): func compile_node(program, parsed_node):
if program.nodes.has(parsed_node.name): assert(not program.nodes.has(parsed_node.name), 'Duplicate node in program: %s' % parsed_node.name)
emit_error(DUPLICATE_NODES_IN_PROGRAM)
printerr('Duplicate node in program: %s' % parsed_node.name)
else:
var node_compiled = Program.WolNode.new() var node_compiled = Program.WolNode.new()
node_compiled.name = parsed_node.name node_compiled.name = parsed_node.name
node_compiled.tags = parsed_node.tags node_compiled.tags = parsed_node.tags
#raw text if parsed_node.source != null and not parsed_node.source.empty():
if parsed_node.source != null && !parsed_node.source.empty(): node_compiled.source_id = register_string(
node_compiled.source_id = register_string(parsed_node.source,parsed_node.name, parsed_node.source,
'line:'+parsed_node.name, 0, []) parsed_node.name,
'line:' + parsed_node.name,
0,
[]
)
else: else:
#compile node var start_label = register_label()
var start_label : String = 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: for statement in parsed_node.statements:
generate_statement(node_compiled,statement) generate_statement(node_compiled, statement)
#add options
#todo: add parser flag
var dangling_options = false var dangling_options = false
for instruction in node_compiled.instructions : for instruction in node_compiled.instructions:
if instruction.operation == Constants.ByteCode.AddOption: if instruction.operation == Constants.ByteCode.AddOption:
dangling_options = true dangling_options = true
if instruction.operation == Constants.ByteCode.ShowOptions: if instruction.operation == Constants.ByteCode.ShowOptions:
@ -154,7 +149,6 @@ func compile_node(program, parsed_node):
else: else:
emit(Constants.ByteCode.Stop, node_compiled) emit(Constants.ByteCode.Stop, node_compiled)
program.nodes[node_compiled.name] = node_compiled program.nodes[node_compiled.name] = node_compiled
func register_string(text:String,node_name:String,id:String='',line_number:int=-1,tags:Array=[])->String: func register_string(text:String,node_name:String,id:String='',line_number:int=-1,tags:Array=[])->String:
@ -163,7 +157,7 @@ func register_string(text:String,node_name:String,id:String='',line_number:int=-
var implicit : bool var implicit : bool
if id.empty(): if id.empty():
line_id_used = '%s-%s-%d' % [self._file_name,node_name,self._string_count] line_id_used = '%s-%s-%d' % [self.filename,node_name,self._string_count]
self._string_count+=1 self._string_count+=1
#use this when we generate implicit tags #use this when we generate implicit tags
@ -176,7 +170,7 @@ func register_string(text:String,node_name:String,id:String='',line_number:int=-
line_id_used = id line_id_used = id
implicit = false implicit = false
var string_info = Program.Line.new(text,node_name,line_number,_file_name,implicit,tags) var string_info = Program.Line.new(text,node_name,line_number,filename,implicit,tags)
#add to string table and return id #add to string table and return id
self._string_table[line_id_used] = string_info self._string_table[line_id_used] = string_info
@ -202,18 +196,11 @@ func emit(bytecode, node = _current_node, operands = []):
node.labels[instruction.operands[0].value] = node.instructions.size()-1 node.labels[instruction.operands[0].value] = node.instructions.size()-1
func get_string_tokens()->Array:
return []
#compile header
func generate_header(): func generate_header():
pass pass
#compile instructions for statements
#this will walk through all child branches
#of the parse tree
func generate_statement(node,statement): func generate_statement(node,statement):
# print('generating statement')
match statement.type: match statement.type:
Constants.StatementTypes.CustomCommand: Constants.StatementTypes.CustomCommand:
generate_custom_command(node,statement.custom_command) generate_custom_command(node,statement.custom_command)
@ -230,8 +217,7 @@ func generate_statement(node,statement):
Constants.StatementTypes.Line: Constants.StatementTypes.Line:
generate_line(node,statement,statement.line) generate_line(node,statement,statement.line)
_: _:
emit_error(ERR_COMPILATION_FAILED) assert(false, 'Illegal statement type [%s]. Could not generate code.' % statement.type)
printerr('illegal statement type [%s]- could not generate code' % statement.type)
#compile instructions for custom commands #compile instructions for custom commands
func generate_custom_command(node,command): func generate_custom_command(node,command):
@ -306,7 +292,6 @@ func generate_shortcut_group(node,shortcut_group):
emit(Constants.ByteCode.Pop,node) emit(Constants.ByteCode.Pop,node)
#compile instructions for block #compile instructions for block
#blocks are just groups of statements #blocks are just groups of statements
func generate_block(node,statements:Array=[]): func generate_block(node,statements:Array=[]):
@ -347,7 +332,7 @@ func generate_option(node,option):
# print('generating option') # print('generating option')
var destination : String = option.destination var destination : String = option.destination
if option.label == null || option.label.empty(): if option.label == null or option.label.empty():
#jump to another node #jump to another node
emit(Constants.ByteCode.RunNode,node,[Program.Operand.new(destination)]) emit(Constants.ByteCode.RunNode,node,[Program.Operand.new(destination)])
else : else :
@ -401,7 +386,7 @@ func generate_assignment(node,assignment):
#compile expression instructions #compile expression instructions
func generate_expression(node,expression): func generate_expression(node,expression):
# print('generating expression') # print('generating expression')
#expression = value || func call #expression = value or func call
match expression.type: match expression.type:
Constants.ExpressionType.Value: Constants.ExpressionType.Value:
generate_value(node,expression.value) generate_value(node,expression.value)
@ -438,22 +423,6 @@ func generate_value(node,value):
_: _:
printerr('Unrecognized valuenode type: %s' % value.value.type) printerr('Unrecognized valuenode type: %s' % value.value.type)
#get the error flags
func get_errors()->int:
return _errors
#get the last error code reported
func get_last_error()->int:
return _last_error
func clear_errors()->void:
_errors = NO_ERROR
_last_error = NO_ERROR
func emit_error(error : int)->void:
_last_error = error
_errors |= _last_error
static func print_tokens(tokens:Array=[]): static func print_tokens(tokens:Array=[]):
var list : PoolStringArray = [] var list : PoolStringArray = []
list.append('\n') list.append('\n')

View file

@ -1,4 +1,5 @@
extends Object extends Object
class_name Lexer
const Constants = preload('res://addons/Wol/core/constants.gd') const Constants = preload('res://addons/Wol/core/constants.gd')
@ -23,16 +24,22 @@ var WHITESPACE : String = '\\s*'
var _states : Dictionary = {} var _states : Dictionary = {}
var _defaultState : LexerState var _defaultState : LexerState
var _currentState : LexerState var _currentState : LexerState
var _indentStack : Array = [] var _indentStack : Array = []
var _shouldTrackIndent : bool = false var _shouldTrackIndent : bool = false
var filename = ''
var title = ''
var text = ''
func _init(): func _init(_filename, _title, _text):
create_states() create_states()
filename = _filename
title = _title
text = _text
func create_states(): func create_states():
var patterns : Dictionary = {} var patterns : Dictionary = {}
patterns[Constants.TokenType.Text] = ['.*', 'any text'] patterns[Constants.TokenType.Text] = ['.*', 'any text']
@ -43,7 +50,7 @@ func create_states():
patterns[Constants.TokenType.LeftParen] = ['\\(', 'left parenthesis ('] patterns[Constants.TokenType.LeftParen] = ['\\(', 'left parenthesis (']
patterns[Constants.TokenType.RightParen] = ['\\)', 'right parenthesis )'] patterns[Constants.TokenType.RightParen] = ['\\)', 'right parenthesis )']
patterns[Constants.TokenType.EqualTo] = ['(==|is(?!\\w)|eq(?!\\w))', '"=", "is" or "eq"'] patterns[Constants.TokenType.EqualTo] = ['(==|is(?!\\w)|eq(?!\\w))', '"=", "is" or "eq"']
patterns[Constants.TokenType.EqualToOrAssign] = ['(=|to(?!\\w))', 'equal to "=" or assign "="'] patterns[Constants.TokenType.EqualToOrAssign] = ['(=|to(?!\\w))', '"=" or "to"']
patterns[Constants.TokenType.NotEqualTo] = ['(\\!=|neq(?!\\w))', '"!=" or "neq"'] patterns[Constants.TokenType.NotEqualTo] = ['(\\!=|neq(?!\\w))', '"!=" or "neq"']
patterns[Constants.TokenType.GreaterThanOrEqualTo] = ['(\\>=|gte(?!\\w))', '">=" or "gte"'] patterns[Constants.TokenType.GreaterThanOrEqualTo] = ['(\\>=|gte(?!\\w))', '">=" or "gte"']
patterns[Constants.TokenType.GreaterThan] = ['(\\>|gt(?!\\w))', '">" or "gt"'] patterns[Constants.TokenType.GreaterThan] = ['(\\>|gt(?!\\w))', '">" or "gt"']
@ -81,9 +88,9 @@ func create_states():
patterns[Constants.TokenType.ShortcutOption] = ['\\-\\>\\s*', '"->"'] patterns[Constants.TokenType.ShortcutOption] = ['\\-\\>\\s*', '"->"']
#compound states #compound states
var shortcut_option : String= SHORTCUT + DASH + OPTION var shortcut_option : String = SHORTCUT + DASH + OPTION
var shortcut_option_tag : String = shortcut_option + DASH + TAG var shortcut_option_tag : String = shortcut_option + DASH + TAG
var command_or_expression : String= COMMAND + DASH + OR + DASH + EXPRESSION var command_or_expression : String = COMMAND + DASH + OR + DASH + EXPRESSION
var link_destination : String = LINK + DASH + DESTINATION var link_destination : String = LINK + DASH + DESTINATION
_states = {} _states = {}
@ -91,7 +98,7 @@ func create_states():
_states[BASE] = LexerState.new(patterns) _states[BASE] = LexerState.new(patterns)
_states[BASE].add_transition(Constants.TokenType.BeginCommand,COMMAND,true) _states[BASE].add_transition(Constants.TokenType.BeginCommand,COMMAND,true)
_states[BASE].add_transition(Constants.TokenType.OptionStart,LINK,true) _states[BASE].add_transition(Constants.TokenType.OptionStart,LINK,true)
_states[BASE].add_transition(Constants.TokenType.ShortcutOption,shortcut_option) _states[BASE].add_transition(Constants.TokenType.ShortcutOption, shortcut_option)
_states[BASE].add_transition(Constants.TokenType.TagMarker,TAG,true) _states[BASE].add_transition(Constants.TokenType.TagMarker,TAG,true)
_states[BASE].add_text_rule(Constants.TokenType.Text) _states[BASE].add_text_rule(Constants.TokenType.Text)
@ -112,7 +119,7 @@ func create_states():
_states[COMMAND].add_transition(Constants.TokenType.ElseToken) _states[COMMAND].add_transition(Constants.TokenType.ElseToken)
_states[COMMAND].add_transition(Constants.TokenType.ElseIf,EXPRESSION) _states[COMMAND].add_transition(Constants.TokenType.ElseIf,EXPRESSION)
_states[COMMAND].add_transition(Constants.TokenType.EndIf) _states[COMMAND].add_transition(Constants.TokenType.EndIf)
_states[COMMAND].add_transition(Constants.TokenType.Set,ASSIGNMENT) _states[COMMAND].add_transition(Constants.TokenType.Set, ASSIGNMENT)
_states[COMMAND].add_transition(Constants.TokenType.EndCommand,BASE,true) _states[COMMAND].add_transition(Constants.TokenType.EndCommand,BASE,true)
_states[COMMAND].add_transition(Constants.TokenType.Identifier,command_or_expression) _states[COMMAND].add_transition(Constants.TokenType.Identifier,command_or_expression)
_states[COMMAND].add_text_rule(Constants.TokenType.Text) _states[COMMAND].add_text_rule(Constants.TokenType.Text)
@ -173,9 +180,9 @@ func create_states():
for stateKey in _states.keys(): for stateKey in _states.keys():
_states[stateKey].stateName = stateKey _states[stateKey].stateName = stateKey
func tokenize(text, title, filename): func tokenize():
_indentStack.clear() _indentStack.clear()
_indentStack.push_front(IntBoolPair.new(0,false)) _indentStack.push_front(IntBoolPair.new(0, false))
_shouldTrackIndent = false _shouldTrackIndent = false
var tokens : Array = [] var tokens : Array = []
@ -188,7 +195,7 @@ func tokenize(text, title, filename):
var line_number : int = 1 var line_number : int = 1
for line in lines: for line in lines:
tokens += tokenize_line(line, line_number, title, filename) tokens += tokenize_line(line, line_number)
line_number += 1 line_number += 1
var endOfInput = Token.new( var endOfInput = Token.new(
@ -201,14 +208,14 @@ func tokenize(text, title, filename):
return tokens return tokens
func tokenize_line(line, line_number, title, filename): func tokenize_line(line, line_number):
var tokenStack : Array = [] var tokenStack : Array = []
var freshLine = line.replace('\t',' ').replace('\r','') var freshLine = line.replace('\t',' ').replace('\r','')
#record indentation #record indentation
var indentation = line_indentation(line) var indentation = line_indentation(line)
var prevIndentation : IntBoolPair = _indentStack.front() var prevIndentation = _indentStack.front()
if _shouldTrackIndent && indentation > prevIndentation.key: if _shouldTrackIndent && indentation > prevIndentation.key:
#we add an indenation token to record indent level #we add an indenation token to record indent level
@ -259,7 +266,7 @@ func tokenize_line(line, line_number, title, filename):
var tokenText : String var tokenText : String
if rule.tokenType == Constants.TokenType.Text: if rule.token_type == Constants.TokenType.Text:
#if this is text then we back up to the most recent #if this is text then we back up to the most recent
#delimiting token and treat everything from there as text. #delimiting token and treat everything from there as text.
@ -289,29 +296,29 @@ func tokenize_line(line, line_number, title, filename):
column += tokenText.length() column += tokenText.length()
#pre-proccess string #pre-proccess string
if rule.tokenType == Constants.TokenType.Str: if rule.token_type == Constants.TokenType.Str:
tokenText = tokenText.substr(1, tokenText.length() - 2) tokenText = tokenText.substr(1, tokenText.length() - 2)
tokenText = tokenText.replace('\\\\', '\\') tokenText = tokenText.replace('\\\\', '\\')
tokenText = tokenText.replace('\\\'','\'') tokenText = tokenText.replace('\\\'','\'')
var token = Token.new( var token = Token.new(
rule.tokenType, rule.token_type,
_currentState, _currentState,
filename, filename,
line_number, line_number,
column, column,
tokenText tokenText
) )
token.delimitsText = rule.delimitsText token.delimits_text = rule.delimits_text
tokenStack.push_front(token) tokenStack.push_front(token)
if rule.enterState != null and rule.enterState.length() > 0: if rule.enter_state != null and rule.enter_state.length() > 0:
if not _states.has(rule.enterState): if not _states.has(rule.enter_state):
printerr('State[%s] not known - line(%s) col(%s)' % [rule.enterState, line_number, column]) printerr('State[%s] not known - line(%s) col(%s)' % [rule.enter_state, line_number, column])
return [] return []
enter_state(_states[rule.enterState]) enter_state(_states[rule.enter_state])
if _shouldTrackIndent: if _shouldTrackIndent:
if _indentStack.front().key < indentation: if _indentStack.front().key < indentation:
@ -323,7 +330,7 @@ func tokenize_line(line, line_number, title, filename):
if not matched: if not matched:
var rules = [] var rules = []
for rule in _currentState.rules: for rule in _currentState.rules:
rules.append('"%s" (%s)' % [Constants.token_type_name(rule.tokenType), rule.human_readable_identifier]) rules.append('"%s" (%s)' % [Constants.token_type_name(rule.token_type), rule.human_readable_identifier])
var error_data = [ var error_data = [
PoolStringArray(rules).join(', ') if rules.size() == 1 else PoolStringArray(rules.slice(0, rules.size() - 2)).join(', ') + ' or %s' % rules[-1], PoolStringArray(rules).join(', ') if rules.size() == 1 else PoolStringArray(rules.slice(0, rules.size() - 2)).join(', ') + ' or %s' % rules[-1],
@ -368,17 +375,17 @@ class Token:
var column = -1 var column = -1
var text = '' var text = ''
var delimitsText = false var delimits_text = false
var paramCount = -1 var paramCount = -1
var lexerState = '' var lexerState = ''
func _init(type, state, filename, line_number = -1, column = -1, value = ''): func _init(_type, _state, _filename, _line_number = -1, _column = -1, _value = ''):
self.type = type type = _type
self.lexerState = state.stateName lexerState = _state.stateName
self.filename = filename filename = _filename
self.line_number = line_number line_number = _line_number
self.column = column column = _column
self.value = value value = _value
func _to_string(): func _to_string():
return '%s (%s) at %s:%s (state: %s)' % [Constants.token_type_name(type),value,line_number,column,lexerState] return '%s (%s) at %s:%s (state: %s)' % [Constants.token_type_name(type),value,line_number,column,lexerState]
@ -390,8 +397,8 @@ class LexerState:
var rules : Array = [] var rules : Array = []
var track_indent : bool = false var track_indent : bool = false
func _init(patterns): func _init(_patterns):
self.patterns = patterns patterns = _patterns
func add_transition(type : int, state : String = '',delimitText : bool = false)->Rule: func add_transition(type : int, state : String = '',delimitText : bool = false)->Rule:
var pattern = '\\G%s' % patterns[type][0] var pattern = '\\G%s' % patterns[type][0]
@ -407,48 +414,49 @@ class LexerState:
var delimiters:Array = [] var delimiters:Array = []
for rule in rules: for rule in rules:
if rule.delimitsText: if rule.delimits_text:
delimiters.append('%s' % rule.regex.get_pattern().substr(2)) delimiters.append('%s' % rule.regex.get_pattern().substr(2))
var pattern = '\\G((?!%s).)*' % [PoolStringArray(delimiters).join('|')] var pattern = '\\G((?!%s).)*' % [PoolStringArray(delimiters).join('|')]
var rule : Rule = add_transition(type,state) var rule : Rule = add_transition(type,state)
rule.regex = RegEx.new() rule.regex = RegEx.new()
rule.regex.compile(pattern) rule.regex.compile(pattern)
rule.isTextRule = true rule.is_text_rule = true
return rule return rule
func contains_text_rule()->bool: func contains_text_rule()->bool:
for rule in rules: for rule in rules:
if rule.isTextRule: if rule.is_text_rule:
return true return true
return false return false
class Rule: class Rule:
var regex : RegEx var regex : RegEx
var enterState : String var enter_state : String
var tokenType : int var token_type : int
var isTextRule : bool var is_text_rule : bool
var delimitsText : bool var delimits_text : bool
var human_readable_identifier = '' var human_readable_identifier = ''
func _init(type : int , regex : String, human_readable_identifier, enterState : String, delimitsText:bool): func _init(_type, _regex, _human_readable_identifier, _enter_state, _delimits_text):
self.tokenType = type token_type = _type
self.regex = RegEx.new()
self.regex.compile(regex) regex = RegEx.new()
self.human_readable_identifier = human_readable_identifier regex.compile(_regex)
self.enterState = enterState
self.delimitsText = delimitsText human_readable_identifier = _human_readable_identifier
enter_state = _enter_state
delimits_text = _delimits_text
func _to_string(): func _to_string():
return '[Rule : %s (%s) - %s]' % [Constants.token_type_name(tokenType), human_readable_identifier, regex] return '[Rule : %s (%s) - %s]' % [Constants.token_type_name(token_type), human_readable_identifier, regex]
class IntBoolPair: class IntBoolPair:
var key : int var key = -1
var value : bool var value = false
func _init(key:int,value:bool): func _init(_key, _value):
self.key = key key = _key
self.value = value value = _value

View file

@ -3,12 +3,12 @@ extends Object
const Constants = preload('res://addons/Wol/core/constants.gd') const Constants = preload('res://addons/Wol/core/constants.gd')
const Lexer = preload('res://addons/Wol/core/compiler/lexer.gd') const Lexer = preload('res://addons/Wol/core/compiler/lexer.gd')
var _tokens = [] var tokens = []
var title = '' var title = ''
func _init(tokens, title): func _init(_title, _tokens):
self._tokens = tokens title = _title
self.title = title tokens = _tokens
enum Associativity { enum Associativity {
Left, Left,
@ -20,7 +20,7 @@ func parse_node():
return WolNode.new('Start', null, self) return WolNode.new('Start', null, self)
func next_symbol_is(valid_types): func next_symbol_is(valid_types):
var type = self._tokens.front().type var type = self.tokens.front().type
for valid_type in valid_types: for valid_type in valid_types:
if type == valid_type: if type == valid_type:
return true return true
@ -28,14 +28,14 @@ func next_symbol_is(valid_types):
# NOTE:0 look ahead for `<<` and `else` # NOTE:0 look ahead for `<<` and `else`
func next_symbols_are(valid_types): func next_symbols_are(valid_types):
var temporary = [] + _tokens var temporary = [] + tokens
for type in valid_types: for type in valid_types:
if temporary.pop_front().type != type: if temporary.pop_front().type != type:
return false return false
return true return true
func expect_symbol(token_types = []): func expect_symbol(token_types = []):
var token = _tokens.pop_front() as Lexer.Token var token = tokens.pop_front() as Lexer.Token
if token_types.size() == 0: if token_types.size() == 0:
if token.type == Constants.TokenType.EndOfInput: if token.type == Constants.TokenType.EndOfInput:
@ -51,7 +51,6 @@ func expect_symbol(token_types = []):
for type in token_types: for type in token_types:
token_names.append(Constants.token_type_name(type)) token_names.append(Constants.token_type_name(type))
var error_guess = '\n' var error_guess = '\n'
if Constants.token_type_name(token.type) == 'Identifier' \ if Constants.token_type_name(token.type) == 'Identifier' \
@ -74,9 +73,6 @@ func expect_symbol(token_types = []):
static func tab(indent_level, input, newline = true): static func tab(indent_level, input, newline = true):
return '%*s| %s%s' % [indent_level * 2, '', input, '' if not newline else '\n'] return '%*s| %s%s' % [indent_level * 2, '', input, '' if not newline else '\n']
func tokens():
return _tokens
class ParseNode: class ParseNode:
var name = '' var name = ''
@ -84,21 +80,22 @@ class ParseNode:
var line_number = -1 var line_number = -1
var tags = [] var tags = []
func _init(parent, parser): func _init(_parent, _parser):
self.parent = parent parent = _parent
var tokens = parser.tokens() as Array var tokens = _parser.tokens as Array
if tokens.size() > 0: if tokens.size() > 0:
line_number = tokens.front().line_number line_number = tokens.front().line_number
else: else:
line_number = -1 line_number = -1
tags = [] tags = []
func tree_string(indent_level): func tree_string(_indent_level):
return 'Not_implemented' return 'Not_implemented'
func tags_to_string(indent_level): func tags_to_string(_indent_level):
return '%s' % 'TAGS<tags_to_string>NOTIMPLEMENTED' return 'TAGS<tags_to_string>NOTIMPLEMENTED'
func get_node_parent(): func get_node_parent():
var node = self var node = self
@ -111,10 +108,6 @@ class ParseNode:
func tab(indent_level, input, newline = true): func tab(indent_level, input, newline = true):
return '%*s| %s%s' % [ indent_level * 2, '', input, '' if !newline else '\n'] return '%*s| %s%s' % [ indent_level * 2, '', input, '' if !newline else '\n']
func set_parent(parent):
self.parent = parent
#this is a Wol Node - contains all the text
class WolNode extends ParseNode: class WolNode extends ParseNode:
var source = '' var source = ''
@ -123,7 +116,7 @@ class WolNode extends ParseNode:
func _init(name, parent, parser).(parent, parser): func _init(name, parent, parser).(parent, parser):
self.name = name self.name = name
while parser.tokens().size() > 0 \ while parser.tokens.size() > 0 \
and not parser.next_symbol_is([Constants.TokenType.Dedent, Constants.TokenType.EndOfInput]): and not parser.next_symbol_is([Constants.TokenType.Dedent, Constants.TokenType.EndOfInput]):
statements.append(Statement.new(self, parser)) statements.append(Statement.new(self, parser))
@ -180,7 +173,7 @@ class Statement extends ParseNode:
type = Type.Line type = Type.Line
else: else:
printerr('Expected a statement but got %s instead. (probably an imbalanced if statement)' % parser.tokens().front()._to_string()) printerr('Expected a statement but got %s instead. (probably an imbalanced if statement)' % parser.tokens.front()._to_string())
var tags = [] var tags = []
@ -242,9 +235,8 @@ class CustomCommand extends ParseNode:
if (command_tokens.size() > 1 && command_tokens[0].type == Constants.TokenType.Identifier if (command_tokens.size() > 1 && command_tokens[0].type == Constants.TokenType.Identifier
&& command_tokens[1].type == Constants.TokenType.LeftParen): && command_tokens[1].type == Constants.TokenType.LeftParen):
var p = get_script().new(command_tokens, parser.library) var p = get_script().new(command_tokens, parser.library)
var expression = ExpressionNode.parse(self, p) expression = ExpressionNode.parse(self, p)
type = Type.Expression type = Type.Expression
self.expression = expression
else: else:
#otherwise evaluuate command #otherwise evaluuate command
type = Type.ClientCommand type = Type.ClientCommand
@ -548,50 +540,43 @@ class ValueNode extends ParseNode:
func tree_string(indent_level): func tree_string(indent_level):
return tab(indent_level, '%s' % value.value()) return tab(indent_level, '%s' % value.value())
#Expressions encompass a wide range of things like:
# math (1 + 2 - 5 * 3 / 10 % 2)
# Identifiers
# Values
class ExpressionNode extends ParseNode: class ExpressionNode extends ParseNode:
var type var type
var value var value
var function var function
var params = []#ExpressionNode var parameters = []
func _init(parent, parser, value, function = '', params = []).(parent, parser): func _init(parent, parser, _value, _function = '', _parameters = []).(parent, parser):
#no function - means value if _value != null:
if value != null: type = Constants.ExpressionType.Value
self.type = Constants.ExpressionType.Value value = _value
self.value = value else:
else:#function type = Constants.ExpressionType.FunctionCall
function = _function
self.type = Constants.ExpressionType.FunctionCall parameters = _parameters
self.function = function
self.params = params
func tree_string(indent_level): func tree_string(indent_level):
var info = [] var info = []
match type: match type:
Constants.ExpressionType.Value: Constants.ExpressionType.Value:
return value.tree_string(indent_level) return value.tree_string(indent_level)
Constants.ExpressionType.FunctionCall: Constants.ExpressionType.FunctionCall:
info.append(tab(indent_level,'Func[%s - params(%s)]:{'%[function, params.size()])) info.append(tab(indent_level,'Func[%s - parameters(%s)]:{'%[function, parameters.size()]))
for param in params: for param in parameters:
info.append(param.tree_string(indent_level+1)) info.append(param.tree_string(indent_level+1))
info.append(tab(indent_level,'}')) info.append(tab(indent_level,'}'))
return info.join('') return info.join('')
#using Djikstra's shunting-yard algorithm to convert # using Djikstra's shunting-yard algorithm to convert stream of expresions into postfix notation,
#stream of expresions into postfix notaion, then # & then build a tree of expressions
#build a tree of expressions
static func parse(parent, parser): static func parse(parent, parser):
var rpn = [] var rpn = []
var op_stack = [] var op_stack = []
#track params #track parameters
var func_stack = [] var func_stack = []
var valid_types = [ var valid_types = [
@ -612,7 +597,7 @@ class ExpressionNode extends ParseNode:
var last var last
#read expression content #read expression content
while parser.tokens().size() > 0 && parser.next_symbol_is(valid_types): while parser.tokens.size() > 0 && parser.next_symbol_is(valid_types):
var next = parser.expect_symbol(valid_types) var next = parser.expect_symbol(valid_types)
if next.type == Constants.TokenType.Variable \ if next.type == Constants.TokenType.Variable \
@ -646,7 +631,7 @@ class ExpressionNode extends ParseNode:
# next parser token not allowed to be right paren or comma # next parser token not allowed to be right paren or comma
if parser.next_symbol_is([Constants.TokenType.RightParen, if parser.next_symbol_is([Constants.TokenType.RightParen,
Constants.TokenType.Comma]): Constants.TokenType.Comma]):
printerr('Expected Expression : %s' % parser.tokens().front().name) printerr('Expected Expression : %s' % parser.tokens.front().name)
#find the closest function on stack #find the closest function on stack
#increment parameters #increment parameters
@ -698,7 +683,7 @@ class ExpressionNode extends ParseNode:
op_stack.pop_back() op_stack.pop_back()
if op_stack.back().type == Constants.TokenType.Identifier: if op_stack.back().type == Constants.TokenType.Identifier:
#function call #function call
#last token == left paren this == no params #last token == left paren this == no parameters
#else #else
#we have more than 1 param #we have more than 1 param
if last.type != Constants.TokenType.LeftParen: if last.type != Constants.TokenType.LeftParen:
@ -731,36 +716,35 @@ class ExpressionNode extends ParseNode:
if eval_stack.size() < info.arguments: 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]) 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 = []#ExpressionNode var function_parameters = []
for i in range(info.arguments): for _i in range(info.arguments):
params.append(eval_stack.pop_back()) function_parameters.append(eval_stack.pop_back())
params.invert() function_parameters.invert()
var function = get_func_name(next.type) var function_name = get_func_name(next.type)
var expression = ExpressionNode.new(parent, parser, null, function_name, function_parameters)
var expression = ExpressionNode.new(parent, parser, null, function, params)
eval_stack.append(expression) eval_stack.append(expression)
# A function call
elif next.type == Constants.TokenType.Identifier: elif next.type == Constants.TokenType.Identifier:
#function call var function_name = next.value
var function = next.value var function_parameters = []
for _i in range(next.param_count):
function_parameters.append(eval_stack.pop_back())
var params = []#ExpressionNode function_parameters.invert()
for i in range(next.param_count):
params.append(eval_stack.pop_back()) var expression = ExpressionNode.new(parent, parser, null, function_name, function_parameters)
params.invert()
var expression = ExpressionNode.new(parent, parser, null, function, params)
eval_stack.append(expression) eval_stack.append(expression)
else: #raw value
var value = ValueNode.new(parent, parser, next) # A raw value
var expression = ExpressionNode.new(parent, parser, value) else:
var raw_value = ValueNode.new(parent, parser, next)
var expression = ExpressionNode.new(parent, parser, raw_value)
eval_stack.append(expression) eval_stack.append(expression)
@ -769,45 +753,39 @@ class ExpressionNode extends ParseNode:
if eval_stack.size() != 1: if eval_stack.size() != 1:
printerr('[%s] Error parsing expression (stack did not reduce correctly )' % first.name) printerr('[%s] Error parsing expression (stack did not reduce correctly )' % first.name)
return eval_stack.pop_back() return eval_stack.pop_back()
static func get_func_name(type): static func get_func_name(_type):
var string = '' var string = ''
for key in Constants.TokenType.keys(): for key in Constants.TokenType.keys():
if Constants.TokenType[key] == type: if Constants.TokenType[key] == _type:
return key return key
return string return string
static func is_apply_precedence(type, operator_stack): static func is_apply_precedence(_type, operator_stack):
if operator_stack.size() == 0: if operator_stack.size() == 0:
return false return false
if not Operator.is_op(type): if not Operator.is_op(_type):
printerr('Unable to parse expression!') assert(false, 'Unable to parse expression!')
var second = operator_stack.back().type var second = operator_stack.back().type
if not Operator.is_op(second): if not Operator.is_op(second):
return false return false
var first_info = Operator.op_info(type) var first_info = Operator.op_info(_type)
var second_info = Operator.op_info(second) var second_info = Operator.op_info(second)
if (first_info.associativity == Associativity.Left && return \
first_info.precedence <= second_info.precedence): (first_info.associativity == Associativity.Left \
return true and first_info.precedence <= second_info.precedence) \
or \
if (first_info.associativity == Associativity.Right && (first_info.associativity == Associativity.Right \
first_info.precedence < second_info.precedence): and first_info.precedence < second_info.precedence)
return true
return false
class Assignment extends ParseNode: class Assignment extends ParseNode:
var destination var destination
var value var value
var operation var operation
@ -845,15 +823,13 @@ class Assignment extends ParseNode:
] ]
class Operator extends ParseNode: class Operator extends ParseNode:
var op_type var op_type
func _init(parent, parser, op_type=null).(parent, parser): func _init(parent, parser, _op_type = null).(parent, parser):
if _op_type == null :
if op_type == null : op_type = parser.expect_symbol(Operator.op_types()).type
self.op_type = parser.expect_symbol(Operator.op_types()).type
else: else:
self.op_type = op_type op_type = _op_type
func tree_string(indent_level): func tree_string(indent_level):
var info = [] var info = []
@ -923,18 +899,18 @@ class OperatorInfo:
var precedence = -1 var precedence = -1
var arguments = -1 var arguments = -1
func _init(associativity, precedence, arguments): func _init(_associativity, _precedence, _arguments):
self.associativity = associativity associativity = _associativity
self.precedence = precedence precedence = _precedence
self.arguments = arguments arguments = _arguments
class Clause: class Clause:
var expression var expression
var statements = [] #Statement var statements = [] #Statement
func _init(expression = null, statements = []): func _init(_expression = null, _statements = []):
self.expression = expression expression = _expression
self.statements = statements statements = _statements
func tree_string(indent_level): func tree_string(indent_level):
var info = [] var info = []

View file

@ -49,6 +49,7 @@ func set_node(name = DEFAULT_START):
_vm.set_node(name) _vm.set_node(name)
func start(): func start():
print('got here')
if _vm.executionState == Constants.ExecutionState.Stopped: if _vm.executionState == Constants.ExecutionState.Stopped:
_vm.resume() _vm.resume()

View file

@ -1,39 +1,35 @@
extends Object extends Object
var Value : GDScript = load("res://addons/Wol/core/value.gd") var Value : GDScript = load("res://addons/Wol/core/value.gd")
#name of the function var name = ''
var name : String # NOTE: -1 means variable arguments
var parameter_count = 0
var function
var returns_value = false
#param count of this function func _init(_name, _parameter_count, _function = null, _returns_value = false):
# -1 means variable arguments name = _name
var paramCount : int = 0 parameter_count = _parameter_count
#function implementation function = _function
var function : FuncRef returns_value = _returns_value
var returnsValue : bool = false
func _init(name: String, paramCount: int, function: FuncRef = null, returnsValue: bool = false): func invoke(parameters = []):
self.name = name
self.paramCount = paramCount
self.function = function
self.returnsValue = returnsValue
func invoke(params = []):
var length = 0 var length = 0
if params != null: if parameters != null:
length = params.size() length = parameters.size()
if check_param_count(length): if check_param_count(length):
if returnsValue: if returns_value:
if length > 0: if length > 0:
return Value.new(function.call_funcv(params)) return Value.new(function.call_funcv(parameters))
else: else:
return Value.new(function.call_func()) return Value.new(function.call_func())
else: else:
if length > 0: if length > 0:
function.call_funcv(params) function.call_funcv(parameters)
else : else :
function.call_func() function.call_func()
return null return null
func check_param_count(paramCount: int): func check_param_count(_parameter_count):
return self.paramCount == paramCount || self.paramCount == -1 return parameter_count == _parameter_count or parameter_count == -1

View file

@ -15,28 +15,28 @@ class Line:
var implicit = false var implicit = false
var meta = [] var meta = []
func _init(text, node_name, line_number, file_name, implicit, meta): func _init(_text, _node_name, _line_number, _file_name, _implicit, _meta):
self.text = text text = _text
self.node_name = node_name node_name = _node_name
self.file_name = file_name file_name = _file_name
self.implicit = implicit implicit = _implicit
self.meta = meta meta = _meta
class Option: class Option:
var line var line
var id = -1 var id = -1
var destination = '' var destination = ''
func _init(line, id, destination): func _init(_line, _id, _destination):
self.line = line line = _line
self.id = id id = _id
self.destination = destination destination = _destination
class Command: class Command:
var command = '' var command = ''
func _init(command): func _init(_command):
self.command = command command = _command
class WolNode: class WolNode:
var name = '' var name = ''
@ -55,15 +55,15 @@ class WolNode:
source_id = other.source_id source_id = other.source_id
func equals(other): func equals(other):
if other.get_script() != self.get_script(): if other.get_script() != get_script():
return false return false
if other.name != self.name: if other.name != name:
return false return false
if other.instructions != self.instructions: if other.instructions != instructions:
return false return false
if other.label != self.label: if other.labels != labels:
return false return false
if other.sourceId != self.sourceId: if other.source_id != source_id:
return false return false
return true return true
@ -82,33 +82,33 @@ class Operand:
var value var value
var type var type
func _init(value): func _init(_value):
if typeof(value) == TYPE_OBJECT and value.get_script() == self.get_script(): if typeof(_value) == TYPE_OBJECT and _value.get_script() == get_script():
set_value(value.value) set_value(_value.value)
else: else:
set_value(value) set_value(_value)
func set_value(value): func set_value(_value):
match typeof(value): match typeof(_value):
TYPE_REAL,TYPE_INT: TYPE_REAL,TYPE_INT:
set_number(value) set_number(_value)
TYPE_BOOL: TYPE_BOOL:
set_boolean(value) set_boolean(_value)
TYPE_STRING: TYPE_STRING:
set_string(value) set_string(_value)
func set_boolean(value): func set_boolean(_value):
_value(value) value = _value
type = ValueType.BooleanValue type = ValueType.BooleanValue
return self return self
func set_string(value): func set_string(_value):
_value(value) value = _value
type = ValueType.StringValue type = ValueType.StringValue
return self return self
func set_number(value): func set_number(_value):
_value(value) value = _value
type = ValueType.FloatValue type = ValueType.FloatValue
return self return self
@ -122,9 +122,6 @@ class Operand:
func _to_string(): func _to_string():
return "Operand[%s:%s]" % [type, value] return "Operand[%s:%s]" % [type, value]
func _value(value):
self.value = value
class Instruction: class Instruction:
var operation = -1 var operation = -1
var operands = [] var operands = []
@ -134,9 +131,6 @@ class Instruction:
self.operation = other.operation self.operation = other.operation
self.operands += other.operands self.operands += other.operands
func dump(program, library):
return "InstructionInformation:NotImplemented"
func _to_string(): func _to_string():
return Constants.bytecode_name(operation) + ':' + operands as String return Constants.bytecode_name(operation) + ':' + operands as String

View file

@ -219,27 +219,27 @@ func run_instruction(instruction)->bool:
var function = _dialogue.library.get_function(functionName) var function = _dialogue.library.get_function(functionName)
var expectedParamCount : int = function.paramCount var expected_parameter_count : int = function.paramCount
var actualParamCount : int = _state.pop_value().as_number() var actual_parameter_count : int = _state.pop_value().as_number()
#if function takes in -1 params disregard #if function takes in -1 params disregard
#expect the compiler to have placed the number of params #expect the compiler to have placed the number of params
#at the top of the stack #at the top of the stack
if expectedParamCount == -1: if expected_parameter_count == -1:
expectedParamCount = actualParamCount expected_parameter_count = actual_parameter_count
if expectedParamCount != actualParamCount: if expected_parameter_count != actual_parameter_count:
printerr('Function %s expected %d parameters but got %d instead' %[functionName, printerr('Function %s expected %d parameters but got %d instead' %[functionName,
expectedParamCount,actualParamCount]) expected_parameter_count,actual_parameter_count])
return false return false
var result var result
if actualParamCount == 0: if actual_parameter_count == 0:
result = function.invoke() result = function.invoke()
else: else:
var params : Array = []#value var params : Array = []#value
for i in range(actualParamCount): for _i in range(actual_parameter_count):
params.push_front(_state.pop_value()) params.push_front(_state.pop_value())
result = function.invoke(params) result = function.invoke(params)
@ -352,6 +352,6 @@ class SimpleEntry:
var key var key
var value var value
func _init(key, value): func _init(_key, _value):
self.key = key key = _key
self.value = value value = _value

View file

@ -20,6 +20,11 @@ _global_script_classes=[ {
"path": "res://addons/Wol/core/constants.gd" "path": "res://addons/Wol/core/constants.gd"
}, { }, {
"base": "Object", "base": "Object",
"class": "Lexer",
"language": "GDScript",
"path": "res://addons/Wol/core/compiler/lexer.gd"
}, {
"base": "Object",
"class": "Program", "class": "Program",
"language": "GDScript", "language": "GDScript",
"path": "res://addons/Wol/core/program.gd" "path": "res://addons/Wol/core/program.gd"
@ -32,6 +37,7 @@ _global_script_classes=[ {
_global_script_class_icons={ _global_script_class_icons={
"Compiler": "", "Compiler": "",
"Constants": "", "Constants": "",
"Lexer": "",
"Program": "", "Program": "",
"Wol": "" "Wol": ""
} }
@ -42,6 +48,12 @@ config/name="Wol"
run/main_scene="res://Dialogue.tscn" run/main_scene="res://Dialogue.tscn"
config/icon="res://icon.png" config/icon="res://icon.png"
[debug]
gdscript/warnings/treat_warnings_as_errors=true
gdscript/warnings/exclude_addons=false
gdscript/warnings/return_value_discarded=false
[editor_plugins] [editor_plugins]
enabled=PoolStringArray( "res://addons/Wol/plugin.cfg" ) enabled=PoolStringArray( "res://addons/Wol/plugin.cfg" )