infornoid 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/infornoid.rb ADDED
@@ -0,0 +1,312 @@
1
+ #coding:utf-8
2
+ # infornoid 智能编译器 — 主驱动
3
+ # 混合架构: 确定性前端 + 规则引擎语义 + LLM 顾问优化 + 确定性代码生成
4
+ # 零第三方依赖, Ruby 标准库实现
5
+ #
6
+ # 子命令风格 CLI (类似 gcc/clang 的分阶段调用):
7
+ # infornoid compile <source.inf> [-o out] [--target js|py] [--no-opt] [--llm]
8
+ # infornoid lex <source.inf> [--json] # 输出 Token 列表
9
+ # infornoid parse <source.inf> [--json] # 输出 AST
10
+ # infornoid semantic <source.inf> [--json] # 输出符号表/类型/错误
11
+ # infornoid optimize <source.inf> [--json] # 输出优化后 AST
12
+ # infornoid codegen <source.inf> [--target js|py] [--llm]
13
+ # infornoid check <source.inf> # 仅做语义检查
14
+ #
15
+ # 兼容旧用法 (无子命令时等同 compile):
16
+ # ruby lib/infornoid.rb <source.inf> [-o out] [--target py] [--ast] [--tokens] [--no-opt] [--llm]
17
+ #
18
+ # 数据流:
19
+ # 源码 → Lexer → Token → Parser → AST → Semantic → Optimizer → Codegen → 目标代码
20
+
21
+ %w[lexer parser advisor semantic optimizer codegen].each{|f| require_relative f }
22
+
23
+ module Infor
24
+ module Compiler
25
+ module_function
26
+
27
+ VERSION = '0.2.0'
28
+
29
+ # 子命令 → 处理函数
30
+ COMMANDS = {
31
+ 'compile' => :cmd_compile,
32
+ 'lex' => :cmd_lex,
33
+ 'parse' => :cmd_parse,
34
+ 'semantic' => :cmd_semantic,
35
+ 'optimize' => :cmd_optimize,
36
+ 'codegen' => :cmd_codegen,
37
+ 'check' => :cmd_check,
38
+ }
39
+
40
+ def main argv
41
+ args = argv.dup
42
+ cmd = nil
43
+
44
+ # 首个非选项参数若不是已存在文件, 则视为子命令 (兼容: 文件路径 → compile)
45
+ if args[0] && !args[0].start_with?('-')
46
+ if COMMANDS.key?(args[0])
47
+ cmd = args.shift
48
+ elsif args[0] == 'help'
49
+ print_help; return 0
50
+ end
51
+ end
52
+
53
+ # 无子命令 → 默认 compile (兼容旧用法: ruby infornoid.rb <file> ...)
54
+ cmd ||= 'compile'
55
+
56
+ # 兼容旧调试标志: --ast / --tokens → 映射为对应子命令
57
+ if cmd == 'compile'
58
+ if args.include?('--tokens')
59
+ cmd = 'lex'; args.delete('--tokens')
60
+ elsif args.include?('--ast')
61
+ cmd = 'parse'; args.delete('--ast')
62
+ end
63
+ end
64
+
65
+ send(COMMANDS[cmd], args)
66
+ end
67
+
68
+ #### 子命令实现 ####
69
+
70
+ # 编译: 完整流水线
71
+ def cmd_compile argv
72
+ src_path, out_path, flags = parse_args(argv)
73
+ unless src_path
74
+ return err
75
+ end
76
+
77
+ src = read_source(src_path)
78
+ return 1 unless src
79
+
80
+ # 词法
81
+ ts = Lexer.lex(src)
82
+ # 语法
83
+ ast = Parser.parse(ts)
84
+ # 语义
85
+ sem = Semantic.analyze(ast)
86
+ report_errors(sem[:errors]) if sem[:errors].any?
87
+
88
+ # 优化
89
+ unless flags[:no_opt]
90
+ opt = Optimizer.optimize(ast, flags[:llm])
91
+ ast = opt[:ast]
92
+ opt[:log].each{|l| $stderr.puts l }
93
+ end
94
+
95
+ # 代码生成
96
+ target = map_target(flags[:target])
97
+ result = Codegen.gen(ast, target, flags[:llm])
98
+ result[:log].each{|l| $stderr.puts l }
99
+
100
+ code = result[:code]
101
+ if out_path
102
+ File.open(out_path, 'w:utf-8'){|f| f.puts code }
103
+ $stderr.puts "[infornoid] 编译完成 → #{out_path}"
104
+ else
105
+ $stdout.puts code
106
+ end
107
+ 0
108
+ end
109
+
110
+ # 词法分析: in [--json]
111
+ def cmd_lex argv
112
+ src_path, _, flags = parse_args(argv)
113
+ return err unless src_path
114
+ src = read_source(src_path)
115
+ return 1 unless src
116
+ ts = Lexer.lex(src)
117
+ if flags[:json]
118
+ require 'json'
119
+ $stdout.puts JSON.pretty_generate(ts)
120
+ else
121
+ ts.each{|tk| $stdout.puts "%-6s | %-12s | L%d:C%d"%[tk[:t], tk[:v], tk[:l], tk[:c]] }
122
+ end
123
+ 0
124
+ end
125
+
126
+ # 语法分析: parse [--json]
127
+ def cmd_parse argv
128
+ src_path, _, flags = parse_args(argv)
129
+ return err unless src_path
130
+ src = read_source(src_path)
131
+ return 1 unless src
132
+ ast = Parser.parse(Lexer.lex(src))
133
+ require 'json'
134
+ $stdout.puts flags[:json] ? JSON.pretty_generate(ast) : ast.inspect
135
+ 0
136
+ end
137
+
138
+ # 语义分析: semantic [--json]
139
+ def cmd_semantic argv
140
+ src_path, _, flags = parse_args(argv)
141
+ return err unless src_path
142
+ src = read_source(src_path)
143
+ return 1 unless src
144
+ sem = Semantic.analyze(Parser.parse(Lexer.lex(src)))
145
+ report_errors(sem[:errors]) if sem[:errors].any?
146
+ if flags[:json]
147
+ require 'json'
148
+ $stdout.puts JSON.pretty_generate(sem)
149
+ else
150
+ $stdout.puts "符号表: #{sem[:types].inspect}" if sem[:types]
151
+ $stdout.puts "错误数: #{sem[:errors].length}"
152
+ end
153
+ sem[:errors].any? ? 1 : 0
154
+ end
155
+
156
+ # 优化: optimize [--json]
157
+ def cmd_optimize argv
158
+ src_path, _, flags = parse_args(argv)
159
+ return err unless src_path
160
+ src = read_source(src_path)
161
+ return 1 unless src
162
+ ast = Parser.parse(Lexer.lex(src))
163
+ opt = Optimizer.optimize(ast, flags[:llm])
164
+ opt[:log].each{|l| $stderr.puts l }
165
+ require 'json'
166
+ $stdout.puts flags[:json] ? JSON.pretty_generate(opt[:ast]) : opt[:ast].inspect
167
+ 0
168
+ end
169
+
170
+ # 代码生成: codegen [--target js|py] [--llm]
171
+ def cmd_codegen argv
172
+ src_path, _, flags = parse_args(argv)
173
+ return err unless src_path
174
+ src = read_source(src_path)
175
+ return 1 unless src
176
+ ast = Parser.parse(Lexer.lex(src))
177
+ opt = Optimizer.optimize(ast, flags[:llm])
178
+ result = Codegen.gen(opt[:ast], map_target(flags[:target]), flags[:llm])
179
+ result[:log].each{|l| $stderr.puts l }
180
+ $stdout.puts result[:code]
181
+ 0
182
+ end
183
+
184
+ # 语法检查: check (只跑词法+语法+语义, 不输出代码)
185
+ def cmd_check argv
186
+ src_path, _, flags = parse_args(argv)
187
+ return err unless src_path
188
+ src = read_source(src_path)
189
+ return 1 unless src
190
+ sem = Semantic.analyze(Parser.parse(Lexer.lex(src)))
191
+ if sem[:errors].any?
192
+ report_errors(sem[:errors])
193
+ 1
194
+ else
195
+ $stderr.puts "[infornoid] 语法检查通过 (#{src_path})"
196
+ 0
197
+ end
198
+ end
199
+
200
+ #### 参数解析 ####
201
+
202
+ def parse_args argv
203
+ src_path = nil
204
+ out_path = nil
205
+ flags = { json:false, no_opt:false, llm:false, target:'js' }
206
+ nxt = nil
207
+
208
+ argv.each do|a|
209
+ case a
210
+ when '-o' then nxt = :out
211
+ when '--target' then nxt = :target
212
+ when '--json' then flags[:json] = true
213
+ when '--no-opt' then flags[:no_opt] = true
214
+ when '--llm' then flags[:llm] = true
215
+ when '--help', '-h' then print_help; exit 0
216
+ when '--version', '-v' then $stdout.puts VERSION; exit 0
217
+ else
218
+ case nxt
219
+ when :out then out_path = a; nxt = nil
220
+ when :target then flags[:target] = a; nxt = nil
221
+ else src_path = a
222
+ end
223
+ end
224
+ end
225
+
226
+ [src_path, out_path, flags]
227
+ end
228
+
229
+ #### 辅助 ####
230
+
231
+ def err
232
+ $stderr.puts "用法: infornoid <source.inf> [选项]"
233
+ $stderr.puts "运行 --help 查看完整帮助"
234
+ 1
235
+ end
236
+
237
+ def read_source path
238
+ braw = File.binread(path)
239
+ begin
240
+ braw.force_encoding('UTF-8')
241
+ rescue
242
+ begin
243
+ braw.force_encoding('GBK').encode('UTF-8')
244
+ rescue
245
+ braw.force_encoding('ASCII-8BIT')
246
+ end
247
+ end
248
+ rescue => e
249
+ $stderr.puts "[infornoid] 无法读取文件 #{path}: #{e.message}"
250
+ nil
251
+ end
252
+
253
+ def map_target t
254
+ case t.to_s.downcase
255
+ when 'js', 'javascript' then 'JavaScript'
256
+ when 'py', 'python' then 'Python'
257
+ else 'JavaScript'
258
+ end
259
+ end
260
+
261
+ def report_errors errors
262
+ $stderr.puts "[infornoid] 语义错误 (#{errors.length} 个):"
263
+ errors.each{|e| $stderr.puts " line #{e[:line]}: #{e[:msg]}" }
264
+ end
265
+
266
+ def print_help
267
+ $stdout.puts <<~HELP
268
+ infornoid v#{VERSION} — LLM 辅助编译器 (混合架构)
269
+
270
+ 用法 (子命令风格):
271
+ infornoid compile <source.inf> [-o out] [--target js|py] [--no-opt] [--llm]
272
+ infornoid lex <source.inf> [--json] 输出 Token 列表
273
+ infornoid parse <source.inf> [--json] 输出 AST
274
+ infornoid semantic <source.inf> [--json] 输出语义分析结果
275
+ infornoid optimize <source.inf> [--json] 输出优化后 AST
276
+ infornoid codegen <source.inf> [--target js|py] [--llm] 仅生成代码
277
+ infornoid check <source.inf> 语法检查 (不输出代码)
278
+
279
+ 兼容用法 (无子命令时等同 compile):
280
+ infornoid <source.inf> [-o out] [--target py] [--ast] [--tokens] [--no-opt] [--llm]
281
+
282
+ 选项:
283
+ -o <file> 指定输出文件路径
284
+ --target <lang> 目标语言 (js/javascript, py/python), 默认 js
285
+ --json 以 JSON 输出调试信息 (lex/parse/semantic/optimize)
286
+ --ast 输出 AST JSON (调试用, 等同 parse)
287
+ --tokens 输出 Token 列表 (调试用, 等同 lex)
288
+ --no-opt 跳过优化阶段
289
+ --llm 启用 LLM 顾问 (需要 Ollama 运行)
290
+ --help, -h 显示帮助
291
+ --version, -v 显示版本
292
+
293
+ 架构:
294
+ 源码 → [Lexer] → Token → [Parser] → AST
295
+ → [Semantic 规则引擎] → Typed AST
296
+ → [Optimizer LLM顾问+确定性] → Optimized AST
297
+ → [Codegen 确定性模板] → 目标代码
298
+
299
+ LLM 顾问:
300
+ --llm 标志启用后, 在优化和代码生成阶段调用本地 Ollama.
301
+ LLM 只输出建议性元数据, 不直接生成可执行代码.
302
+ 无 Ollama 时自动降级为纯确定性编译.
303
+ HELP
304
+ end
305
+
306
+ end
307
+ end
308
+
309
+ if :main == :main && $0 == __FILE__
310
+ $rc = Infor::Compiler.main(ARGV)
311
+ exit($rc || 0)
312
+ end
data/lib/lexer.rb ADDED
@@ -0,0 +1,144 @@
1
+ #coding:utf-8
2
+ # infornoid 词法分析器 — 确定性前端
3
+ # 零依赖, 手写扫描器, Token 列表输出
4
+ # CALL: Lexer.lex(source) → [Token]
5
+ # RETN: [{type, value, line, col}]
6
+
7
+ module Infor
8
+ module Lexer
9
+ module_function
10
+
11
+ #### token 定义 ####
12
+
13
+ KWS = {
14
+ 'let'=>:LET, 'fn'=>:FN, 'if'=>:IF, 'else'=>:ELS,
15
+ 'while'=>:WHL, 'return'=>:RTN, 'true'=>:TRU, 'false'=>:FLS
16
+ }
17
+
18
+ TYS = %w[Number String Boolean Void].inject({}){|h,t| h[t]=:TY; h}
19
+
20
+ # 单字符符号表 → token type
21
+ SMAP = {
22
+ '('=>:LPR, ')'=>:RPR, '{'=>:LBR, '}'=>:RBR,
23
+ ';'=>:SCL, ','=>:CMA, ':'=>:COL
24
+ }
25
+
26
+ def lex src
27
+ ts = []
28
+ i = 0
29
+ ln = 1
30
+ co = 1
31
+ sl = src.length
32
+ while i < sl
33
+ c = src[i]
34
+ # 空白
35
+ if c =~ /\s/
36
+ c == "\n" ? (ln += 1; co = 1) : (co += 1)
37
+ i += 1
38
+ next
39
+ end
40
+ # 注释 //...
41
+ if c == '/' && src[i+1] == '/'
42
+ while i < sl && src[i] != "\n"
43
+ i += 1; co += 1
44
+ end
45
+ next
46
+ end
47
+ # 注释 /*...*/
48
+ if c == '/' && src[i+1] == '*'
49
+ i += 2; co += 2
50
+ while i < sl
51
+ src[i] == "\n" ? (ln += 1; co = 1) : (co += 1)
52
+ break if src[i] == '*' && src[i+1] == '/'
53
+ i += 1
54
+ end
55
+ i += 2; co += 2
56
+ next
57
+ end
58
+ # 字符串
59
+ if c == '"'
60
+ i += 1; co += 1
61
+ v = ''
62
+ while i < sl && src[i] != '"'
63
+ if src[i] == '\\' && i+1 < sl
64
+ n = src[i+1]
65
+ v += {'n'=>"\n",'t'=>"\t",'r'=>"\r",'\\'=>'\\','"'=>'"'}[n] || n
66
+ i += 2; co += 2
67
+ else
68
+ v += src[i]; i += 1; co += 1
69
+ end
70
+ end
71
+ err "unterminated string", ln, co and return ts if i >= sl
72
+ i += 1; co += 1
73
+ ts << {t: :STR, v:v, l:ln, c:co}
74
+ next
75
+ end
76
+ # 数字
77
+ if c =~ /[0-9]/
78
+ s = co
79
+ v = ''
80
+ while i < sl && src[i] =~ /[0-9.]/
81
+ v += src[i]; i += 1; co += 1
82
+ end
83
+ ts << {t: :NUM, v:v, l:ln, c:s}
84
+ next
85
+ end
86
+ # 标识符 / 关键字
87
+ if c =~ /[a-zA-Z_]/
88
+ s = co
89
+ v = ''
90
+ while i < sl && src[i] =~ /[a-zA-Z0-9_]/
91
+ v += src[i]; i += 1; co += 1
92
+ end
93
+ tt = KWS[v] || TYS[v] || :IDT
94
+ ts << {t:tt, v:v, l:ln, c:s}
95
+ next
96
+ end
97
+ # 多字符运算符
98
+ if c == '-' && src[i+1] == '>'
99
+ ts << {t: :ARR, v:'->', l:ln, c:co}
100
+ i += 2; co += 2
101
+ next
102
+ end
103
+ m2 = { '=='=>:EQ, '!='=>:NE, '<='=>:LE, '>='=>:GE,
104
+ '&&'=>:AND, '||'=>:OR }[src[i,2]]
105
+ if m2
106
+ ts << {t:m2, v:src[i,2], l:ln, c:co}
107
+ i += 2; co += 2
108
+ next
109
+ end
110
+ # 单字符运算符
111
+ m1 = { '='=>:ASN, '+'=>:ADD, '-'=>:SUB, '*'=>:MUL, '/'=>:DIV,
112
+ '%'=>:MOD, '<'=>:LT, '>'=>:GT, '!'=>:NOT }[c]
113
+ if m1
114
+ ts << {t:m1, v:c, l:ln, c:co}
115
+ i += 1; co += 1
116
+ next
117
+ end
118
+ # 分隔符
119
+ if SMAP[c]
120
+ ts << {t:SMAP[c], v:c, l:ln, c:co}
121
+ i += 1; co += 1
122
+ next
123
+ end
124
+ err "unexpected char '#{c}'", ln, co
125
+ i += 1; co += 1
126
+ end
127
+ ts << {t: :EOF, v:'', l:ln, c:co}
128
+ ts
129
+ end
130
+
131
+ def err msg, ln, co
132
+ $stderr.puts "[lex error] line %d col %d: %s"%[ln, co, msg]
133
+ end
134
+
135
+ end
136
+ end
137
+
138
+ if :main == :main && $0 == __FILE__
139
+ #### 自测 ####
140
+ while line = ARGF.gets
141
+ ts = Infor::Lexer.lex(line)
142
+ ts.each{|tk| puts "%-6s | %-12s | L%d:C%d"%[tk[:t], tk[:v], tk[:l], tk[:c]] }
143
+ end
144
+ end
data/lib/optimizer.rb ADDED
@@ -0,0 +1,238 @@
1
+ #coding:utf-8
2
+ # infornoid 优化器 — LLM 顾问 + 确定性执行
3
+ # 确定性 Pass: 常量折叠, 死代码消除
4
+ # LLM 角色: 分析 IR 输出优化建议元数据, 编译器验证后执行
5
+ # CALL: Optimizer.optimize(ast, use_llm) → {ast, passes, log}
6
+
7
+ module Infor
8
+ module Optimizer
9
+ module_function
10
+
11
+ def optimize ast, use_llm = false
12
+ @log = []
13
+ @passes = []
14
+
15
+ # 1. 确定性常量折叠
16
+ ast = constant_fold(ast)
17
+ log_pass 'constant_folding', '常量折叠 (确定性)'
18
+
19
+ # 2. 确定性死代码消除
20
+ before = count_nodes(ast)
21
+ ast = dead_code_elim(ast)
22
+ after = count_nodes(ast)
23
+ if after < before
24
+ log_pass 'dead_code_elimination', "死代码消除: #{before} → #{after} 节点"
25
+ end
26
+
27
+ # 3. LLM 顾问 (可选)
28
+ if use_llm
29
+ advice = Advisor.optimize_advice(ast)
30
+ if advice[:ok]
31
+ advice[:suggestions].each do|sg|
32
+ r = apply_llm_pass(ast, sg)
33
+ if r && r[:applied]
34
+ ast = r[:ast]
35
+ log_pass sg['pass'] || sg[:pass], "LLM 建议: #{sg['reason'] || sg[:reason]}"
36
+ end
37
+ end
38
+ else
39
+ @log << "[advisor] LLM 降级: #{advice[:reason]}" if advice[:reason]
40
+ end
41
+ end
42
+
43
+ { ast:ast, passes:@passes, log:@log }
44
+ end
45
+
46
+ #### 常量折叠 — 确定性 ####
47
+
48
+ def constant_fold node
49
+ return nil unless node
50
+ case node[:type]
51
+ when 'Program'
52
+ node[:body] = node[:body].map{|s| constant_fold(s) }.compact
53
+ node
54
+ when 'Block'
55
+ node[:body] = node[:body].map{|s| constant_fold(s) }.compact
56
+ node
57
+ when 'FnDecl'
58
+ node[:body] = constant_fold(node[:body])
59
+ node
60
+ when 'LetDecl'
61
+ node[:value] = constant_fold(node[:value])
62
+ node
63
+ when 'IfStmt'
64
+ node[:cond] = constant_fold(node[:cond])
65
+ node[:then] = constant_fold(node[:then])
66
+ node[:els] = constant_fold(node[:els]) if node[:els]
67
+ # 条件为常量 → 编译时分支消除
68
+ if node[:cond][:type] == 'BoolLit'
69
+ return node[:cond][:value] ? node[:then] : (node[:els] || { type:'Block', body:[], line:node[:line] })
70
+ end
71
+ node
72
+ when 'WhileStmt'
73
+ node[:cond] = constant_fold(node[:cond])
74
+ node[:body] = constant_fold(node[:body])
75
+ # 条件为 false → 整个循环消除
76
+ if node[:cond][:type] == 'BoolLit' && !node[:cond][:value]
77
+ return nil
78
+ end
79
+ node
80
+ when 'ReturnStmt'
81
+ node[:value] = constant_fold(node[:value]) if node[:value]
82
+ node
83
+ when 'ExprStmt'
84
+ node[:expr] = constant_fold(node[:expr])
85
+ node
86
+ when 'Assign'
87
+ node[:value] = constant_fold(node[:value])
88
+ node
89
+ when 'Binary'
90
+ node[:left] = constant_fold(node[:left])
91
+ node[:right] = constant_fold(node[:right])
92
+ try_fold(node)
93
+ when 'Unary'
94
+ node[:operand] = constant_fold(node[:operand])
95
+ try_fold_unary(node)
96
+ else
97
+ node
98
+ end
99
+ end
100
+
101
+ #尝试折叠二元常量
102
+ def try_fold node
103
+ l = node[:left]
104
+ r = node[:right]
105
+ return node unless l[:type] == 'NumLit' && r[:type] == 'NumLit'
106
+ lv = eval_num(l[:value])
107
+ rv = eval_num(r[:value])
108
+ result = case node[:op]
109
+ when '+' then lv + rv
110
+ when '-' then lv - rv
111
+ when '*' then lv * rv
112
+ when '/' then rv == 0 ? nil : lv / rv
113
+ when '%' then rv == 0 ? nil : lv % rv
114
+ when '<' then lv < rv
115
+ when '>' then lv > rv
116
+ when '<=' then lv <= rv
117
+ when '>=' then lv >= rv
118
+ when '==' then lv == rv
119
+ when '!=' then lv != rv
120
+ end
121
+ return node unless result
122
+ if [true, false].include?(result)
123
+ { type:'BoolLit', value:result, line:node[:line] }
124
+ else
125
+ { type:'NumLit', value:result.to_s, line:node[:line] }
126
+ end
127
+ end
128
+
129
+ def try_fold_unary node
130
+ o = node[:operand]
131
+ return node unless o[:type] == 'NumLit' || o[:type] == 'BoolLit'
132
+ case node[:op]
133
+ when '-'
134
+ v = eval_num(o[:value])
135
+ { type:'NumLit', value:(-v).to_s, line:node[:line] }
136
+ when '!'
137
+ { type:'BoolLit', value:!o[:value], line:node[:line] }
138
+ else
139
+ node
140
+ end
141
+ end
142
+
143
+ #### 死代码消除 — 确定性 ####
144
+
145
+ def dead_code_elim node
146
+ return nil unless node
147
+ case node[:type]
148
+ when 'Program', 'Block'
149
+ node[:body] = node[:body].map{|s| dead_code_elim(s) }.compact
150
+ # 移除 return 之后的语句
151
+ ret_idx = node[:body].index{ |s| s[:type] == 'ReturnStmt' }
152
+ if ret_idx && ret_idx < node[:body].length - 1
153
+ node[:body] = node[:body][0..ret_idx]
154
+ @log << "[dce] 移除 return 后的死代码 (line #{node[:line]})"
155
+ end
156
+ node
157
+ when 'FnDecl'
158
+ node[:body] = dead_code_elim(node[:body])
159
+ node
160
+ when 'IfStmt'
161
+ node[:then] = dead_code_elim(node[:then])
162
+ node[:els] = dead_code_elim(node[:els]) if node[:els]
163
+ node
164
+ when 'WhileStmt'
165
+ node[:body] = dead_code_elim(node[:body])
166
+ node
167
+ else
168
+ node
169
+ end
170
+ end
171
+
172
+ #### LLM 顾问建议执行 ####
173
+
174
+ def apply_llm_pass ast, suggestion
175
+ pass = suggestion['pass'] || suggestion[:pass]
176
+ case pass
177
+ when 'constant_folding'
178
+ # 已在确定性阶段完成, 跳过
179
+ nil
180
+ when 'dead_code_elimination'
181
+ # 已在确定性阶段完成, 跳过
182
+ nil
183
+ when 'inline_call'
184
+ # ※ 确定性内联: 仅内联单行函数体
185
+ r = inline_single_line(ast)
186
+ r ? { applied:true, ast:r } : nil
187
+ else
188
+ # 未实现的 Pass, 跳过
189
+ nil
190
+ end
191
+ end
192
+
193
+ #内联仅含单个 return 的函数
194
+ def inline_single_line ast
195
+ # 简化实现: 标记但不实际执行内联
196
+ # 复杂内联需 SSA + 控制流图, 此处保守不执行
197
+ nil
198
+ end
199
+
200
+ #### 辅助 ####
201
+
202
+ def eval_num v
203
+ v.to_s.include?('.') ? v.to_f : v.to_i
204
+ end
205
+
206
+ def count_nodes node
207
+ return 0 unless node
208
+ n = 1
209
+ case node[:type]
210
+ when 'Program', 'Block'
211
+ n += node[:body].inject(0){ |s, c| s + count_nodes(c) }
212
+ when 'LetDecl', 'ReturnStmt', 'ExprStmt'
213
+ n += count_nodes(node[:value]) if node[:value]
214
+ n += count_nodes(node[:expr]) if node[:expr]
215
+ when 'IfStmt'
216
+ n += count_nodes(node[:cond]) + count_nodes(node[:then])
217
+ n += count_nodes(node[:els]) if node[:els]
218
+ when 'WhileStmt'
219
+ n += count_nodes(node[:cond]) + count_nodes(node[:body])
220
+ when 'Binary'
221
+ n += count_nodes(node[:left]) + count_nodes(node[:right])
222
+ when 'Unary'
223
+ n += count_nodes(node[:operand])
224
+ when 'Assign'
225
+ n += count_nodes(node[:target]) + count_nodes(node[:value])
226
+ when 'Call'
227
+ n += node[:args].inject(0){ |s, a| s + count_nodes(a) }
228
+ end
229
+ n
230
+ end
231
+
232
+ def log_pass name, desc
233
+ @passes << name
234
+ @log << "[opt] #{name}: #{desc}"
235
+ end
236
+
237
+ end
238
+ end