Files
hank/hank-godot/scripts/Parser.gd

352 lines
9.2 KiB
GDScript

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