class_name HankBuffer var path: String var cleanBuffer: String var rawBuffer: String var line: int var column: int func _init(path2: String, rawBuffer2: String, line2: int = 1, column2: int = 1) -> void: if (rawBuffer2 == null): assert(false, str("Tried to create buffer of path " + path2 + " with null contents: " + rawBuffer2)) self.path = path2 self.rawBuffer = rawBuffer2 self.cleanBuffer = self.stripComments(rawBuffer2, "//", "\n", false) self.cleanBuffer = self.stripComments(self.cleanBuffer, "/*", "*/", true) self.line = line2 self.column = column2 func lines() -> Array[String]: var _this: String = self.cleanBuffer var tempArray: Array[String] = Array(Array(_this.split("\n")), Variant.Type.TYPE_STRING, "", null) self.drop(self.cleanBuffer) return tempArray func stripComments(s: String, o: String, c: String, dc: bool) -> String: while (s.find(o) != -1): var start: int = s.find(o) var end: int = s.find(c, start) if (end == -1): s = s.substr(0, start) else: if (dc): end += c.length() s = s.substr(0, start) + s.substr(end) return s func indexOf(s: String, start: int = 0) -> int: return self.cleanBuffer.find(s, start) func everyIndexOf(s: String) -> Array[int]: var _gthis: HankBuffer = self var tempArray if true: var _g: Array[int] = ([] as Array[int]) if true: var _g1: int = 0 var _g2: int = self.cleanBuffer.length() while (_g1 < _g2): var tempNumber if true: _g1 += 1 tempNumber = _g1 - 1 var i: int = tempNumber _g.push_back(i) tempArray = _g var temp = func(i: int) -> bool: return _gthis.cleanBuffer[i] == s var result: Array[int] = ([] as Array[int]) if true: var _g: int = 0 while (_g < tempArray.size()): var v: int = tempArray[_g] _g += 1 if (temp.call(v)): result.push_back(v) return result func everyRootIndexOf(s: String) -> Array[int]: var _gthis: HankBuffer = self var tempArray if true: var _g: Array[int] = ([] as Array[int]) if true: var _g1: int = 0 var _g2: Array[int] = self.everyIndexOf(s) while (_g1 < _g2.size()): var i: int = _g2[_g1] _g1 += 1 _g.push_back(i) tempArray = _g var temp = func(i: int) -> bool: return _gthis.depthAtIndex("{", "}", i) == 0 var result: Array[int] = ([] as Array[int]) if true: var _g: int = 0 while (_g < tempArray.size()): var v: int = tempArray[_g] _g += 1 if (temp.call(v)): result.push_back(v) return result func rootIndexOf(s: String) -> int: var start: int = 0 while (true): start = self.indexOf(s, start) if (start == -1): return -1 if (self.depthAtIndex("{", "}", start) == 0): return start start += 1 return -1 func rootSplit(delimiter: String) -> Array[String]: var rootIndices: Array[int] = self.everyRootIndexOf(delimiter) if (rootIndices.size() == 0): return ([self.cleanBuffer] as Array[String]) var substrs: Array[String] = ([] as Array[String]) var lastIdx: int = 0 var _g: int = 0 while (_g < rootIndices.size()): var i: int = rootIndices[_g] _g += 1 substrs.push_back(self.cleanBuffer.substr(lastIdx, i - lastIdx)) lastIdx = i + 1 substrs.push_back(self.cleanBuffer.substr(lastIdx)) return substrs func length() -> int: return self.cleanBuffer.length() func position() -> Position: return Position.new(self.path, self.line, self.column) func peekAhead(start: int, length: int) -> String: return self.cleanBuffer.substr(start, length) func peekUntil(terminators: Array[String], eofTerminates: bool = false, raw: bool = false) -> Variant: var tempString if (raw): tempString = self.rawBuffer else: tempString = self.cleanBuffer var buffer: String = tempString if (buffer.length() == 0): return { "_index": 1 } var index: int = buffer.length() var whichTerminator: String = "" var _g: int = 0 while (_g < terminators.size()): var terminator: String = terminators[_g] _g += 1 var nextIndex: int = buffer.find(terminator) if (nextIndex != -1 && nextIndex < index): index = nextIndex whichTerminator = terminator var tempResult if (index < buffer.length() || eofTerminates): tempResult = { "_index": 0, "v": { "output": buffer.substr(0, index), "terminator": whichTerminator } } else: tempResult = { "_index": 1 } return tempResult func dropRaw(s: String) -> void: var actual: String = self.rawBuffer.substr(0, s.length()) if (actual != s): assert(false, str("Expected to drop \"" + s + "\" but was \"" + actual + "\"")) var lines: Array[String] = Array(Array(s.split("\n")), Variant.Type.TYPE_STRING, "", null) if (lines.size() > 1): self.line += lines.size() - 1 self.column = lines[lines.size() - 1].length() + 1 else: self.column += lines[0].length() self.rawBuffer = self.rawBuffer.substr(s.length()) func dropClean(s: String) -> void: var actual: String = self.cleanBuffer.substr(0, s.length()) if (actual != s): assert(false, str("Expected to drop \"" + s + "\" but was \"" + actual + "\"")) self.cleanBuffer = self.cleanBuffer.substr(s.length()) func drop(s: String) -> void: var untilNextComment: Variant = self.peekUntil((["//", "/*"] as Array[String]), false, true) if (untilNextComment._index == 0): var _g: Variant = untilNextComment.v var _g1: String = _g.get("output") var _g2: String = _g.get("terminator") var rawS: String = _g1 var commentOpener: String = _g2 if (rawS.length() < s.length()): var tempString match (commentOpener): "/*": tempString = "*/" "//": tempString = "\n" _: assert(false, str("FUBAR")) var commentTerminator: String = tempString self.dropRaw(rawS + commentOpener) self.dropClean(rawS) var untilEndOfComment: Variant = self.peekUntil(([commentTerminator] as Array[String]), true, true) if (untilEndOfComment._index == 0): var _g3: Variant = untilEndOfComment.v var _g4: String = _g3.get("output") var _g5: String = _g3.get("terminator") var comment: String = _g4 self.dropRaw(comment) if (commentTerminator != "\n"): self.dropRaw(commentTerminator) var rest: String = s.substr(rawS.length()) self.drop(rest) else: assert(false, str("FUBAR")) else: self.dropClean(s) self.dropRaw(s) else: self.dropClean(s) self.dropRaw(s) func takeUntil(terminators: Array[String], eofTerminates: bool = false, dropTerminator: bool = true) -> Variant: var tempResult if true: var _g: Variant = self.peekUntil(terminators, eofTerminates, false) match (_g._index): 0: var _g2: Variant = _g.v var _g1: String = _g2.get("output") var _g3: String = _g2.get("terminator") var s: String = _g1 var t: String = _g3 self.drop(s) if (dropTerminator): self.drop(t) tempResult = { "_index": 0, "v": { "output": s, "terminator": t } } 1: tempResult = { "_index": 1 } return tempResult func peek(chars: int) -> String: if (self.cleanBuffer.length() < chars): assert(false, str("Not enough characters left in buffer.")) var data: String = self.cleanBuffer.substr(0, chars) return data func take(chars: int) -> String: var data: String = self.peek(chars) self.drop(data) return data func countConsecutive(s: String) -> int: var num: int = 0 while (self.cleanBuffer.substr(0, s.length()) == s): num += 1 self.drop(s) return num func expressionIfNext(o: String, c: String) -> Variant: if (StringTools.startsWith(self.cleanBuffer, o) && self.cleanBuffer.find(c) != -1): self.drop(o) var end: int = self.cleanBuffer.find(c) var content: String = self.take(end) self.drop(c) return { "_index": 0, "v": content } return { "_index": 1 } func getLine(trimmed: String, retriever, dropNewline: bool) -> Variant: var nextLine: Variant = retriever.call((["\n"] as Array[String]), true, false) var tempResult match (nextLine._index): 0: var _g: Variant = nextLine.v var _g1: String = _g.get("output") var _g2: String = _g.get("terminator") var nextLine2: String = _g1 if (dropNewline && !self.isEmpty()): self.drop("\n") if (trimmed.find("r") != -1): var tempRight if true: var l: int = nextLine2.length() var r: int = 0 while (r < l && StringTools.isSpace(nextLine2, l - r - 1)): r += 1 if (r > 0): tempRight = nextLine2.substr(0, l - r) else: tempRight = nextLine2 nextLine2 = tempRight if (trimmed.find("l") != -1): var tempRight1 if true: var l: int = nextLine2.length() var r: int = 0 while (r < l && StringTools.isSpace(nextLine2, r)): r += 1 if (r > 0): tempRight1 = nextLine2.substr(r, l - r) else: tempRight1 = nextLine2 nextLine2 = tempRight1 tempResult = { "_index": 0, "v": nextLine2 } 1: tempResult = { "_index": 1 } return tempResult func peekLine(trimmed: String = "") -> Variant: return self.getLine(trimmed, self.peekUntil, false) func takeLine(trimmed: String = "") -> Variant: return self.getLine(trimmed, self.takeUntil, true) func skipWhitespace(terminator: String = "") -> void: var nextTerm: int = self.cleanBuffer.find(terminator) var tempString var s: String = self.cleanBuffer var l: int = s.length() var r: int = 0 while (r < l && StringTools.isSpace(s, r)): r += 1 if (r > 0): tempString = s.substr(r, l - r) else: tempString = s var withoutTerm: int = self.cleanBuffer.length() - (tempString).length() var tempNumber if (nextTerm <= 0): tempNumber = withoutTerm else: var v: float = min(nextTerm, withoutTerm) tempNumber = int(floor(v)) var end: int = tempNumber var whitespace: String = self.cleanBuffer.substr(0, end) self.drop(whitespace) func isEmpty() -> bool: return self.cleanBuffer.length() == 0 func depthAtIndex(o: String, c: String, index: int) -> int: var depth: int = 0 var snippet: String = self.cleanBuffer.substr(0, index) var _g: int = 0 var _g1: int = snippet.length() while (_g < _g1): var tempNumber _g += 1 tempNumber = _g - 1 var i: int = tempNumber var whichC: String = snippet[i] if (whichC == o): depth += 1 else: if (whichC == c): depth -= 1 return depth func findNestedExpression(o: String, c: String, start: int = 0, throwExceptions: bool = true) -> Variant: var startIdx: int = start var endIdx: int = start var depth: int = 0 var nextIdx: int = start while true: var nextOpeningIdx: int = self.cleanBuffer.find(o, nextIdx) var nextClosingIdx: int = self.cleanBuffer.find(c, nextIdx) if (nextOpeningIdx == -1 && nextClosingIdx == -1): return { "_index": 1 } else: if (depth == 0 && nextOpeningIdx == -1): if (throwExceptions): assert(false, str("Your input file " + self.path + " has an expression with an unmatched closing operator " + c)) else: return { "_index": 1 } else: if (depth != 0 && nextClosingIdx == -1): if (throwExceptions): assert(false, str("Your input file " + self.path + " has an expression with an unmatched opening operator " + o)) else: return { "_index": 1 } else: if (nextOpeningIdx != -1 && nextOpeningIdx < nextClosingIdx): if (depth == 0): startIdx = nextOpeningIdx depth += 1 nextIdx = nextOpeningIdx + o.length() else: depth -= 1 nextIdx = nextClosingIdx + c.length() endIdx = nextClosingIdx + c.length() if !(depth > 0 && nextIdx < self.cleanBuffer.length()): break return { "_index": 0, "v": BufferSlice.new(startIdx, endIdx - startIdx, self) } static func Dummy(text: String) -> HankBuffer: return HankBuffer.new("_", text, 1, 1) static func FromFile(path2: String, files = null) -> HankBuffer: var rawBuffer2: String = "" rawBuffer2 = FileAccess.open(path2, FileAccess.READ).get_as_text() return HankBuffer.new(path2, rawBuffer2)