Naposledy aktivní 2 days ago

compiler_wip.gd Raw Playground
1extends RefCounted
2class_name NovaCompiler
3# NOTE: Godot's docs for array states that `.resize` + direct assignment is faster
4# than repeatedly calling `append` and `push_back`.
5static var prelude = """
6extends RefCounted
7
8var state = %s
9
10func push(stack_symbol, symbol) -> void:
11 state[stack_symbol].push_back(symbol)
12
13func peek(stack_symbol, depth : int, symbol) -> bool:
14 var stack = state[stack_symbol]
15 if not stack: return false
16 return stack[stack.size() - depth] == symbol
17
18func pop(stack_symbol) -> void:
19 state[stack_symbol].pop_back()
20"""
21
22static func compile(json_string: String) -> GDScript:
23 var json = JSON.new()
24 var error = json.parse(json_string)
25 if error != OK:
26 print("JSON Parse Error: ", json.get_error_message(), " in ", json_string, " at line ", json.get_error_line())
27 return GDScript.new()
28
29 var rules = json.data.rules
30 var state = json.data.state
31
32 var compiled_rules := PackedStringArray([prelude % state])
33 var i := 0
34
35 for rule in rules:
36 var conditions = rule.conditions
37 var results = rule.results
38 compiled_rules.push_back("func _rule_%d() -> bool:" % i)
39 var depths : Dictionary[String, int] = {}
40 for condition in conditions:
41 for action in condition.pattern:
42 match action.type:
43 "variable":
44 # TODO: name mangle this
45 compiled_rules.push_back("\tvar %s" % action.symbol)
46 for condition in conditions:
47 var stack : String = condition.stack.c_escape()
48 for action in condition.pattern:
49 var depth = depths.get_or_add(stack, 0)
50 depths[stack] = depth + 1
51 match action.type:
52 "literal":
53 compiled_rules.push_back("\tif not peek(\"%s\", %d, \"%s\"): return false" % [
54 stack,
55 depth,
56 action.symbol.c_escape()
57 ])
58 "variable":
59 compiled_rules.push_back("\tif %s == null and peek(\"%s\", %d, \"%s\"): return false" % [
60 action.symbol.c_escape(),
61 stack,
62 depth,
63 action.symbol.c_escape()
64 ])
65 compiled_rules.push_back("\t%s = peek(\"%s\", %d, \"%s\")" % [
66 action.symbol.c_escape(),
67 stack,
68 depth,
69 action.symbol.c_escape()
70 ])
71 for condition in conditions:
72 var stack : String = condition.stack.c_escape()
73 for __ in range(condition.pattern.size()):
74 compiled_rules.push_back("\tpop(\"%s\")" % stack)
75 for result in results:
76 var stack : String = result.stack
77 for action in result.pattern:
78 match action.type:
79 "literal":
80 compiled_rules.push_back("\tpush(\"%s\", \"%s\")" % [
81 stack, action.symbol.c_escape()
82 ])
83 "variable":
84 compiled_rules.push_back("\tpush(\"%s\", %s if %s else \"%s\")" % [
85 stack, action.symbol, action.symbol, action.symbol.c_escape()
86 ])
87 pass
88 print(conditions)
89 print(results)
90 print("\n".join(compiled_rules))
91
92 return GDScript.new()
93
progress.gd Raw Playground
1extends RefCounted
2
3var state = { "foo": ["foo", "bar", "baz", "quux"], "bar": ["quux", "baz", "bar", "foo"] }
4
5func push(stack_symbol, symbol) -> void:
6 state[stack_symbol].push_back(symbol)
7
8func peek(stack_symbol, depth : int, symbol) -> bool:
9 var stack = state[stack_symbol]
10 if not stack: return false
11 return stack[stack.size() - depth] == symbol
12
13func pop(stack_symbol) -> void:
14 state[stack_symbol].pop_back()
15
16func _rule_0() -> bool:
17 var baz
18 if not peek("foo", 0, "bar"): return false
19 if baz == null and peek("foo", 1, "baz"): return false
20 baz = peek("foo", 1, "baz")
21 if not peek("foo", 2, "bin"): return false
22 pop("foo")
23 pop("foo")
24 pop("foo")
25 push("bar", "quux")
26 push("bar", baz if baz else "baz")
27 push("bar", "quin")
28