class_name Parser static var symbols: Array[Variant] = [] static var choices: int = 0 var buffers: Array[HankBuffer] = [] var ast: Array[Variant] = [] func _init() -> void: choices = 0 symbols.push_back({ "key": "INCLUDE ", "value": Parser.include }) symbols.push_back({ "key": "<-", "value": Parser.thread }) symbols.push_back({ "key": "->", "value": Parser.divert }) symbols.push_back({ "key": "===", "value": Parser.knot }) symbols.push_back({ "key": "==", "value": Parser.knot }) symbols.push_back({ "key": "=", "value": Parser.stitch }) symbols.push_back({ "key": "~", "value": Parser.haxeLine }) symbols.push_back({ "key": "```", "value": Parser.haxeBlock }) symbols.push_back({ "key": "-", "value": Parser.gather }) symbols.push_back({ "key": "*", "value": Parser.choice }) symbols.push_back({ "key": "+", "value": Parser.choice }) symbols.push_back({ "key": "#", "value": Parser.tag }) func parseString(h: String) -> Array[Variant]: var stringBuffer: HankBuffer = HankBuffer.Dummy(h) var parsedAST: Array[Variant] = ([] as Array[Variant]) while true: var position: Position = stringBuffer.position() stringBuffer.skipWhitespace("") if (stringBuffer.isEmpty()): break var expr: Variant = Parser.parseExpr(stringBuffer, position) match (expr._index): 0: var _g: String = expr.path var file: String = _g assert(false, str("cannot include files from within an embedded Hank block")) 6: pass _: parsedAST.push_back({ "position": position, "expr": expr }) if !(!stringBuffer.isEmpty()): break return parsedAST func parseFile(f: String, files = null, includedFile: bool = false) -> Array[Variant]: var directory: String = "" var tempNumber var startIndex: int = -1 if (startIndex < 0): tempNumber = f.rfind("/") else: var tempString var endIndex: int = startIndex + f.length() if (endIndex < 0): tempString = f.substr(0) else: tempString = f.substr(0, endIndex) tempNumber = (tempString).rfind("/") var lastSlashIdx: int = tempNumber if (lastSlashIdx != -1): directory = f.substr(0, lastSlashIdx + 1) f = f.substr(lastSlashIdx + 1) if true: var _this: Array[HankBuffer] = self.buffers var x: HankBuffer = HankBuffer.FromFile(directory + f, files) if true: _this.insert(0, x) null while (self.buffers.size() > 0): var position: Position = self.buffers[0].position() self.buffers[0].skipWhitespace("") if (self.buffers[0].isEmpty()): var _this: Array[HankBuffer] = self.buffers var x: HankBuffer = self.buffers[0] var index: int = _this.find(x) if (index >= 0): _this.remove_at(index) true else: false else: var expr: Variant = Parser.parseExpr(self.buffers[0], position) match (expr._index): 0: var _g: String = expr.path var file: String = _g self.parseFile(directory + file, files, true) 6: pass _: self.ast.push_back({ "position": position, "expr": expr }) var parsedAST: Array[Variant] = self.ast if (!includedFile): self.ast = ([] as Array[Variant]) return parsedAST static func parseExpr(buffer: HankBuffer, position: Position) -> Variant: var line: Variant = buffer.peekLine("") match (line._index): 0: var _g: String = line.v var line2: String = _g if (StringTools.trim(line2).length() == 0): return { "_index": 6 } var _g2: int = 0 var _g1: Array[Variant] = symbols while (_g2 < _g1.size()): var rule: Variant = _g1[_g2] _g2 += 1 var symbol: String = rule.get("key") var rule2 = rule.get("value") if (StringTools.startsWith(StringTools.trim(line2), symbol)): buffer.skipWhitespace("") return rule2.call(buffer, position) return Parser.output(buffer, position) 1: assert(false, str("Tried to parse expr when no lines were left in file")) return null static func lineTokens(buffer: HankBuffer, n: int, position: Position, throwOnMismatch: bool = true, rtrim: bool = true) -> Array[String]: var line: String = Extensions.unwrap(buffer.takeLine("")) if (rtrim): var tempRight var l: int = line.length() var r: int = 0 while (r < l && StringTools.isSpace(line, l - r - 1)): r += 1 if (r > 0): tempRight = line.substr(0, l - r) else: tempRight = line line = tempRight var tokens: Array[String] = Array(Array(line.split(" ")), Variant.Type.TYPE_STRING, "", null) if (tokens.size() != n): if (throwOnMismatch): assert(false, str("Wrong number of tokens at " + str(position) + ": " + str(tokens.size()) + " tokens provided--should be " + str(n) + ".\nLine: `" + line + "`\nTokens: " + str(tokens))) else: return tokens.slice(0, n) return tokens static func include(buffer: HankBuffer, position: Position) -> Variant: var tokens: Array[String] = Parser.lineTokens(buffer, 2, position, true, true) return { "_index": 0, "path": tokens[1] } static func divert(buffer: HankBuffer, position: Position) -> Variant: var line: String = Extensions.unwrap(buffer.takeLine("")) var _g: Array[String] = ([] as Array[String]) var _g1: int = 0 var _g2: Array[String] = Array(Array(line.split("->")), Variant.Type.TYPE_STRING, "", null) while (_g1 < _g2.size()): var t: String = _g2[_g1] _g1 += 1 _g.push_back(StringTools.trim(t)) var targets: Array[String] = (_g).slice(1) return { "_index": 2, "targets": targets } static func thread(buffer: HankBuffer, position: Position) -> Variant: buffer.drop("<-") buffer.skipWhitespace("") var tokens: Array[String] = Parser.lineTokens(buffer, 1, position, true, true) return { "_index": 3, "target": tokens[0] } static func output(buffer: HankBuffer, position: Position) -> Variant: return { "_index": 1, "o": Output.parse(buffer, false) } static func knot(buffer: HankBuffer, position: Position) -> Variant: buffer.drop("==") if (buffer.peekAhead(0, 1) == "="): buffer.drop("=") buffer.skipWhitespace("") var tokens: Array[String] = Parser.lineTokens(buffer, 1, position, false, true) return { "_index": 4, "name": tokens[0] } static func stitch(buffer: HankBuffer, position: Position) -> Variant: buffer.drop("=") buffer.skipWhitespace("") var tokens: Array[String] = Parser.lineTokens(buffer, 1, position, false, true) return { "_index": 5, "name": tokens[0] } static func haxeLine(buffer: HankBuffer, position: Position) -> Variant: buffer.drop("~") return { "_index": 7, "haxe": Extensions.unwrap(buffer.takeLine("lr")) } static func gather(buffer: HankBuffer, position: Position) -> Variant: var depth: int = buffer.countConsecutive("-") buffer.skipWhitespace("") var label: Variant = buffer.expressionIfNext("(", ")") buffer.skipWhitespace("\n") return { "_index": 9, "label": label, "depth": depth, "expr": { "_index": 6 } } static func choice(buffer: HankBuffer, position: Position) -> Variant: var symbol: String = buffer.peek(1) var onceOnly: bool = symbol == "*" var depth: int = buffer.countConsecutive(symbol) buffer.skipWhitespace("") var label: Variant = buffer.expressionIfNext("(", ")") buffer.skipWhitespace("") var condition: Variant = buffer.expressionIfNext("{", "}?") buffer.skipWhitespace("") var output: Output = Output.parse(buffer, false) var divertTarget: Variant = output.takeInlineDivert() choices += 1 var tempNumber: int = choices - 1 return { "_index": 10, "c": { "id": tempNumber, "onceOnly": onceOnly, "label": label, "condition": condition, "depth": depth, "output": output, "divertTarget": divertTarget, "tags": ([] as Array[String]) } } static func tag(buffer: HankBuffer, position: Position) -> Variant: buffer.drop("#") var tagLine: String = Extensions.unwrap(buffer.takeLine("lr")) var tags: Array[String] = Extensions.tokenize(tagLine) var expr: Variant = Parser.parseExpr(buffer, position) if (expr._index == 10): var _g: Variant = expr.c var c: Variant = _g c.set("tags", tags) else: haxe_Log.trace.call("Warning! Can't apply tags to " + str(expr), { "fileName": "../hank/Parser.hx", "lineNumber": 234, "className": "hank.Parser", "methodName": "tag" }) return expr static func haxeBlock(buffer: HankBuffer, position: Position) -> Variant: buffer.drop("```\n") var rawContents: String = Extensions.unwrap(buffer.takeUntil((["```"] as Array[String]), false, true)).get("output") var processedContents: String = "" var blockBuffer: HankBuffer = HankBuffer.Dummy(rawContents) while true: var nextLine: String = Extensions.unwrap(blockBuffer.takeLine("lr")) if (nextLine == ",,,"): var embeddedHankBlock: String = Extensions.unwrap(blockBuffer.takeUntil(([",,,"] as Array[String]), false, true)).get("output") processedContents += "story.runEmbeddedHank(\"" + Parser.escapeQuotes(embeddedHankBlock) + "\"); " else: if (StringTools.startsWith(nextLine, ",")): nextLine = StringTools.trim(nextLine.substr(1)) processedContents += "story.runEmbeddedHank(\"" + Parser.escapeQuotes(nextLine) + "\"); " else: processedContents += nextLine + " " if !(!blockBuffer.isEmpty()): break return { "_index": 8, "haxe": processedContents } static func escapeQuotes(s: String) -> String: var escaped: String = s escaped = StringTools.replace(escaped, "'", "\\'") escaped = StringTools.replace(escaped, "\"", "\\\"") return escaped