# # BSD 3-Clause License # # Copyright (c) 2018 - 2023, Oleg Malyavkin # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. extends Node const PROTO_VERSION_CONST : String = "const PROTO_VERSION = " const PROTO_VERSION_DEFAULT : String = PROTO_VERSION_CONST + "0" class Document: func _init(doc_name : String, doc_text : String): name = doc_name text = doc_text var name : String var text : String class TokenPosition: func _init(b : int, e : int): begin = b end = e var begin : int = 0 var end : int = 0 class Helper: class StringPosition: func _init(s : int, c : int, l : int): str_num = s column = c length = l var str_num : int var column : int var length : int static func str_pos(text : String, position : TokenPosition) -> StringPosition: var cur_str : int = 1 var cur_col : int = 1 var res_str : int = 0 var res_col : int = 0 var res_length : int = 0 for i in range(text.length()): if text[i] == "\n": cur_str += 1 cur_col = 0 if position.begin == i: res_str = cur_str res_col = cur_col res_length = position.end - position.begin + 1 break cur_col += 1 return StringPosition.new(res_str, res_col, res_length) static func text_pos(tokens : Array, index : int) -> TokenPosition: var res_begin : int = 0 var res_end : int = 0 if index < tokens.size() && index >= 0: res_begin = tokens[index].position.begin res_end = tokens[index].position.end return TokenPosition.new(res_begin, res_end) static func error_string(file_name, col, row, error_text): return file_name + ":" + str(col) + ":" + str(row) + ": error: " + error_text class AnalyzeResult: var classes : Array = [] var fields : Array = [] var groups : Array = [] var version : int = 0 var state : bool = false var tokens : Array = [] var syntax : Analysis.TranslationResult var imports : Array = [] var doc : Document func soft_copy() -> AnalyzeResult: var res : AnalyzeResult = AnalyzeResult.new() res.classes = classes res.fields = fields res.groups = groups res.version = version res.state = state res.tokens = tokens res.syntax = syntax res.imports = imports res.doc = doc return res class Analysis: func _init(path : String, doc : Document): path_dir = path document = doc var document : Document var path_dir : String const LEX = { LETTER = "[A-Za-z]", DIGIT_DEC = "[0-9]", DIGIT_OCT = "[0-7]", DIGIT_HEX = "[0-9]|[A-F]|[a-f]", BRACKET_ROUND_LEFT = "\\(", BRACKET_ROUND_RIGHT = "\\)", BRACKET_CURLY_LEFT = "\\{", BRACKET_CURLY_RIGHT = "\\}", BRACKET_SQUARE_LEFT = "\\[", BRACKET_SQUARE_RIGHT = "\\]", BRACKET_ANGLE_LEFT = "\\<", BRACKET_ANGLE_RIGHT = "\\>", SEMICOLON = ";", COMMA = ",", EQUAL = "=", SIGN = "\\+|\\-", SPACE = "\\s", QUOTE_SINGLE = "'", QUOTE_DOUBLE = "\"", } const TOKEN_IDENT : String = "(" + LEX.LETTER + "+" + "(" + LEX.LETTER + "|" + LEX.DIGIT_DEC + "|" + "_)*)" const TOKEN_FULL_IDENT : String = TOKEN_IDENT + "{0,1}(\\." + TOKEN_IDENT + ")+" const TOKEN_BRACKET_ROUND_LEFT : String = "(" + LEX.BRACKET_ROUND_LEFT + ")" const TOKEN_BRACKET_ROUND_RIGHT : String = "(" + LEX.BRACKET_ROUND_RIGHT + ")" const TOKEN_BRACKET_CURLY_LEFT : String = "(" + LEX.BRACKET_CURLY_LEFT + ")" const TOKEN_BRACKET_CURLY_RIGHT : String = "(" + LEX.BRACKET_CURLY_RIGHT + ")" const TOKEN_BRACKET_SQUARE_LEFT : String = "(" + LEX.BRACKET_SQUARE_LEFT + ")" const TOKEN_BRACKET_SQUARE_RIGHT : String = "(" + LEX.BRACKET_SQUARE_RIGHT + ")" const TOKEN_BRACKET_ANGLE_LEFT : String = "(" + LEX.BRACKET_ANGLE_LEFT + ")" const TOKEN_BRACKET_ANGLE_RIGHT : String = "(" + LEX.BRACKET_ANGLE_RIGHT + ")" const TOKEN_SEMICOLON : String = "(" + LEX.SEMICOLON + ")" const TOKEN_EUQAL : String = "(" + LEX.EQUAL + ")" const TOKEN_SIGN : String = "(" + LEX.SIGN + ")" const TOKEN_LITERAL_DEC : String = "(([1-9])" + LEX.DIGIT_DEC +"*)" const TOKEN_LITERAL_OCT : String = "(0" + LEX.DIGIT_OCT +"*)" const TOKEN_LITERAL_HEX : String = "(0(x|X)(" + LEX.DIGIT_HEX +")+)" const TOKEN_LITERAL_INT : String = "((\\+|\\-){0,1}" + TOKEN_LITERAL_DEC + "|" + TOKEN_LITERAL_OCT + "|" + TOKEN_LITERAL_HEX + ")" const TOKEN_LITERAL_FLOAT_DEC : String = "(" + LEX.DIGIT_DEC + "+)" const TOKEN_LITERAL_FLOAT_EXP : String = "((e|E)(\\+|\\-)?" + TOKEN_LITERAL_FLOAT_DEC + "+)" const TOKEN_LITERAL_FLOAT : String = "((\\+|\\-){0,1}(" + TOKEN_LITERAL_FLOAT_DEC + "\\." + TOKEN_LITERAL_FLOAT_DEC + "?" + TOKEN_LITERAL_FLOAT_EXP + "?)|(" + TOKEN_LITERAL_FLOAT_DEC + TOKEN_LITERAL_FLOAT_EXP + ")|(\\." + TOKEN_LITERAL_FLOAT_DEC + TOKEN_LITERAL_FLOAT_EXP + "?))" const TOKEN_SPACE : String = "(" + LEX.SPACE + ")+" const TOKEN_COMMA : String = "(" + LEX.COMMA + ")" const TOKEN_CHAR_ESC : String = "[\\\\(a|b|f|n|r|t|v|\\\\|'|\")]" const TOKEN_OCT_ESC : String = "[\\\\" + LEX.DIGIT_OCT + "{3}]" const TOKEN_HEX_ESC : String = "[\\\\(x|X)" + LEX.DIGIT_HEX + "{2}]" const TOKEN_CHAR_EXCLUDE : String = "[^\\0\\n\\\\]" const TOKEN_CHAR_VALUE : String = "(" + TOKEN_HEX_ESC + "|" + TOKEN_OCT_ESC + "|" + TOKEN_CHAR_ESC + "|" + TOKEN_CHAR_EXCLUDE + ")" const TOKEN_STRING_SINGLE : String = "('" + TOKEN_CHAR_VALUE + "*?')" const TOKEN_STRING_DOUBLE : String = "(\"" + TOKEN_CHAR_VALUE + "*?\")" const TOKEN_COMMENT_SINGLE : String = "((//[^\\n\\r]*[^\\s])|//)" const TOKEN_COMMENT_MULTI : String = "/\\*(.|[\\n\\r])*?\\*/" const TOKEN_SECOND_MESSAGE : String = "^message$" const TOKEN_SECOND_SIMPLE_DATA_TYPE : String = "^(double|float|int32|int64|uint32|uint64|sint32|sint64|fixed32|fixed64|sfixed32|sfixed64|bool|string|bytes)$" const TOKEN_SECOND_ENUM : String = "^enum$" const TOKEN_SECOND_MAP : String = "^map$" const TOKEN_SECOND_ONEOF : String = "^oneof$" const TOKEN_SECOND_LITERAL_BOOL : String = "^(true|false)$" const TOKEN_SECOND_SYNTAX : String = "^syntax$" const TOKEN_SECOND_IMPORT : String = "^import$" const TOKEN_SECOND_PACKAGE : String = "^package$" const TOKEN_SECOND_OPTION : String = "^option$" const TOKEN_SECOND_SERVICE : String = "^service$" const TOKEN_SECOND_RESERVED : String = "^reserved$" const TOKEN_SECOND_IMPORT_QUALIFICATION : String = "^(weak|public)$" const TOKEN_SECOND_FIELD_QUALIFICATION : String = "^(repeated|required|optional)$" const TOKEN_SECOND_ENUM_OPTION : String = "^allow_alias$" const TOKEN_SECOND_QUALIFICATION : String = "^(custom_option|extensions)$" const TOKEN_SECOND_FIELD_OPTION : String = "^packed$" class TokenEntrance: func _init(i : int, b : int, e : int, t : String): position = TokenPosition.new(b, e) text = t id = i var position : TokenPosition var text : String var id : int enum RANGE_STATE { INCLUDE = 0, EXCLUDE_LEFT = 1, EXCLUDE_RIGHT = 2, OVERLAY = 3, EQUAL = 4, ENTERS = 5 } class TokenRange: func _init(b : int, e : int, s): position = TokenPosition.new(b, e) state = s var position : TokenPosition var state class Token: var _regex : RegEx var _entrance : TokenEntrance = null var _entrances : Array = [] var _entrance_index : int = 0 var _id : int var _ignore : bool var _clarification : String func _init(id : int, clarification : String, regex_str : String, ignore = false): _id = id _regex = RegEx.new() _regex.compile(regex_str) _clarification = clarification _ignore = ignore func find(text : String, start : int) -> TokenEntrance: _entrance = null if !_regex.is_valid(): return null var match_result : RegExMatch = _regex.search(text, start) if match_result != null: var capture capture = match_result.get_string(0) if capture.is_empty(): return null _entrance = TokenEntrance.new(_id, match_result.get_start(0), capture.length() - 1 + match_result.get_start(0), capture) return _entrance func find_all(text : String) -> Array: var pos : int = 0 clear() while find(text, pos) != null: _entrances.append(_entrance) pos = _entrance.position.end + 1 return _entrances func add_entrance(entrance) -> void: _entrances.append(entrance) func clear() -> void: _entrance = null _entrances = [] _entrance_index = 0 func get_entrances() -> Array: return _entrances func remove_entrance(index) -> void: if index < _entrances.size(): _entrances.remove_at(inde