Make hank work in GDScript
Some checks failed
/ test (push) Failing after 41s

This commit is contained in:
2025-10-30 21:42:19 -05:00
parent 20c38dbe4c
commit b0a3c0b3d9
29 changed files with 2663 additions and 715 deletions

View File

@@ -1,15 +1,6 @@
class_name Alt
static var behaviorMap: haxe_ds_StringMap = haxe_ds_StringMap.new().__set(">", AltBehavior.AltBehavior.Sequence)
haxe_ds_StringMap.new().__set("!", AltBehavior.AltBehavior.OnceOnly)
haxe_ds_StringMap.new().__set("&", AltBehavior.AltBehavior.Cycle)
haxe_ds_StringMap.new().__set("%", AltBehavior.AltBehavior.Shuffle)
haxe_ds_StringMap.new().__set("sequence:", AltBehavior.AltBehavior.Sequence)
haxe_ds_StringMap.new().__set("once:", AltBehavior.AltBehavior.OnceOnly)
haxe_ds_StringMap.new().__set("cycle:", AltBehavior.AltBehavior.Cycle)
haxe_ds_StringMap.new().__set("shuffle:", AltBehavior.AltBehavior.Shuffle)
haxe_ds_StringMap.new()
static var behaviorMap: Array[Variant] = []
var behavior: AltBehavior.AltBehavior
var outputs: Array[Output]
@@ -34,23 +25,53 @@ static func parse(buffer: HankBuffer) -> Variant:
tempString = s
var expr: String = tempString
if (behaviorMap.size() == 0):
behaviorMap.push_back({
"key": ">",
"value": AltBehavior.AltBehavior.Sequence
})
behaviorMap.push_back({
"key": "!",
"value": AltBehavior.AltBehavior.OnceOnly
})
behaviorMap.push_back({
"key": "&",
"value": AltBehavior.AltBehavior.Cycle
})
behaviorMap.push_back({
"key": "%",
"value": AltBehavior.AltBehavior.Shuffle
})
behaviorMap.push_back({
"key": "sequence:",
"value": AltBehavior.AltBehavior.Sequence
})
behaviorMap.push_back({
"key": "once:",
"value": AltBehavior.AltBehavior.OnceOnly
})
behaviorMap.push_back({
"key": "cycle:",
"value": AltBehavior.AltBehavior.Cycle
})
behaviorMap.push_back({
"key": "shuffle:",
"value": AltBehavior.AltBehavior.Shuffle
})
var behavior2: AltBehavior.AltBehavior = AltBehavior.AltBehavior.Sequence
var tempIterator
if true:
var this1: Variant = behaviorMap
tempIterator = this1.keys()
while (tempIterator.get("hasNext").call()):
var prefix2: String = tempIterator.get("next").call()
if (StringTools.startsWith(expr, prefix2)):
expr = StringTools.trim(expr.substr(prefix2.length()))
var tempRight
if true:
var this1: Variant = behaviorMap
tempRight = this1.__get(prefix2)
behavior2 = tempRight
break
var _g: int = 0
var _g1: Array[Variant] = behaviorMap
while (_g < _g1.size()):
var prefix: Variant = _g1[_g]
_g += 1
if (StringTools.startsWith(expr, prefix.get("key"))):
expr = StringTools.trim(expr.substr(prefix.get("key").length()))
behavior2 = prefix.get("value")
break
var outputsBuffer: HankBuffer = HankBuffer.Dummy(expr)
var eachOutputExpr: Array[String] = outputsBuffer.rootSplit("|")
@@ -58,25 +79,28 @@ static func parse(buffer: HankBuffer) -> Variant:
if (eachOutputExpr.size() == 1):
return { "_index": 1 }
var _g: Array[Output] = ([] as Array[Output])
var _g1: int = 0
var tempArray
while (_g1 < eachOutputExpr.size()):
var outputExpr: String = eachOutputExpr[_g1]
_g1 += 1
var tempString1
if true:
var _g: Array[Output] = ([] as Array[Output])
if true:
var l: int = outputExpr.length()
var r: int = 0
while (r < l && StringTools.isSpace(outputExpr, r)):
r += 1
if (r > 0):
tempString1 = outputExpr.substr(r, l - r)
else:
tempString1 = outputExpr
_g.push_back(Output.parse(HankBuffer.Dummy(tempString1), true))
var _g1: int = 0
while (_g1 < eachOutputExpr.size()):
var outputExpr: String = eachOutputExpr[_g1]
_g1 += 1
var tempString1
if true:
var l: int = outputExpr.length()
var r: int = 0
while (r < l && StringTools.isSpace(outputExpr, r)):
r += 1
if (r > 0):
tempString1 = outputExpr.substr(r, l - r)
else:
tempString1 = outputExpr
_g.push_back(Output.parse(HankBuffer.Dummy(tempString1), true))
tempArray = _g
buffer.take(rawExpr.length())
return { "_index": 0, "v": Alt.new(behavior2, _g) }
return { "_index": 0, "v": Alt.new(behavior2, tempArray) }

View File

@@ -1,143 +0,0 @@
class_name haxe__CallStack_CallStack_Impl_
func _init() -> void:
pass
static func callStack() -> Array[Variant]:
return haxe_NativeStackTrace.toHaxe(haxe_NativeStackTrace.callStack())
static func exceptionStack(fullStack: bool = false) -> Array[Variant]:
var eStack: Array[Variant] = haxe_NativeStackTrace.toHaxe(haxe_NativeStackTrace.exceptionStack())
var tempCallStack
if (fullStack):
tempCallStack = eStack
else:
tempCallStack = haxe__CallStack_CallStack_Impl_.subtract(eStack, haxe__CallStack_CallStack_Impl_.callStack())
var this1: Array[Variant] = tempCallStack
return this1
static func subtract(this1: Array[Variant], stack: Array[Variant]) -> Array[Variant]:
var startIndex: int = -1
var i: int = -1
while true:
var tempLeft
i += 1
tempLeft = i
if !(tempLeft < this1.size()):
break
var _g: int = 0
var _g1: int = stack.size()
while (_g < _g1):
var tempNumber
_g += 1
tempNumber = _g - 1
var j: int = tempNumber
if (haxe__CallStack_CallStack_Impl_.equalItems(this1[i], stack[j])):
if (startIndex < 0):
startIndex = i
i += 1
if (i >= this1.size()):
break
else:
startIndex = -1
if (startIndex >= 0):
break
var tempResult
if (startIndex >= 0):
tempResult = this1.slice(0, startIndex)
else:
tempResult = this1
return tempResult
static func equalItems(item1, item2) -> bool:
var tempResult
if (item1 == null):
if (item2 == null):
tempResult = true
else:
tempResult = false
else:
match (item1._index):
0:
if (item2 == null):
tempResult = false
else:
if (item2._index == 0):
tempResult = true
else:
tempResult = false
1:
var _g: String = item1.m
if (item2 == null):
tempResult = false
else:
if (item2._index == 1):
var _g1: String = item2.m
var m2: String = _g1
var m1: String = _g
tempResult = m1 == m2
else:
tempResult = false
2:
var _g = item1.s
var _g1: String = item1.file
var _g2: int = item1.line
var _g3 = item1.column
if (item2 == null):
tempResult = false
else:
if (item2._index == 2):
var _g4 = item2.s
var _g5: String = item2.file
var _g6: int = item2.line
var _g7 = item2.column
var item3 = _g4
var file2: String = _g5
var line2: int = _g6
var col2 = _g7
var col1 = _g3
var line1: int = _g2
var file1: String = _g1
var item4 = _g
tempResult = file1 == file2 && line1 == line2 && col1 == col2 && haxe__CallStack_CallStack_Impl_.equalItems(item4, item3)
else:
tempResult = false
3:
var _g = item1.classname
var _g1: String = item1.method
if (item2 == null):
tempResult = false
else:
if (item2._index == 3):
var _g2 = item2.classname
var _g3: String = item2.method
var class2 = _g2
var method2: String = _g3
var method1: String = _g1
var class1 = _g
tempResult = class1 == class2 && method1 == method2
else:
tempResult = false
4:
var _g = item1.v
if (item2 == null):
tempResult = false
else:
if (item2._index == 4):
var _g1 = item2.v
var v2 = _g1
var v1 = _g
tempResult = v1 == v2
else:
tempResult = false
return tempResult

View File

@@ -485,4 +485,3 @@ static func viewCountOf(viewCounts2: Variant, val) -> Variant:
tempResult = { "_index": 1 }
return tempResult

View File

@@ -8,54 +8,6 @@ var ast: Array[Variant] = []
func _init() -> void:
choices = 0
symbols.push_back({
"key": "INCLUDE ",
"value": HParser.include
})
symbols.push_back({
"key": "<-",
"value": HParser.thread
})
symbols.push_back({
"key": "->",
"value": HParser.divert
})
symbols.push_back({
"key": "===",
"value": HParser.knot
})
symbols.push_back({
"key": "==",
"value": HParser.knot
})
symbols.push_back({
"key": "=",
"value": HParser.stitch
})
symbols.push_back({
"key": "~",
"value": HParser.haxeLine
})
symbols.push_back({
"key": "```",
"value": HParser.haxeBlock
})
symbols.push_back({
"key": "-",
"value": HParser.gather
})
symbols.push_back({
"key": "*",
"value": HParser.choice
})
symbols.push_back({
"key": "+",
"value": HParser.choice
})
symbols.push_back({
"key": "#",
"value": HParser.tag
})
func parseString(h: String) -> Array[Variant]:
var stringBuffer: HankBuffer = HankBuffer.Dummy(h)
@@ -148,6 +100,56 @@ func parseFile(f: String, files = null, includedFile: bool = false) -> Array[Var
return parsedAST
static func parseExpr(buffer: HankBuffer, position: Position) -> Variant:
if (symbols.size() == 0):
symbols.push_back({
"key": "INCLUDE ",
"value": HParser.include
})
symbols.push_back({
"key": "<-",
"value": HParser.thread
})
symbols.push_back({
"key": "->",
"value": HParser.divert
})
symbols.push_back({
"key": "===",
"value": HParser.knot
})
symbols.push_back({
"key": "==",
"value": HParser.knot
})
symbols.push_back({
"key": "=",
"value": HParser.stitch
})
symbols.push_back({
"key": "~",
"value": HParser.haxeLine
})
symbols.push_back({
"key": "```",
"value": HParser.haxeBlock
})
symbols.push_back({
"key": "-",
"value": HParser.gather
})
symbols.push_back({
"key": "*",
"value": HParser.choice
})
symbols.push_back({
"key": "+",
"value": HParser.choice
})
symbols.push_back({
"key": "#",
"value": HParser.tag
})
var line: Variant = buffer.peekLine("")
match (line._index):
@@ -311,7 +313,7 @@ static func tag(buffer: HankBuffer, position: Position) -> Variant:
else:
haxe_Log.trace.call("Warning! Can't apply tags to " + str(expr), {
"fileName": "../hank/HParser.hx",
"lineNumber": 234,
"lineNumber": 236,
"className": "hank.HParser",
"methodName": "tag"
})

View File

@@ -1,59 +0,0 @@
class_name HankAssert
func _init() -> void:
pass
static func _assert(condition: bool, message: String) -> void:
if (condition):
HankAssert._pass(message)
else:
HankAssert.fail(message)
static func _pass(message: String) -> void:
message = "" + message
LogUtil.cleanTrace(message)
static func fail(message: String) -> void:
message = "" + message
LogUtil.cleanTrace(message)
static func equals(expected, actual, pos = null) -> void:
var verboseMessage: String = " Asserting that " + str(actual) + " is expected value " + str(expected)
HankAssert._assert(str(expected) == str(actual), verboseMessage)
static func contains(expected: String, actual: String) -> void:
var verboseMessage: String = " Asserting that \"" + actual + "\" contains \"" + expected + "\""
HankAssert._assert(actual.find(expected) != -1, verboseMessage)
static func notContains(unexpected: String, actual: String) -> void:
var verboseMessage: String = " Asserting that \"" + actual + "\" does not contain \"" + unexpected + "\""
HankAssert._assert(actual.find(unexpected) == -1, verboseMessage)
static func isSome(option: Variant) -> void:
var verboseMessage: String = " Asserting that " + str(option) + " is Some(_)"
match (option._index):
0:
var _g = option.v
HankAssert._pass(verboseMessage)
1:
HankAssert.fail(verboseMessage)
static func isNone(option: Variant) -> void:
var verboseMessage: String = " Asserting that " + str(option) + " is None"
match (option._index):
0:
var _g = option.v
HankAssert.fail(verboseMessage)
1:
HankAssert._pass(verboseMessage)
static func throws(f, message = null) -> void:
if true:
f.call()
HankAssert.fail("Expected an exception to be thrown")

View File

@@ -113,6 +113,8 @@ func rootIndexOf(s: String) -> int:
return start
start += 1
return -1
func rootSplit(delimiter: String) -> Array[String]:
var rootIndices: Array[int] = self.everyRootIndexOf(delimiter)
@@ -444,7 +446,8 @@ static func Dummy(text: String) -> HankBuffer:
return HankBuffer.new("_", text, 1, 1)
static func FromFile(path2: String, files = null) -> HankBuffer:
var rawBuffer2: String = File.getContent(path2)
var rawBuffer2: String = ""
rawBuffer2 = FileAccess.open(path2, FileAccess.READ).get_as_text()
return HankBuffer.new(path2, rawBuffer2)

View File

@@ -141,7 +141,7 @@ func initOps() -> void:
return v1 * v2)
self.assignOp("/=", func(v1: float, v2: float) -> float:
return v1 / v2)
self.assignOp("%=", func(v1: float, v2: float) -> float:
self.assignOp("%=", func(v1: int, v2: int) -> int:
return v1 % v2)
self.assignOp("&=", func(v1: int, v2: int) -> int:
return v1 & v2)
@@ -838,13 +838,10 @@ func expr(e: Variant):
if (isAllString):
tempVar1 = haxe_ds_StringMap.new()
else:
if (isAllEnum):
tempVar1 = haxe_ds_EnumValueMap.new()
if (isAllObject):
tempVar1 = haxe_ds_ObjectMap.new()
else:
if (isAllObject):
tempVar1 = haxe_ds_ObjectMap.new()
else:
assert(false, str("Inconsistent key types"))
assert(false, str("Inconsistent key types"))
var map = tempVar1
if true:
var _g2: int = 0
@@ -1091,10 +1088,4 @@ func _call(o, f, args: Array[Variant]):
return f.callv(args)
func cnew(cl: String, args: Array[Variant]):
var c: Class = Type.resolveClass(cl)
if (c == null):
c = self.resolve(cl)
return Type.createInstance(c, args)
return null

View File

@@ -266,6 +266,8 @@ static func parseBraceExpression(buffer: HankBuffer) -> Variant:
else:
return Output.parseHaxeExpression(buffer)
return null
static func parseHaxeExpression(buffer: HankBuffer) -> Variant:
var rawExpression: String = Extensions.unwrap(buffer.findNestedExpression("{", "}", 0, true)).checkValue()
var hExpression: String = rawExpression.substr(1, rawExpression.length() - 2)
@@ -285,4 +287,3 @@ static func appendNextText(story: Story, fullOutput: String, throwOnFail: String
assert(false, str(throwOnFail))
return fullOutput

View File

@@ -24,7 +24,7 @@ func _init() -> void:
self.opChars = "+*/-=!><&|^%~"
self.identChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"
var priorities: Array[Array[String]] = ([(["%"] as Array[String]), (["*", "/"] as Array[String]), (["+", "-"] as Array[String]), (["<<", ">>", ">>>"] as Array[String]), (["|", "&", "^"] as Array[String]), (["==", "!=", ">", "<", ">=", "<="] as Array[String]), (["..."] as Array[String]), (["&&"] as Array[String]), (["||"] as Array[String]), (["=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "|=", "&=", "^=", "=>"] as Array[String]), (["->"] as Array[String])] as Array[Array[String]])
var priorities: Array[Variant] = ([(["%"] as Array[Variant]), (["*", "/"] as Array[Variant]), (["+", "-"] as Array[Variant]), (["<<", ">>", ">>>"] as Array[Variant]), (["|", "&", "^"] as Array[Variant]), (["==", "!=", ">", "<", ">=", "<="] as Array[Variant]), (["..."] as Array[Variant]), (["&&"] as Array[Variant]), (["||"] as Array[Variant]), (["=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "|=", "&=", "^=", "=>"] as Array[Variant]), (["->"] as Array[Variant])] as Array[Variant])
self.opPriority = haxe_ds_StringMap.new()
self.opRightAssoc = haxe_ds_StringMap.new()
@@ -37,10 +37,10 @@ func _init() -> void:
_g += 1
tempNumber = _g - 1
var i: int = tempNumber
var p: Array[Variant] = (priorities[i] as Array[Variant])
var _g2: int = 0
var _g3: Array[String] = priorities[i]
while (_g2 < _g3.size()):
var x: String = _g3[_g2]
while (_g2 < p.size()):
var x = p[_g2]
_g2 += 1
self.opPriority.__set(x, i)
if (i == 9):
@@ -173,13 +173,13 @@ func ensure(tk) -> void:
if (t != tk):
self.unexpected(t)
func ensureToken(tk) -> void:
func ensureToken(tk: Variant) -> void:
var t = self.token()
if (!Type.enumEq(t, tk)):
self.unexpected(t)
func maybe(tk) -> bool:
func maybe(tk: Variant) -> bool:
var t = self.token()
if (Type.enumEq(t, tk)):
@@ -196,7 +196,7 @@ func getIdent() -> String:
if (tk == null):
self.unexpected(tk)
return null
return ""
else:
if (tk._index == 2):
var _g: String = tk.s
@@ -204,7 +204,7 @@ func getIdent() -> String:
return id
else:
self.unexpected(tk)
return null
return ""
func expr(e: Variant) -> Variant:
return e
@@ -475,7 +475,7 @@ func parseExpr():
tk = self.token()
if (tk == { "_index": 5 }):
if true:
var tk2 = { "_index": 3, "s": "->" }
var tk2: Variant = { "_index": 3, "s": "->" }
var t = self.token()
if (!Type.enumEq(t, tk2)):
self.unexpected(t)
@@ -706,7 +706,7 @@ func parseLambda(args: Array[Variant], pmin) -> Variant:
break
if true:
var tk = { "_index": 3, "s": "->" }
var tk: Variant = { "_index": 3, "s": "->" }
var t = self.token()
if (!Type.enumEq(t, tk)):
self.unexpected(t)
@@ -937,7 +937,7 @@ func parseStructure(id: String):
self.unexpected(t)
var vname: String = self.getIdent()
if true:
var tk = { "_index": 2, "s": "in" }
var tk: Variant = { "_index": 2, "s": "in" }
var t = self.token()
if (!Type.enumEq(t, tk)):
self.unexpected(t)
@@ -1180,7 +1180,7 @@ func parseStructure(id: String):
"try":
var e = self.parseExpr()
if true:
var tk = { "_index": 2, "s": "catch" }
var tk: Variant = { "_index": 2, "s": "catch" }
var t = self.token()
if (!Type.enumEq(t, tk)):
self.unexpected(t)
@@ -1197,7 +1197,7 @@ func parseStructure(id: String):
if (self.allowTypes):
t = self.parseType()
else:
var tk = { "_index": 2, "s": "Dynamic" }
var tk: Variant = { "_index": 2, "s": "Dynamic" }
var t2 = self.token()
if (!Type.enumEq(t2, tk)):
self.unexpected(t2)
@@ -1756,6 +1756,8 @@ func parseType() -> Variant:
_:
return self.unexpected(t)
return null
func parseTypeNext(t: Variant) -> Variant:
var tk = self.token()
@@ -1980,7 +1982,7 @@ func parseModuleDecl() -> Variant:
var name: String = self.getIdent()
var params: Variant = self.parseParams()
if true:
var tk = { "_index": 3, "s": "=" }
var tk: Variant = { "_index": 3, "s": "=" }
var t = self.token()
if (!Type.enumEq(t, tk)):
self.unexpected(t)
@@ -2655,6 +2657,8 @@ func evalPreproCond(e: Variant) -> bool:
assert(false, str(err))
return false
return false
func preprocess(id: String) -> Variant:
match (id):
"else", "elseif":
@@ -2846,4 +2850,3 @@ func tokenString(t: Variant) -> String:
tempResult = "#" + id
return tempResult

View File

@@ -405,4 +405,3 @@ static func bound(Value: float, Min = null, Max = null) -> float:
tempResult = lowerBound
return tempResult

View File

@@ -776,4 +776,3 @@ static func FromFile(script: String, files = null, randomSeed = null) -> Story:
var ast2: Array[Variant] = parser2.parseFile(script, files, false)
return Story.FromAST(script, ast2, randomSeed)

View File

@@ -1,142 +0,0 @@
class_name StoryTestCase
var testsDirectory: String
var files: Variant = haxe_ds_StringMap.new()
var lastTranscriptLine: int = 0
func _init(testsDirectory2: String, preloadedFiles = null) -> void:
self.testsDirectory = testsDirectory2
if (preloadedFiles != null):
self.files = preloadedFiles
func testAllExamples() -> void:
var exampleTranscripts: Variant = haxe_ds_StringMap.new()
var exampleFolders: Array[String] = FileSystem.readDirectory(self.testsDirectory)
if true:
var _g: int = 0
while (_g < exampleFolders.size()):
var folder: String = exampleFolders[_g]
_g += 1
var files2: Array[String] = FileSystem.readDirectory("" + self.testsDirectory + "/" + folder)
if true:
var tempArray
if true:
var _g2: Array[String] = ([] as Array[String])
if true:
var _g1: int = 0
while (_g1 < files2.size()):
var file: String = files2[_g1]
_g1 += 1
if (StringTools.endsWith(file, ".hlog")):
_g2.push_back(file)
tempArray = _g2
var value: Array[String] = tempArray
exampleTranscripts.__set(folder, value)
if true:
var folder: Variant = exampleTranscripts.keys()
while (folder.get("hasNext").call()):
var folder2: String = folder.get("next").call()
if (StringTools.startsWith(folder2, "_")):
LogUtil.cleanTrace(" Skipping tests for example \"" + folder2 + "\"")
continue
LogUtil.cleanTrace(" Running tests for example \"" + folder2 + "\"")
if true:
var _g: int = 0
var _g1 = exampleTranscripts.__get(folder2)
while (_g < _g1.size()):
var file: String = _g1[_g]
_g += 1
var disabled: bool = file.find("disabled") != -1
var debug: bool = file.find("debug") != -1
var partial: bool = file.find("partial") != -1
if (!disabled):
LogUtil.cleanTrace(" Running " + file)
self.validateAgainstTranscript("" + self.testsDirectory + "/" + folder2 + "/main.hank", "" + self.testsDirectory + "/" + folder2 + "/" + file, !partial, debug)
func validateAgainstTranscript(storyFile: String, transcriptFile: String, fullTranscript: bool = true, debug: bool = false) -> void:
var buffer: HankBuffer = HankBuffer.FromFile(transcriptFile, self.files)
var transcriptLines: Array[String] = buffer.lines()
var randomSeed = null
if (StringTools.startsWith(transcriptLines[0], "@")):
randomSeed = transcriptLines[0].substr(1).to_int()
if true:
var index: int = transcriptLines.find(transcriptLines[0])
if (index >= 0):
transcriptLines.remove_at(index)
true
else:
false
var story: Story = null
story = Story.FromFile(storyFile, self.files, randomSeed)
story.hInterface.addVariable("DEBUG", debug)
var i: int = 0
while (i < transcriptLines.size()):
var line: String = transcriptLines[i]
self.lastTranscriptLine = i
if (StringTools.startsWith(line, "#")):
var tempArray
var _this: String = StringTools.trim(line.substr(1))
tempArray = Array(Array(_this.split(":")), Variant.Type.TYPE_STRING, "", null)
var parts: Array[String] = tempArray
HankAssert.equals(StringTools.trim(parts[1]), story.hInterface.evaluateExpr(parts[0], story.nodeScopes), null)
i += 1
continue
var frame: Variant = story.nextFrame()
if (StringTools.startsWith(line, "*")):
var choices: Array[String] = []
while true:
choices.push_back(line.substr(2))
var tempIndex
i += 1
tempIndex = i
line = transcriptLines[tempIndex]
if !(line != null && StringTools.startsWith(line, "*")):
break
var tempArray1
var _g: Array[Variant] = ([] as Array[Variant])
var _g1: int = 0
while (_g1 < choices.size()):
var choice: String = choices[_g1]
_g1 += 1
_g.push_back(([] as Array[Variant]))
tempArray1 = _g
HankAssert.equals({ "_index": 1, "choices": choices, "tags": tempArray1 }, frame, null)
continue
else:
if (StringTools.startsWith(line, ">")):
var tempRight
var s: String = line.substr(1)
var l: int = s.length()
var r: int = 0
while (r < l && StringTools.isSpace(s, r)):
r += 1
if (r > 0):
tempRight = s.substr(r, l - r)
else:
tempRight = s
line = tempRight
var firstColonIdx: int = line.find(":")
if (firstColonIdx == -1):
assert(false, str("Choice line in transcript does not specify expected output: > " + line))
var index: int = line.substr(0, firstColonIdx).to_int() - 1
var expectedOutput: String = StringTools.trim(line.substr(firstColonIdx + 1))
var output: String = story.choose(index)
HankAssert.equals(expectedOutput, output, null)
else:
if (line.length() > 0):
HankAssert.equals({ "_index": 0, "text": line }, frame, null)
if (frame == { "_index": 2 }):
break
i += 1
if (fullTranscript):
HankAssert.equals({ "_index": 2 }, story.nextFrame(), null)

View File

@@ -0,0 +1,10 @@
class_name Type
func _init() -> void:
pass
static func enumEq(a, b) -> bool:
return false
static func enumConstructor(e) -> String:
return ""

View File

@@ -1,13 +1,9 @@
{
"filesGenerated": [
"tests_Examples.gd",
"sys_FileSeek.gd",
"plugin.gd",
"plugin.cfg",
"hscript_Interp_Stop.gd",
"hscript_Expr_FieldAccess.gd",
"haxe_ds_GenericStack_GenericCell.gd",
"haxe_ds_BalancedTree_TreeNode.gd",
"haxe_StringMap.gd",
"haxe_StringKeyValueIterator.gd",
"haxe_StringIterator.gd",
@@ -17,12 +13,12 @@
"haxe_MapKeyValueIterator.gd",
"haxe_IntMap.gd",
"haxe_GenericStack.gd",
"haxe_EnumValueMap.gd",
"haxe_Encoding.gd",
"haxe_BalancedTree.gd",
"haxe_ArrayIterator.gd",
"hank_Story_EmbedMode.gd",
"hank_StoryTree_StoryNode.gd",
"hank_Parser_Type.gd",
"hank_Interp_Stop.gd",
"hank_HankBuffer_Position.gd",
"hank_HankBuffer_BufferSlice.gd",
"hank_HankAST_ASTExtension.gd",
@@ -34,7 +30,6 @@
"SysTools.gd",
"StringTools.gd",
"StringBuf.gd",
"StoryTestCase.gd",
"Story.gd",
"Reflect.gd",
"Random.gd",
@@ -45,16 +40,14 @@
"Interp.gd",
"IntIterator.gd",
"HankBuffer.gd",
"HankAssert.gd",
"HParser.gd",
"HInterface.gd",
"Extensions.gd",
"Exception.gd",
"EReg.gd",
"CallStack.gd",
"Alt.gd"
],
"id": 33,
"id": 62,
"wasCached": false,
"version": 1
}

View File

@@ -7,6 +7,7 @@ var story: Story
func _init(hInterface2: HInterface) -> void:
self.hInterface = hInterface2
super()
func setStory(story2: Story) -> void:
self.story = story2
@@ -115,4 +116,3 @@ func assign(e1: Variant, e2: Variant):
return super.assign(e1, e2)
else:
return super.assign(e1, e2)

View File

@@ -0,0 +1,11 @@
class_name Type
func _init() -> void:
pass
static func enumEq(a, b) -> bool:
return false
static func enumConstructor(e) -> String:
return ""

View File

@@ -1,132 +0,0 @@
class_name haxe_ds_BalancedTree
var root: haxe_ds_TreeNode
func _init() -> void:
pass
func __set(key, value) -> void:
self.root = self.setLoop(key, value, self.root)
func __get(key):
var node: haxe_ds_TreeNode = self.root
while (node != null):
var c: int = self.compare(key, node.key)
if (c == 0):
return node.value
if (c < 0):
node = node.left
else:
node = node.right
return null
func keys() -> Variant:
var ret: Array[Variant] = ([] as Array[Variant])
self.keysLoop(self.root, ret)
return haxe_iterators_ArrayIterator.new(ret)
func setLoop(k, v, node: haxe_ds_TreeNode) -> haxe_ds_TreeNode:
if (node == null):
return haxe_ds_TreeNode.new(null, k, v, null)
var c: int = self.compare(k, node.key)
var tempResult
if (c == 0):
var tempNumber
if (node == null):
tempNumber = 0
else:
tempNumber = node._height
tempResult = haxe_ds_TreeNode.new(node.left, k, v, node.right, tempNumber)
else:
if (c < 0):
var nl: haxe_ds_TreeNode = self.setLoop(k, v, node.left)
tempResult = self.balance(nl, node.key, node.value, node.right)
else:
var nr: haxe_ds_TreeNode = self.setLoop(k, v, node.right)
tempResult = self.balance(node.left, node.key, node.value, nr)
return tempResult
func keysLoop(node: haxe_ds_TreeNode, acc: Array[Variant]) -> void:
if (node != null):
self.keysLoop(node.left, acc)
acc.push_back(node.key)
self.keysLoop(node.right, acc)
func balance(l: haxe_ds_TreeNode, k, v, r: haxe_ds_TreeNode) -> haxe_ds_TreeNode:
var tempNumber
if (l == null):
tempNumber = 0
else:
tempNumber = l._height
var hl: int = tempNumber
var tempNumber1
if (r == null):
tempNumber1 = 0
else:
tempNumber1 = r._height
var hr: int = tempNumber1
var tempResult
if (hl > hr + 2):
var tempLeft
if true:
var _this: haxe_ds_TreeNode = l.left
if (_this == null):
tempLeft = 0
else:
tempLeft = _this._height
var tempRight
if true:
var _this: haxe_ds_TreeNode = l.right
if (_this == null):
tempRight = 0
else:
tempRight = _this._height
if (tempLeft >= tempRight):
tempResult = haxe_ds_TreeNode.new(l.left, l.key, l.value, haxe_ds_TreeNode.new(l.right, k, v, r))
else:
tempResult = haxe_ds_TreeNode.new(haxe_ds_TreeNode.new(l.left, l.key, l.value, l.right.left), l.right.key, l.right.value, haxe_ds_TreeNode.new(l.right.right, k, v, r))
else:
if (hr > hl + 2):
var tempLeft1
if true:
var _this: haxe_ds_TreeNode = r.right
if (_this == null):
tempLeft1 = 0
else:
tempLeft1 = _this._height
var tempRight1
if true:
var _this: haxe_ds_TreeNode = r.left
if (_this == null):
tempRight1 = 0
else:
tempRight1 = _this._height
if (tempLeft1 > tempRight1):
tempResult = haxe_ds_TreeNode.new(haxe_ds_TreeNode.new(l, k, v, r.left), r.key, r.value, r.right)
else:
tempResult = haxe_ds_TreeNode.new(haxe_ds_TreeNode.new(l, k, v, r.left.left), r.left.key, r.left.value, haxe_ds_TreeNode.new(r.left.right, r.key, r.value, r.right))
else:
var tempNumber2
if (hl > hr):
tempNumber2 = hl
else:
tempNumber2 = hr
tempResult = haxe_ds_TreeNode.new(l, k, v, r, (tempNumber2) + 1)
return tempResult
func compare(k1, k2) -> int:
return Reflect.compare(k1, k2)

View File

@@ -50,4 +50,3 @@ func compareArg(v1, v2) -> int:
tempResult = Reflect.compare(v1, v2)
return tempResult

View File

@@ -1,46 +0,0 @@
class_name haxe_ds_TreeNode
var left: haxe_ds_TreeNode
var right: haxe_ds_TreeNode
var key
var value
var _height: int
func _init(l: haxe_ds_TreeNode, k, v, r: haxe_ds_TreeNode, h: int = -1) -> void:
self.left = l
self.key = k
self.value = v
self.right = r
if (h == -1):
var tempNumber
var tempLeft
if true:
var _this: haxe_ds_TreeNode = self.left
if (_this == null):
tempLeft = 0
else:
tempLeft = _this._height
var tempRight
if true:
var _this: haxe_ds_TreeNode = self.right
if (_this == null):
tempRight = 0
else:
tempRight = _this._height
if (tempLeft > tempRight):
var _this: haxe_ds_TreeNode = self.left
if (_this == null):
tempNumber = 0
else:
tempNumber = _this._height
else:
var _this: haxe_ds_TreeNode = self.right
if (_this == null):
tempNumber = 0
else:
tempNumber = _this._height
self._height = (tempNumber) + 1
else:
self._height = h

View File

@@ -1,7 +0,0 @@
class_name FileSeek extends Object
enum FileSeek {
SeekBegin,
SeekCur,
SeekEnd,
}

View File

@@ -1,18 +0,0 @@
class_name Examples
static var files: Variant = haxe_ds_StringMap.new()
static var fileBuffer = func(path: String) -> HankBuffer:
return HankBuffer.new(path, files.__get(path))
func _init() -> void:
pass
static func main() -> void:
haxe_Log.trace.call("Testing examples for target " + LogUtil.currentTarget(), {
"fileName": "../tests/main/Examples.hx",
"lineNumber": 8,
"className": "tests.main.Examples",
"methodName": "main"
})
StoryTestCase.new("examples").testAllExamples()

View File

@@ -19,16 +19,7 @@ class Alt {
var behavior:AltBehavior;
var outputs:Array<Output>;
static var behaviorMap = [
'>' => Sequence,
'!' => OnceOnly,
'&' => Cycle,
'%' => Shuffle,
'sequence:' => Sequence,
'once:' => OnceOnly,
'cycle:' => Cycle,
'shuffle:' => Shuffle
];
static var behaviorMap:Array<{key:String, value:AltBehavior}> = [];
public function new(behavior:AltBehavior, outputs:Array<Output>) {
this.behavior = behavior;
@@ -41,12 +32,23 @@ class Alt {
var expr = rawExpr.substr(1, rawExpr.length - 2).ltrim();
// trace (expr);
if(behaviorMap.length == 0) {
behaviorMap.push({key: '>', value: Sequence});
behaviorMap.push({key: '!', value: OnceOnly});
behaviorMap.push({key: '&', value: Cycle});
behaviorMap.push({key: '%', value: Shuffle});
behaviorMap.push({key: 'sequence:', value: Sequence});
behaviorMap.push({key: 'once:', value: OnceOnly});
behaviorMap.push({key: 'cycle:', value: Cycle});
behaviorMap.push({key: 'shuffle:', value: Shuffle});
}
// Sequences are the default behavior
var behavior = Sequence;
for (prefix in behaviorMap.keys()) {
if (expr.startsWith(prefix)) {
expr = expr.substr(prefix.length).trim();
behavior = behaviorMap[prefix];
for (prefix in behaviorMap) {
if (expr.startsWith(prefix.key)) {
expr = expr.substr(prefix.key.length).trim();
behavior = prefix.value;
break; // <-- Finally figured that one out.
}
}

View File

@@ -4,8 +4,8 @@ using Reflect;
using Type;
import haxe.ds.Option;
import hscript.Parser;
import hscript.Interp;
import hank.Parser;
import hank.Interp;
import hscript.Expr;
using hank.Extensions;
@@ -24,6 +24,9 @@ class HankInterp extends Interp {
public function new(hInterface:HInterface) {
this.hInterface = hInterface;
super();
#if gdscript
untyped __gdscript__("super()");
#end
}
public override function expr(e:Expr):Dynamic {

View File

@@ -20,18 +20,6 @@ class HParser {
public function new() {
choices = 0;
symbols.push({key: 'INCLUDE ', value: include});
symbols.push({key: '<-', value: thread});
symbols.push({key: '->', value: divert});
symbols.push({key: '===', value: knot});
symbols.push({key: '==', value: knot});
symbols.push({key: '=', value: stitch});
symbols.push({key: '~', value: haxeLine});
symbols.push({key: '```', value: haxeBlock});
symbols.push({key: '-', value: gather});
symbols.push({key: '*', value: choice});
symbols.push({key: '+', value: choice});
symbols.push({key: '#', value: tag});
}
public function parseString(h:String):HankAST {
@@ -102,6 +90,20 @@ class HParser {
}
static function parseExpr(buffer:HankBuffer, position:HankBuffer.Position):ExprType {
if (symbols.length == 0) {
symbols.push({key: 'INCLUDE ', value: include});
symbols.push({key: '<-', value: thread});
symbols.push({key: '->', value: divert});
symbols.push({key: '===', value: knot});
symbols.push({key: '==', value: knot});
symbols.push({key: '=', value: stitch});
symbols.push({key: '~', value: haxeLine});
symbols.push({key: '```', value: haxeBlock});
symbols.push({key: '-', value: gather});
symbols.push({key: '*', value: choice});
symbols.push({key: '+', value: choice});
symbols.push({key: '#', value: tag});
};
var line = buffer.peekLine();
switch (line) {
case None:

View File

@@ -88,15 +88,18 @@ class HankBuffer {
public static function FromFile(path:String, ?files:PreloadedFiles) {
// Keep a raw buffer of the file for tracking accurate file positions
#if (sys || hxnodejs)
var rawBuffer = sys.io.File.getContent(path);
var rawBuffer:String = "";
#if gdscript
untyped __gdscript__('{0} = FileAccess.open({1}, FileAccess.READ).get_as_text()', rawBuffer, path);
#elseif (sys || hxnodejs)
rawBuffer = sys.io.File.getContent(path);
#else
if (files == null) {
throw 'Tried to open file $path on a non-sys platform without passing in preloaded files';
} else if (!files.exists(path)) {
throw 'Tried to open file $path that was not pre-loaded';
}
var rawBuffer = files[path];
rawBuffer = files[path];
#end
return new HankBuffer(path, rawBuffer);
}
@@ -148,6 +151,7 @@ class HankBuffer {
}
start += 1;
}
return -1;
}
public function rootSplit(delimiter:String):Array<String> {

707
hank/Interp.hx Normal file
View File

@@ -0,0 +1,707 @@
/*
* Copyright (C)2008-2017 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package hank;
import haxe.PosInfos;
import hscript.Expr;
import haxe.Constraints.IMap;
import hscript.Tools;
private enum Stop {
SBreak;
SContinue;
SReturn;
}
class Interp {
#if haxe3
public var variables : Map<String,Dynamic>;
var locals : Map<String,{ r : Dynamic }>;
var binops : Map<String, Expr -> Expr -> Dynamic >;
#else
public var variables : Hash<Dynamic>;
var locals : Hash<{ r : Dynamic }>;
var binops : Hash< Expr -> Expr -> Dynamic >;
#end
var depth : Int;
var inTry : Bool;
var declared : Array<{ n : String, old : { r : Dynamic } }>;
var returnValue : Dynamic;
#if hscriptPos
var curExpr : Expr;
#end
public function new() {
#if haxe3
locals = new Map();
#else
locals = new Hash();
#end
declared = new Array();
resetVariables();
initOps();
}
private function resetVariables(){
#if haxe3
variables = new Map<String,Dynamic>();
#else
variables = new Hash();
#end
variables.set("null",null);
variables.set("true",true);
variables.set("false",false);
variables.set("trace", Reflect.makeVarArgs(function(el) {
var inf = posInfos();
var v = el.shift();
if( el.length > 0 ) inf.customParams = el;
haxe.Log.trace(Std.string(v), inf);
}));
}
public function posInfos(): PosInfos {
#if hscriptPos
if (curExpr != null)
return cast { fileName : curExpr.origin, lineNumber : curExpr.line };
#end
return cast { fileName : "hscript", lineNumber : 0 };
}
function initOps() {
var me = this;
#if haxe3
binops = new Map();
#else
binops = new Hash();
#end
binops.set("+",function(e1,e2) return me.expr(e1) + me.expr(e2));
binops.set("-",function(e1,e2) return me.expr(e1) - me.expr(e2));
binops.set("*",function(e1,e2) return me.expr(e1) * me.expr(e2));
binops.set("/",function(e1,e2) return me.expr(e1) / me.expr(e2));
binops.set("%",function(e1,e2) return me.expr(e1) % me.expr(e2));
binops.set("&",function(e1,e2) return me.expr(e1) & me.expr(e2));
binops.set("|",function(e1,e2) return me.expr(e1) | me.expr(e2));
binops.set("^",function(e1,e2) return me.expr(e1) ^ me.expr(e2));
binops.set("<<",function(e1,e2) return me.expr(e1) << me.expr(e2));
binops.set(">>",function(e1,e2) return me.expr(e1) >> me.expr(e2));
binops.set(">>>",function(e1,e2) return me.expr(e1) >>> me.expr(e2));
binops.set("==",function(e1,e2) return me.expr(e1) == me.expr(e2));
binops.set("!=",function(e1,e2) return me.expr(e1) != me.expr(e2));
binops.set(">=",function(e1,e2) return me.expr(e1) >= me.expr(e2));
binops.set("<=",function(e1,e2) return me.expr(e1) <= me.expr(e2));
binops.set(">",function(e1,e2) return me.expr(e1) > me.expr(e2));
binops.set("<",function(e1,e2) return me.expr(e1) < me.expr(e2));
binops.set("||",function(e1,e2) return me.expr(e1) == true || me.expr(e2) == true);
binops.set("&&",function(e1,e2) return me.expr(e1) == true && me.expr(e2) == true);
binops.set("=",assign);
binops.set("...",function(e1,e2) return new #if (haxe_211 || haxe3) IntIterator #else IntIter #end(me.expr(e1),me.expr(e2)));
assignOp("+=",function(v1:Dynamic,v2:Dynamic) return v1 + v2);
assignOp("-=",function(v1:Float,v2:Float) return v1 - v2);
assignOp("*=",function(v1:Float,v2:Float) return v1 * v2);
assignOp("/=",function(v1:Float,v2:Float) return v1 / v2);
assignOp("%=",function(v1:Int,v2:Int) return v1 % v2);
assignOp("&=",function(v1,v2) return v1 & v2);
assignOp("|=",function(v1,v2) return v1 | v2);
assignOp("^=",function(v1,v2) return v1 ^ v2);
assignOp("<<=",function(v1,v2) return v1 << v2);
assignOp(">>=",function(v1,v2) return v1 >> v2);
assignOp(">>>=",function(v1,v2) return v1 >>> v2);
}
function setVar( name : String, v : Dynamic ) {
variables.set(name, v);
}
function assign( e1 : Expr, e2 : Expr ) : Dynamic {
var v = expr(e2);
switch( Tools.expr(e1) ) {
case EIdent(id):
var l = locals.get(id);
if( l == null )
setVar(id,v)
else
l.r = v;
case EField(e,f):
v = set(expr(e),f,v);
case EArray(e, index):
var arr:Dynamic = expr(e);
var index:Dynamic = expr(index);
if (isMap(arr)) {
setMapValue(arr, index, v);
}
else {
arr[index] = v;
}
default:
error(EInvalidOp("="));
}
return v;
}
function assignOp( op, fop : Dynamic -> Dynamic -> Dynamic ) {
var me = this;
binops.set(op,function(e1,e2) return me.evalAssignOp(op,fop,e1,e2));
}
function evalAssignOp(op,fop,e1,e2) : Dynamic {
var v;
switch( Tools.expr(e1) ) {
case EIdent(id):
var l = locals.get(id);
v = fop(expr(e1),expr(e2));
if( l == null )
setVar(id,v)
else
l.r = v;
case EField(e,f):
var obj = expr(e);
v = fop(get(obj,f),expr(e2));
v = set(obj,f,v);
case EArray(e, index):
var arr:Dynamic = expr(e);
var index:Dynamic = expr(index);
if (isMap(arr)) {
v = fop(getMapValue(arr, index), expr(e2));
setMapValue(arr, index, v);
}
else {
v = fop(arr[index],expr(e2));
arr[index] = v;
}
default:
return error(EInvalidOp(op));
}
return v;
}
function increment( e : Expr, prefix : Bool, delta : Int ) : Dynamic {
#if hscriptPos
curExpr = e;
var e = e.e;
#end
switch(e) {
case EIdent(id):
var l = locals.get(id);
var v : Dynamic = (l == null) ? resolve(id) : l.r;
if( prefix ) {
v += delta;
if( l == null ) setVar(id,v) else l.r = v;
} else
if( l == null ) setVar(id,v + delta) else l.r = v + delta;
return v;
case EField(e,f):
var obj = expr(e);
var v : Dynamic = get(obj,f);
if( prefix ) {
v += delta;
set(obj,f,v);
} else
set(obj,f,v + delta);
return v;
case EArray(e, index):
var arr:Dynamic = expr(e);
var index:Dynamic = expr(index);
if (isMap(arr)) {
var v = getMapValue(arr, index);
if (prefix) {
v += delta;
setMapValue(arr, index, v);
}
else {
setMapValue(arr, index, v + delta);
}
return v;
}
else {
var v = arr[index];
if( prefix ) {
v += delta;
arr[index] = v;
} else
arr[index] = v + delta;
return v;
}
default:
return error(EInvalidOp((delta > 0)?"++":"--"));
}
}
public function execute( expr : Expr ) : Dynamic {
depth = 0;
#if haxe3
locals = new Map();
#else
locals = new Hash();
#end
declared = new Array();
return exprReturn(expr);
}
function exprReturn(e) : Dynamic {
try {
return expr(e);
} catch( e : Stop ) {
switch( e ) {
case SBreak: throw "Invalid break";
case SContinue: throw "Invalid continue";
case SReturn:
var v = returnValue;
returnValue = null;
return v;
}
}
return null;
}
function duplicate<T>( h : #if haxe3 Map < String, T > #else Hash<T> #end ) {
#if haxe3
var h2 = new Map();
#else
var h2 = new Hash();
#end
for( k in h.keys() )
h2.set(k,h.get(k));
return h2;
}
function restore( old : Int ) {
while( declared.length > old ) {
var d = declared.pop();
locals.set(d.n,d.old);
}
}
inline function error(e : #if hscriptPos ErrorDef #else Error #end, rethrow=false ) : Dynamic {
#if hscriptPos var e = new Error(e, curExpr.pmin, curExpr.pmax, curExpr.origin, curExpr.line); #end
if( rethrow ) this.rethrow(e) else throw e;
return null;
}
inline function rethrow( e : Dynamic ) {
#if hl
hl.Api.rethrow(e);
#else
throw e;
#end
}
function resolve( id : String ) : Dynamic {
var l = locals.get(id);
if( l != null )
return l.r;
var v = variables.get(id);
if( v == null && !variables.exists(id) )
error(EUnknownVariable(id));
return v;
}
public function expr( e : Expr ) : Dynamic {
#if hscriptPos
curExpr = e;
var e = e.e;
#end
switch( e ) {
case EConst(c):
switch( c ) {
case CInt(v): return v;
case CFloat(f): return f;
case CString(s): return s;
#if !haxe3
case CInt32(v): return v;
#end
}
case EIdent(id):
return resolve(id);
case EVar(n,_,e):
declared.push({ n : n, old : locals.get(n) });
locals.set(n,{ r : (e == null)?null:expr(e) });
return null;
case EParent(e):
return expr(e);
case EBlock(exprs):
var old = declared.length;
var v = null;
for( e in exprs )
v = expr(e);
restore(old);
return v;
case EField(e,f):
return get(expr(e),f);
case EBinop(op,e1,e2):
var fop = binops.get(op);
if( fop == null ) error(EInvalidOp(op));
return fop(e1,e2);
case EUnop(op,prefix,e):
switch(op) {
case "!":
return expr(e) != true;
case "-":
return -expr(e);
case "++":
return increment(e,prefix,1);
case "--":
return increment(e,prefix,-1);
case "~":
#if (neko && !haxe3)
return haxe.Int32.complement(expr(e));
#else
return ~expr(e);
#end
default:
error(EInvalidOp(op));
}
case ECall(e,params):
var args = new Array();
for( p in params )
args.push(expr(p));
switch( Tools.expr(e) ) {
case EField(e,f):
var obj = expr(e);
if( obj == null ) error(EInvalidAccess(f));
return fcall(obj,f,args);
default:
return call(null,expr(e),args);
}
case EIf(econd,e1,e2):
return if( expr(econd) == true ) expr(e1) else if( e2 == null ) null else expr(e2);
case EWhile(econd,e):
whileLoop(econd,e);
return null;
case EDoWhile(econd,e):
doWhileLoop(econd,e);
return null;
case EFor(v,it,e):
forLoop(v,it,e);
return null;
case EBreak:
throw SBreak;
case EContinue:
throw SContinue;
case EReturn(e):
returnValue = e == null ? null : expr(e);
throw SReturn;
case EFunction(params,fexpr,name,_):
var capturedLocals = duplicate(locals);
var me = this;
var hasOpt = false, minParams = 0;
for( p in params )
if( p.opt )
hasOpt = true;
else
minParams++;
var f = function(args:Array<Dynamic>) {
if( ( (args == null) ? 0 : args.length ) != params.length ) {
if( args.length < minParams ) {
var str = "Invalid number of parameters. Got " + args.length + ", required " + minParams;
if( name != null ) str += " for function '" + name+"'";
error(ECustom(str));
}
// make sure mandatory args are forced
var args2 = [];
var extraParams = args.length - minParams;
var pos = 0;
for( p in params )
if( p.opt ) {
if( extraParams > 0 ) {
args2.push(args[pos++]);
extraParams--;
} else
args2.push(null);
} else
args2.push(args[pos++]);
args = args2;
}
var old = me.locals, depth = me.depth;
me.depth++;
me.locals = me.duplicate(capturedLocals);
for( i in 0...params.length )
me.locals.set(params[i].name,{ r : args[i] });
var r = null;
var oldDecl = declared.length;
if( inTry )
try {
r = me.exprReturn(fexpr);
} catch( e : Dynamic ) {
me.locals = old;
me.depth = depth;
#if neko
neko.Lib.rethrow(e);
#else
throw e;
#end
}
else
r = me.exprReturn(fexpr);
restore(oldDecl);
me.locals = old;
me.depth = depth;
return r;
};
var f = Reflect.makeVarArgs(f);
if( name != null ) {
if( depth == 0 ) {
// global function
variables.set(name, f);
} else {
// function-in-function is a local function
declared.push( { n : name, old : locals.get(name) } );
var ref = { r : f };
locals.set(name, ref);
capturedLocals.set(name, ref); // allow self-recursion
}
}
return f;
case EArrayDecl(arr):
if (arr.length > 0 && Tools.expr(arr[0]).match(EBinop("=>", _))) {
var isAllString:Bool = true;
var isAllInt:Bool = true;
var isAllObject:Bool = true;
var isAllEnum:Bool = true;
var keys:Array<Dynamic> = [];
var values:Array<Dynamic> = [];
for (e in arr) {
switch(Tools.expr(e)) {
case EBinop("=>", eKey, eValue): {
var key:Dynamic = expr(eKey);
var value:Dynamic = expr(eValue);
isAllString = isAllString && (key is String);
isAllInt = isAllInt && (key is Int);
isAllObject = isAllObject && Reflect.isObject(key);
isAllEnum = isAllEnum && Reflect.isEnumValue(key);
keys.push(key);
values.push(value);
}
default: throw("=> expected");
}
}
var map:Dynamic = {
if (isAllInt) new haxe.ds.IntMap<Dynamic>();
else if (isAllString) new haxe.ds.StringMap<Dynamic>();
#if !gdscript
else if (isAllEnum) new haxe.ds.EnumValueMap<Dynamic, Dynamic>();
#end
else if (isAllObject) new haxe.ds.ObjectMap<Dynamic, Dynamic>();
else throw 'Inconsistent key types';
}
for (n in 0...keys.length) {
setMapValue(map, keys[n], values[n]);
}
return map;
}
else {
var a = new Array();
for ( e in arr ) {
a.push(expr(e));
}
return a;
}
case EArray(e, index):
var arr:Dynamic = expr(e);
var index:Dynamic = expr(index);
if (isMap(arr)) {
return getMapValue(arr, index);
}
else {
return arr[index];
}
case ENew(cl,params):
var a = new Array();
for( e in params )
a.push(expr(e));
return cnew(cl,a);
case EThrow(e):
throw expr(e);
case ETry(e,n,_,ecatch):
var old = declared.length;
var oldTry = inTry;
try {
inTry = true;
var v : Dynamic = expr(e);
restore(old);
inTry = oldTry;
return v;
} catch( err : Stop ) {
inTry = oldTry;
throw err;
} catch( err : Dynamic ) {
// restore vars
restore(old);
inTry = oldTry;
// declare 'v'
declared.push({ n : n, old : locals.get(n) });
locals.set(n,{ r : err });
var v : Dynamic = expr(ecatch);
restore(old);
return v;
}
case EObject(fl):
var o = {};
for( f in fl )
set(o,f.name,expr(f.e));
return o;
case ETernary(econd,e1,e2):
return if( expr(econd) == true ) expr(e1) else expr(e2);
case ESwitch(e, cases, def):
var val : Dynamic = expr(e);
var match = false;
for( c in cases ) {
for( v in c.values )
if( expr(v) == val ) {
match = true;
break;
}
if( match ) {
val = expr(c.expr);
break;
}
}
if( !match )
val = def == null ? null : expr(def);
return val;
case EMeta(_, _, e):
return expr(e);
case ECheckType(e,_):
return expr(e);
}
return null;
}
function doWhileLoop(econd,e) {
var old = declared.length;
do {
try {
expr(e);
} catch( err : Stop ) {
switch(err) {
case SContinue:
case SBreak: break;
case SReturn: throw err;
}
}
}
while( expr(econd) == true );
restore(old);
}
function whileLoop(econd,e) {
var old = declared.length;
while( expr(econd) == true ) {
try {
expr(e);
} catch( err : Stop ) {
switch(err) {
case SContinue:
case SBreak: break;
case SReturn: throw err;
}
}
}
restore(old);
}
function makeIterator( v : Dynamic ) : Iterator<Dynamic> {
#if ((flash && !flash9) || (php && !php7 && haxe_ver < '4.0.0'))
if ( v.iterator != null ) v = v.iterator();
#else
try v = v.iterator() catch( e : Dynamic ) {};
#end
if( v.hasNext == null || v.next == null ) error(EInvalidIterator(v));
return v;
}
function forLoop(n,it,e) {
var old = declared.length;
declared.push({ n : n, old : locals.get(n) });
var it = makeIterator(expr(it));
while( it.hasNext() ) {
locals.set(n,{ r : it.next() });
try {
expr(e);
} catch( err : Stop ) {
switch( err ) {
case SContinue:
case SBreak: break;
case SReturn: throw err;
}
}
}
restore(old);
}
inline function isMap(o:Dynamic):Bool {
return (o is IMap);
}
inline function getMapValue(map:Dynamic, key:Dynamic):Dynamic {
return cast(map, haxe.Constraints.IMap<Dynamic, Dynamic>).get(key);
}
inline function setMapValue(map:Dynamic, key:Dynamic, value:Dynamic):Void {
cast(map, haxe.Constraints.IMap<Dynamic, Dynamic>).set(key, value);
}
function get( o : Dynamic, f : String ) : Dynamic {
if ( o == null ) error(EInvalidAccess(f));
return {
#if php
// https://github.com/HaxeFoundation/haxe/issues/4915
try {
Reflect.getProperty(o, f);
} catch (e:Dynamic) {
Reflect.field(o, f);
}
#else
Reflect.getProperty(o, f);
#end
}
}
function set( o : Dynamic, f : String, v : Dynamic ) : Dynamic {
if( o == null ) error(EInvalidAccess(f));
Reflect.setProperty(o,f,v);
return v;
}
function fcall( o : Dynamic, f : String, args : Array<Dynamic> ) : Dynamic {
return call(o, get(o, f), args);
}
function call( o : Dynamic, f : Dynamic, args : Array<Dynamic> ) : Dynamic {
return Reflect.callMethod(o,f,args);
}
function cnew( cl : String, args : Array<Dynamic> ) : Dynamic {
#if gdscript
return null;
#else
var c = Type.resolveClass(cl);
if( c == null ) c = resolve(cl);
return Type.createInstance(c,args);
#end
}
}

View File

@@ -163,6 +163,7 @@ class Output {
default:
return parseHaxeExpression(buffer);
}
return null;
}
public static function parseHaxeExpression(buffer:HankBuffer):OutputType {

1742
hank/Parser.hx Normal file

File diff suppressed because it is too large Load Diff