extends RefCounted class_name NovaCompiler # NOTE: Godot's docs for array states that `.resize` + direct assignment is faster # than repeatedly calling `append` and `push_back`. static var prelude = """ extends RefCounted var state = %s func push(stack_symbol, symbol) -> void: state[stack_symbol].push_back(symbol) func peek(stack_symbol, depth : int, symbol) -> bool: var stack = state[stack_symbol] if not stack: return false return stack[stack.size() - depth] == symbol func pop(stack_symbol) -> void: state[stack_symbol].pop_back() """ static func compile(json_string: String) -> GDScript: var json = JSON.new() var error = json.parse(json_string) if error != OK: print("JSON Parse Error: ", json.get_error_message(), " in ", json_string, " at line ", json.get_error_line()) return GDScript.new() var rules = json.data.rules var state = json.data.state var compiled_rules := PackedStringArray([prelude % state]) var i := 0 for rule in rules: var conditions = rule.conditions var results = rule.results compiled_rules.push_back("func _rule_%d() -> bool:" % i) var depths : Dictionary[String, int] = {} for condition in conditions: for action in condition.pattern: match action.type: "variable": # TODO: name mangle this compiled_rules.push_back("\tvar %s" % action.symbol) for condition in conditions: var stack : String = condition.stack.c_escape() for action in condition.pattern: var depth = depths.get_or_add(stack, 0) depths[stack] = depth + 1 match action.type: "literal": compiled_rules.push_back("\tif not peek(\"%s\", %d, \"%s\"): return false" % [ stack, depth, action.symbol.c_escape() ]) "variable": compiled_rules.push_back("\tif %s == null and peek(\"%s\", %d, \"%s\"): return false" % [ action.symbol.c_escape(), stack, depth, action.symbol.c_escape() ]) compiled_rules.push_back("\t%s = peek(\"%s\", %d, \"%s\")" % [ action.symbol.c_escape(), stack, depth, action.symbol.c_escape() ]) for condition in conditions: var stack : String = condition.stack.c_escape() for __ in range(condition.pattern.size()): compiled_rules.push_back("\tpop(\"%s\")" % stack) for result in results: var stack : String = result.stack for action in result.pattern: match action.type: "literal": compiled_rules.push_back("\tpush(\"%s\", \"%s\")" % [ stack, action.symbol.c_escape() ]) "variable": compiled_rules.push_back("\tpush(\"%s\", %s if %s else \"%s\")" % [ stack, action.symbol, action.symbol, action.symbol.c_escape() ]) pass print(conditions) print(results) print("\n".join(compiled_rules)) return GDScript.new()