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

View file

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

View file

@ -1,4 +1,5 @@
extends Object
class_name Lexer
const Constants = preload('res://addons/Wol/core/constants.gd')
@ -23,16 +24,22 @@ var WHITESPACE : String = '\\s*'
var _states : Dictionary = {}
var _defaultState : LexerState
var _currentState : LexerState
var _indentStack : Array = []
var _shouldTrackIndent : bool = false
var filename = ''
var title = ''
var text = ''
func _init():
func _init(_filename, _title, _text):
create_states()
filename = _filename
title = _title
text = _text
func create_states():
var patterns : Dictionary = {}
patterns[Constants.TokenType.Text] = ['.*', 'any text']
@ -43,7 +50,7 @@ func create_states():
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.EqualToOrAssign] = ['(=|to(?!\\w))', '"=" or "to"']
patterns[Constants.TokenType.NotEqualTo] = ['(\\!=|neq(?!\\w))', '"!=" or "neq"']
patterns[Constants.TokenType.GreaterThanOrEqualTo] = ['(\\>=|gte(?!\\w))', '">=" or "gte"']
patterns[Constants.TokenType.GreaterThan] = ['(\\>|gt(?!\\w))', '">" or "gt"']
@ -81,9 +88,9 @@ func create_states():
patterns[Constants.TokenType.ShortcutOption] = ['\\-\\>\\s*', '"->"']
#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 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
_states = {}
@ -91,7 +98,7 @@ func create_states():
_states[BASE] = LexerState.new(patterns)
_states[BASE].add_transition(Constants.TokenType.BeginCommand,COMMAND,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_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.ElseIf,EXPRESSION)
_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.Identifier,command_or_expression)
_states[COMMAND].add_text_rule(Constants.TokenType.Text)
@ -173,9 +180,9 @@ func create_states():
for stateKey in _states.keys():
_states[stateKey].stateName = stateKey
func tokenize(text, title, filename):
func tokenize():
_indentStack.clear()
_indentStack.push_front(IntBoolPair.new(0,false))
_indentStack.push_front(IntBoolPair.new(0, false))
_shouldTrackIndent = false
var tokens : Array = []
@ -188,7 +195,7 @@ func tokenize(text, title, filename):
var line_number : int = 1
for line in lines:
tokens += tokenize_line(line, line_number, title, filename)
tokens += tokenize_line(line, line_number)
line_number += 1
var endOfInput = Token.new(
@ -201,14 +208,14 @@ func tokenize(text, title, filename):
return tokens
func tokenize_line(line, line_number, title, filename):
func tokenize_line(line, line_number):
var tokenStack : Array = []
var freshLine = line.replace('\t',' ').replace('\r','')
#record indentation
var indentation = line_indentation(line)
var prevIndentation : IntBoolPair = _indentStack.front()
var prevIndentation = _indentStack.front()
if _shouldTrackIndent && indentation > prevIndentation.key:
#we add an indenation token to record indent level
@ -259,7 +266,7 @@ func tokenize_line(line, line_number, title, filename):
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
#delimiting token and treat everything from there as text.
@ -289,29 +296,29 @@ func tokenize_line(line, line_number, title, filename):
column += tokenText.length()
#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.replace('\\\\', '\\')
tokenText = tokenText.replace('\\\'','\'')
var token = Token.new(
rule.tokenType,
rule.token_type,
_currentState,
filename,
line_number,
column,
tokenText
)
token.delimitsText = rule.delimitsText
token.delimits_text = rule.delimits_text
tokenStack.push_front(token)
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])
if rule.enter_state != null and rule.enter_state.length() > 0:
if not _states.has(rule.enter_state):
printerr('State[%s] not known - line(%s) col(%s)' % [rule.enter_state, line_number, column])
return []
enter_state(_states[rule.enterState])
enter_state(_states[rule.enter_state])
if _shouldTrackIndent:
if _indentStack.front().key < indentation:
@ -323,7 +330,7 @@ func tokenize_line(line, line_number, title, filename):
if not matched:
var 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 = [
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 text = ''
var delimitsText = false
var delimits_text = false
var paramCount = -1
var lexerState = ''
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
func _init(_type, _state, _filename, _line_number = -1, _column = -1, _value = ''):
type = _type
lexerState = _state.stateName
filename = _filename
line_number = _line_number
column = _column
value = _value
func _to_string():
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 track_indent : bool = false
func _init(patterns):
self.patterns = patterns
func _init(_patterns):
patterns = _patterns
func add_transition(type : int, state : String = '',delimitText : bool = false)->Rule:
var pattern = '\\G%s' % patterns[type][0]
@ -407,48 +414,49 @@ class LexerState:
var delimiters:Array = []
for rule in rules:
if rule.delimitsText:
if rule.delimits_text:
delimiters.append('%s' % rule.regex.get_pattern().substr(2))
var pattern = '\\G((?!%s).)*' % [PoolStringArray(delimiters).join('|')]
var rule : Rule = add_transition(type,state)
rule.regex = RegEx.new()
rule.regex.compile(pattern)
rule.isTextRule = true
rule.is_text_rule = true
return rule
func contains_text_rule()->bool:
for rule in rules:
if rule.isTextRule:
if rule.is_text_rule:
return true
return false
class Rule:
var regex : RegEx
var enterState : String
var tokenType : int
var isTextRule : bool
var delimitsText : bool
var enter_state : String
var token_type : int
var is_text_rule : bool
var delimits_text : bool
var human_readable_identifier = ''
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 _init(_type, _regex, _human_readable_identifier, _enter_state, _delimits_text):
token_type = _type
regex = RegEx.new()
regex.compile(_regex)
human_readable_identifier = _human_readable_identifier
enter_state = _enter_state
delimits_text = _delimits_text
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:
var key : int
var value : bool
var key = -1
var value = false
func _init(key:int,value:bool):
self.key = key
self.value = value
func _init(_key, _value):
key = _key
value = _value

View file

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

View file

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

View file

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

View file

@ -15,28 +15,28 @@ class Line:
var implicit = false
var meta = []
func _init(text, node_name, line_number, file_name, implicit, meta):
self.text = text
self.node_name = node_name
self.file_name = file_name
self.implicit = implicit
self.meta = meta
func _init(_text, _node_name, _line_number, _file_name, _implicit, _meta):
text = _text
node_name = _node_name
file_name = _file_name
implicit = _implicit
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
func _init(_line, _id, _destination):
line = _line
id = _id
destination = _destination
class Command:
var command = ''
func _init(command):
self.command = command
func _init(_command):
command = _command
class WolNode:
var name = ''
@ -55,15 +55,15 @@ class WolNode:
source_id = other.source_id
func equals(other):
if other.get_script() != self.get_script():
if other.get_script() != get_script():
return false
if other.name != self.name:
if other.name != name:
return false
if other.instructions != self.instructions:
if other.instructions != instructions:
return false
if other.label != self.label:
if other.labels != labels:
return false
if other.sourceId != self.sourceId:
if other.source_id != source_id:
return false
return true
@ -82,33 +82,33 @@ class Operand:
var value
var type
func _init(value):
if typeof(value) == TYPE_OBJECT and value.get_script() == self.get_script():
set_value(value.value)
func _init(_value):
if typeof(_value) == TYPE_OBJECT and _value.get_script() == get_script():
set_value(_value.value)
else:
set_value(value)
set_value(_value)
func set_value(value):
match typeof(value):
func set_value(_value):
match typeof(_value):
TYPE_REAL,TYPE_INT:
set_number(value)
set_number(_value)
TYPE_BOOL:
set_boolean(value)
set_boolean(_value)
TYPE_STRING:
set_string(value)
set_string(_value)
func set_boolean(value):
_value(value)
func set_boolean(_value):
value = _value
type = ValueType.BooleanValue
return self
func set_string(value):
_value(value)
func set_string(_value):
value = _value
type = ValueType.StringValue
return self
func set_number(value):
_value(value)
func set_number(_value):
value = _value
type = ValueType.FloatValue
return self
@ -122,9 +122,6 @@ class Operand:
func _to_string():
return "Operand[%s:%s]" % [type, value]
func _value(value):
self.value = value
class Instruction:
var operation = -1
var operands = []
@ -134,9 +131,6 @@ class Instruction:
self.operation = other.operation
self.operands += other.operands
func dump(program, library):
return "InstructionInformation:NotImplemented"
func _to_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 expectedParamCount : int = function.paramCount
var actualParamCount : int = _state.pop_value().as_number()
var expected_parameter_count : int = function.paramCount
var actual_parameter_count : int = _state.pop_value().as_number()
#if function takes in -1 params disregard
#expect the compiler to have placed the number of params
#at the top of the stack
if expectedParamCount == -1:
expectedParamCount = actualParamCount
if expected_parameter_count == -1:
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,
expectedParamCount,actualParamCount])
expected_parameter_count,actual_parameter_count])
return false
var result
if actualParamCount == 0:
if actual_parameter_count == 0:
result = function.invoke()
else:
var params : Array = []#value
for i in range(actualParamCount):
for _i in range(actual_parameter_count):
params.push_front(_state.pop_value())
result = function.invoke(params)
@ -352,6 +352,6 @@ class SimpleEntry:
var key
var value
func _init(key, value):
self.key = key
self.value = value
func _init(_key, _value):
key = _key
value = _value

View file

@ -20,6 +20,11 @@ _global_script_classes=[ {
"path": "res://addons/Wol/core/constants.gd"
}, {
"base": "Object",
"class": "Lexer",
"language": "GDScript",
"path": "res://addons/Wol/core/compiler/lexer.gd"
}, {
"base": "Object",
"class": "Program",
"language": "GDScript",
"path": "res://addons/Wol/core/program.gd"
@ -32,6 +37,7 @@ _global_script_classes=[ {
_global_script_class_icons={
"Compiler": "",
"Constants": "",
"Lexer": "",
"Program": "",
"Wol": ""
}
@ -42,6 +48,12 @@ config/name="Wol"
run/main_scene="res://Dialogue.tscn"
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]
enabled=PoolStringArray( "res://addons/Wol/plugin.cfg" )