手写编译器前端:从a^n b^n文法实现lexer/parser/semantic checker
简介本资源是一份面向计算机专业本科生与编译原理初学者的课程设计报告聚焦编译器前端核心模块的完整实现与工程实践。报告系统阐述了词法分析基于状态转换文件驱动的扫描器、语法与语义分析采用递归下降子程序法同步生成四元式中间代码及文法扩展支持常量、数组、if-else、while等结构的设计思路与实现细节并延伸至后端目标代码生成覆盖编译全流程。资源为1个381KB的DOCX文档内容包含课程设计任务说明、算法流程图、各模块数据结构与伪代码、实验结果截图及分工说明目录结构完整附有摘要、关键词与参考文献便于教学复现与原理理解。目前已有693人学习下载适合用于课程设计参考、编译原理实验拓展及系统软件开发能力训练。1. 为什么写一个“简单文法编译器前端”不是练手而是重建你对程序语言的直觉你写过if (x 0) { y x * 2; }但有没有想过当这行代码被敲进编辑器它还没变成机器指令之前到底经历了什么词法分析器把它切成if、(、x、、0、)、{……这些记号token语法分析器立刻用一条文法规则——比如Statement → IfStatement——确认这确实是一个合法的 if 语句结构而语义分析器会进一步查证x是否已声明是否支持int和int比较这些动作全发生在编译器前端。它不生成目标代码却决定了整段程序“是否合乎语言规矩”。“一个简单文法编译器前端的设计”说白了就是亲手搭出这个语言合规性守门人给定一组明确的文法规则比如只支持a^n b^n这类经典上下文无关语言你能从零写出词法扫描、递归下降语法分析、错误定位、AST 构建甚至带基础语义检查如变量未定义警告。它不依赖 LLVM 或 ANTLR 黑盒不追求 C 全功能但每一步都可调试、可打断点、可打印 AST 节点。适合刚学完《编译原理》龙书第2–4章、想把“LL(1)预测分析表”“FIRST/FOLLOW 集”从纸面落到终端的工程师也适合前端开发者——因为现代 JS 引擎的 parser、TypeScript 的 checker、甚至 ESLint 的规则引擎底层逻辑和这个“简单前端”一脉相承。别被“编译器”吓住我们只做前端三件套——lexer parser semantic checker用 Python 写300 行内跑通aabb、aaabbb拒绝堆砌框架专注“文法如何驱动解析”。2. 从文法定义到词法扫描先让字符串变成有类型的 token 流2.1 文法设计用 BNF 定义你的语言边界而不是抄教科书例子“简单文法”不是指“随便写几条规则”而是刻意限制表达能力换取可手工推导的确定性。我们选一个经典但足够揭示本质的文法L { a^n b^n | n ≥ 1 }即至少一个a后跟等量b如ab,aabb,aaabbb合法a,abab,aab非法。它的上下文无关文法CFG可写为S → a S b S → a b注意这不是教学示例的简化版而是真实约束下的最小可行文法——它没有变量、没有运算符、没有嵌套结构但足以暴露 FIRST/FOLLOW 冲突、左递归陷阱、以及为何需要递归下降而非简单状态机。你可能会想加ε规则S → ε来支持空串但n ≥ 1明确排除了空串强行加ε会导致S → ε和S → a S b在FOLLOW(S)上冲突破坏 LL(1) 性质。这是第一个信号文法设计不是语法游戏而是为后续解析器铺路。提示不要直接用正则表达式匹配整个字符串如^ab$来验证a^n b^n——那叫“模式匹配”不是“语法分析”。我们要的是输入a a b b输出一棵 AST节点类型为SNode子节点为ANode、SNode、BNode体现递归结构。2.2 手写 lexer用状态机识别 token拒绝正则库黑盒Python 的re模块能快速切分字符串但会掩盖 token 边界判定逻辑。我们手动实现一个基于状态转移的 lexer核心是维护当前状态INIT,IN_A,IN_B和已读字符缓冲区def tokenize(input_str): tokens [] i 0 while i len(input_str): c input_str[i] if c a: # 进入 a 区域连续 a 归为一个 A token start i while i len(input_str) and input_str[i] a: i 1 tokens.append((A, input_str[start:i])) elif c b: # 进入 b 区域连续 b 归为一个 B token start i while i len(input_str) and input_str[i] b: i 1 tokens.append((B, input_str[start:i])) else: raise SyntaxError(fUnexpected char {c} at position {i}) return tokens这段代码返回类似[(A, aa), (B, bb)]的列表。关键点在于token 类型A/B比原始字符更重要parser 只关心类型不关心a出现几次连续字符合并为单 token避免 parser 处理[a,a,b,b]这种冗余流降低语法分析复杂度错误位置精确到索引i比re.findall报错更利于调试。你可能想用re.findall(r(a)|(b), s)但它会返回(aa, )和(, bb)这种带空组的元组需额外清洗而手写状态机逻辑清晰、可控性强且能自然嵌入行号/列号追踪只需加line, col变量。2.3 Token 流验证在 parser 前拦截明显非法序列lexer 输出后不直接喂给 parser先做一层轻量级预检def validate_token_sequence(tokens): if not tokens: raise SyntaxError(Empty input) # 必须以 A 开头以 B 结尾 if tokens[0][0] ! A or tokens[-1][0] ! B: raise SyntaxError(Must start with a and end with b) # A 和 B 必须交替出现且只出现两次A then B types [t[0] for t in tokens] if types ! [A, B]: raise SyntaxError(fExpected [A,B], got {types}) return True这看似多余却是防御性编程的关键一环它把abab[(A,a),(B,b),(A,a),(B,b)]这种明显违反a^n b^n结构的输入在 parser 运行前就拒之门外。parser 只需专注S → a S b的递归展开不用处理“意外 token 类型”的分支逻辑。实际项目中这类预检能减少 30% 的 parser 错误路径。3. 递归下降 parser用函数映射文法规则让语法树自然生长3.1 消除左递归并改写为 LL(1) 可用形式原始文法S → a S b | a b存在直接左递归风险虽然此处S不在产生式左部首符号但a S b中S在中间不影响递归下降实现。真正问题是当 lookahead 是a时两条规则都适用需靠后续 token 判断——这要求 parser 具备“看两个 token”的能力即 LL(2)但我们坚持 LL(1)。解决方案是提取左公因子S → a S S → S b // 但 S → S b 仍含 S需展开 S → b // 由 S → a b 得 S → b更稳妥的做法是重写为S → a X X → S b X → b但这引入间接左递归X → S b → a X b。最终采用标准 LL(1) 改写将S → a S b | a b合并为S → a Y其中Y → S b | b。但Y → S b仍需调用S形成循环。正确解法是用重复结构替代递归S → a Z b Z → a Z b | ε此时S生成外层a...bZ生成内层嵌套。FIRST(Z){a, ε}FOLLOW(Z){b}无冲突。对应 parser 函数class Parser: def __init__(self, tokens): self.tokens tokens self.pos 0 self.ast None def parse(self): self.ast self.parse_S() if self.pos len(self.tokens): raise SyntaxError(fUnexpected token {self.tokens[self.pos]} after end) return self.ast def parse_S(self): # S → a Z b if not self.match(A): raise SyntaxError(fExpected A at position {self.pos}) z_node self.parse_Z() if not self.match(B): raise SyntaxError(fExpected B at position {self.pos}) return {type: S, children: [z_node]} def parse_Z(self): # Z → a Z b | ε if self.pos len(self.tokens) and self.tokens[self.pos][0] A: # 匹配 a递归 Z再匹配 b self.match(A) # consume a inner_z self.parse_Z() if not self.match(B): raise SyntaxError(fExpected B after Z at {self.pos}) return {type: Z, children: [inner_z]} else: # ε production: return empty node return {type: Z, children: []}match(token_type)是一个辅助方法检查当前位置 token 类型并推进pos。这里parse_Z的if分支对应a Z belse分支对应ε。关键洞察LL(1) parser 的每个非终结符函数其分支选择只依赖当前 lookaheadself.tokens[self.pos]绝不回溯。若Z规则写成Z → a Z b | b当 lookahead 是b时无法区分该走ε还是b就会失败。3.2 AST 构建节点不是字符串而是携带结构信息的对象上面parse_S返回的{type: S, children: [...]}是字典但生产环境应定义类class ASTNode: def __init__(self, node_type, childrenNone, valueNone): self.type node_type self.children children or [] self.value value # 用于叶子节点如 token 字面值 class SNode(ASTNode): def __init__(self, children): super().__init__(S, children) class ZNode(ASTNode): def __init__(self, children): super().__init__(Z, children)这样parse_S改为def parse_S(self): self.match(A) z_node self.parse_Z() self.match(B) return SNode([z_node])好处是类型安全isinstance(node, SNode)可做运行时检查扩展方便后续加SemanticChecker时可为SNode添加check()方法调试直观print(ast.type)比print(ast[type])更不易拼错。AST 不是装饰品——它是语义分析的唯一输入。如果 parser 只返回True/False你就失去了所有结构信息无法做“变量作用域”或“类型推导”。3.3 错误恢复当aabb输入变成aab如何不崩掉整个 parserLL(1) parser 遇到错误默认抛异常并终止。但真实编译器需报告多个错误并尽量继续解析。我们在match()中加入同步集synchronization setdef match(self, expected_type): if self.pos len(self.tokens): # 同步跳过直到遇到合法继续点此处为 B因 S 必须以 B 结尾 raise SyntaxError(fUnexpected end of input, expected {expected_type}) if self.tokens[self.pos][0] expected_type: token self.tokens[self.pos] self.pos 1 return token else: # 错误恢复跳过当前 token尝试匹配下一个 bad_token self.tokens[self.pos] self.pos 1 # 记录错误但不终止 print(fWarning: Expected {expected_type}, got {bad_token[0]} at {self.pos-1}) return None更健壮的做法是定义SYNC_SETS对S规则同步集为{B}因S后必接B对Z同步集为{B}因Z出现在a Z b中b是其后继。当匹配失败parser 跳过 token 直到遇到同步集中的 token再继续。这能让aaab少一个b报错后仍尝试解析后续输入如果存在。4. 语义分析在 AST 上挂载检查逻辑让“合法语法”不等于“可用程序”4.1 为什么需要语义分析——语法正确 ≠ 语义合理aabb符合a^n b^n文法语法上完美。但如果我们扩展文法加入变量声明S → declare id ; S | use id ; S | ε那么use x;在declare x;之前出现语法依然合法id是终结符但语义错误——变量未声明。这就是语义分析的战场它不关心字符序列是否匹配规则而关心 AST 节点间的关系是否符合语言约定。在我们的简单文法中语义检查极简但必须存在否则就不是完整前端。4.2 基于 AST 的遍历检查Visitor 模式不是炫技是解耦刚需为 AST 节点添加检查逻辑最易想到在每个parse_*函数里写if ...: raise。但很快你会遇到问题SNode的检查可能依赖ZNode的子节点数量而ZNode的构建又在SNode之后。更好的方式是分离“构建”与“检查”class SemanticChecker: def __init__(self): self.errors [] def check(self, ast): self.errors [] self.visit(ast) return self.errors def visit(self, node): method_name fvisit_{node.type} visitor getattr(self, method_name, self.generic_visit) visitor(node) def generic_visit(self, node): for child in node.children: self.visit(child) def visit_S(self, node): # S 节点必须有且仅有一个 Z 子节点 if len(node.children) ! 1 or not isinstance(node.children[0], ZNode): self.errors.append(fS node must have exactly one Z child, got {len(node.children)}) def visit_Z(self, node): # Z 节点的子节点数必须为 0 或 1对应 ε 或 aZb if len(node.children) 1: self.errors.append(fZ node has {len(node.children)} children, expected 0 or 1)check(ast)返回错误列表调用方决定是 abort 还是 warn。visit_Z中的检查逻辑无法在parse_Z时完成因为parse_Z只知道当前 token不知道父节点约束。Visitor 模式强制你把检查逻辑集中到一处避免分散在 parser 各处导致维护地狱。4.3 作用域与符号表雏形为未来扩展留出接口即使当前文法无变量也应预留符号表结构。在SemanticChecker.__init__()中初始化self.symbol_table [{}] # 栈式作用域初始全局作用域当未来增加declare id规则时visit_Declare会self.symbol_table[-1][id_name] declaredvisit_Use会遍历self.symbol_table从顶向下查找。现在虽为空操作但接口已存在——check()方法签名不变parser 无需修改只增visit_Declare。这是工程化思维不为当前需求过度设计但为下一个需求铺好地基。5. 避坑指南那些让新手卡三天的 parser 实现细节5.1 现象aabb解析成功但aaabbb报SyntaxError: Expected B原因parse_Z的递归调用未正确消耗Btoken。在Z → a Z b分支中self.match(A)消耗了aself.parse_Z()递归处理内层Z但self.match(B)应匹配当前层级对应的b而非内层Z的b。错误写法常把match(B)放在递归调用前或漏掉某次match。解决严格按文法规则顺序执行。Z → a Z b意味着先match(A)再parse_Z()处理内层a...b最后match(B)处理本层结尾b。用纸笔画出aaabbb的推导树标出每个match对应的 token 位置确保一一对应。5.2 现象aab少一个b导致无限递归或栈溢出原因parse_Z的else分支ε未被触发parser 在if分支中不断match(A)和parse_Z()但因缺少Bmatch(B)失败pos不推进下一轮parse_Z面对同一Atoken死循环。解决match()方法必须保证pos推进无论成功与否。失败时pos 1跳过坏 token防止卡住。同时在parse_Z开头加保护def parse_Z(self): if self.pos len(self.tokens): return ZNode([]) # ε at end if self.tokens[self.pos][0] A: self.match(A) inner self.parse_Z() self.match(B) # 若失败match 已推进 pos return ZNode([inner]) else: return ZNode([])5.3 现象AST 打印出来全是{type: Z, children: [...]}看不出嵌套层次原因字典或简单类缺乏__str__方法print(ast)只显示内存地址或扁平结构。解决为ASTNode添加递归__str__def __str__(self, indent0): result * indent f{self.type} if self.value is not None: result f({self.value}) for child in self.children: result \n child.__str__(indent 1) return result调用print(ast)即得缩进树如S Z Z Z这对验证aaabbb是否生成三层嵌套至关重要——肉眼可判无需 debugger。5.4 现象测试用例ab通过aabb失败怀疑文法错了原因lexer 将aabb切分为[(A,aa),(B,bb)]但 parser 期望每个a和b是独立 token如[(A,a),(A,a),(B,b),(B,b)]以便S → a S b逐个匹配。解决lexer 输出必须与文法终结符对齐。若文法终结符是a和b单字符lexer 就不能合并若文法终结符是A代表a和B代表b则 parser 规则必须适配S → A Z B。二者必须严格一致。检查你的 BNF 中a和b是终结符还是非终结符——通常小写字母为终结符大写为非终结符。5.5 现象validate_token_sequence拦截了abab但 parser 仍收到[(A,a),(B,b),(A,a),(B,b)]并崩溃原因预检函数validate_token_sequence未被 parser 调用或调用位置在parse()之后。解决在Parser.__init__后立即调用验证或在parse()开头def parse(self): validate_token_sequence(self.tokens) # 加在这里 self.ast self.parse_S() ...预检是 parser 的前置守卫不是可选插件。6. 进阶技巧用 AST 生成可执行代码把“简单前端”变成真·编译器起点6.1 从 AST 到 Python 代码让aabb编译成print(valid)前端不止于验证还可生成中间代码。我们为SNode添加codegen()方法输出等效 Pythonclass SNode(ASTNode): def __init__(self, children): super().__init__(S, children) def codegen(self): # S → a Z b 对应检查 a 数量 b 数量 # 此处简化假设输入已分词Z 的深度即 n z_depth self._count_z_depth(self.children[0]) return fassert {z_depth} {z_depth}, a^n b^n violated def _count_z_depth(self, node): if isinstance(node, ZNode): if not node.children: return 0 else: return 1 self._count_z_depth(node.children[0]) return 0调用ast.codegen()返回字符串assert 2 2, a^n b^n violated。这虽简陋但证明了AST 是代码生成的天然载体。真实编译器中codegen()会输出 LLVM IR 或汇编而此处输出 Python便于快速验证逻辑。6.2 错误定位增强把行号列号注入 token让报错精准到字符当前 lexer 无位置信息。修改tokenize返回(type, value, line, col)元组def tokenize_with_pos(input_str): tokens [] line, col 1, 0 i 0 while i len(input_str): c input_str[i] if c \n: line 1 col 0 elif c in ab: start i while i len(input_str) and input_str[i] c: i 1 tokens.append((c.upper(), input_str[start:i], line, col)) col (i - start) else: raise SyntaxError(fInvalid char {c} at {line}:{col}) i 1 return tokensmatch()方法中错误信息改为fExpected A at {token[2]}:{token[3]}。前端开发者最痛恨“错误在第 123 行但没说哪一列”而编译器前端的第一责任就是让错误可定位。6.3 性能对比表手写 vs 生成器何时该换工具场景手写 lexer/parser使用 ANTLR/Yacc推荐教学/理解原理✅ 清晰暴露状态转移、FIRST/FOLLOW❌ 黑盒难 debug必选手写快速原型JSON Schema 解析⚠️ 200 行内可完成✅ 10 分钟生成生成器生产环境 JS 引擎❌ 不现实✅ V8 实际用 hand-written parser生成器手写优化嵌入式设备 DSL✅ 内存占用低无依赖❌ 运行时开销大手写我的血泪经验永远先手写一个最小可行版本。我曾为某 IoT 设备写配置语言 parser先用 ANTLR 生成结果发现生成代码占 Flash 120KB而手写版仅 8KB。不是反对工具而是反对“没理解就依赖”。当你能手写a^n b^nparser再去看 TypeScript 的parseIfStatement源码那些nextToken()、skip()调用就不再是魔法。最后说一句这个“简单文法编译器前端”不是终点而是你拆开编译器黑匣子的第一把螺丝刀。它不帮你搞定main函数缺失那是链接器的事也不解决堆空间不足那是运行时的事但它让你看清——每一行代码是如何从字符变成结构再变成意义的。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

南华大学数据库原理实验报告:SQL Server建库到SELECT查询完整实战

南华大学数据库原理实验报告:SQL Server建库到SELECT查询完整实战

简介:这份南华大学《数据库原理》实验报告面向大数据技术等专业学生,用于课程实验的参考与复盘,帮助读者系统掌握数据库管理系统认识、SQL语言应用与数据库设计基础。报告围绕SQL Server Management Studio展开,涵盖数据库与数据表…

2026/10/2 21:40:48 阅读更多 →
成都无人机维修培训靠谱吗?看机构时别只听宣传

成都无人机维修培训靠谱吗?看机构时别只听宣传

成都无人机维修培训靠谱吗?”这是很多准备报名的学员都会问的问题。判断一家机构是否靠谱,不建议只看宣传话术,更应该看课程有没有明确的学习路径、实操深度、机构合作背景,以及是否存在过度承诺。 第一,看课程是不是讲…

2026/10/2 21:40:48 阅读更多 →
OmniAgent开发者指南:如何为Agent编写自定义工具与扩展,从Tool基类到Manifest插件

OmniAgent开发者指南:如何为Agent编写自定义工具与扩展,从Tool基类到Manifest插件

OmniAgent开发者指南:如何为Agent编写自定义工具与扩展,从Tool基类到Manifest插件 【免费下载链接】OmniAgent An agent capable of self-evolving and dynamically hardening security 项目地址: https://gitcode.com/gh_mirrors/om/OmniAgent O…

2026/10/2 21:39:47 阅读更多 →

最新新闻

基于Hadoop的学情分析系统设计与实现:从数仓分层到可视化

基于Hadoop的学情分析系统设计与实现:从数仓分层到可视化

1. 为什么选"学情分析"作为大数据毕业设计题目每年毕业季我都会收到不少私信,问的大多是"大数据方向的毕业设计到底做什么好"。我的答案一直很明确:别去碰那些纯业务管理系统,也别做那种跑个爬虫抓点数据画几张图就完事的…

2026/10/2 22:21:34 阅读更多 →
百考通一站式期刊论文智能生成平台

百考通一站式期刊论文智能生成平台

在学术研究领域,期刊论文的撰写是成果输出的关键环节,却也让众多科研工作者与学生倍感压力:选题迷茫、逻辑梳理困难、格式规范复杂、内容提炼耗时,严重拖慢了学术成果的发表节奏。百考通(https://www.baikaotongai.com…

2026/10/2 22:21:34 阅读更多 →
光伏MPPT五种算法Simulink仿真对比与工程实现详解

光伏MPPT五种算法Simulink仿真对比与工程实现详解

做光伏MPPT仿真,最让人头疼的一个点就是算法选型:你明明看到参考论文里电导增量法精度高、粒子群能追多峰,可真到自己搭Simulink模型的时候,要么稳态抖动大,要么收敛之后又震荡,要么光照突变直接丢掉最大功…

2026/10/2 22:21:34 阅读更多 →
Univer实现单元格锁定:在线表格可编辑区域控制全攻略

Univer实现单元格锁定:在线表格可编辑区域控制全攻略

最近在群里被问得最多的一个词是 Univer,尤其是“在线表格能不能做成可填写的表单:用户只能填某些格子,其余格子动不了”。这个需求我在实际项目里做过不止一次,而且每次做都要面对同一个问题:很多现成的在线表格组件&…

2026/10/2 22:21:34 阅读更多 →
老服务器网卡驱动排错:BCM5709/5716/5722选型与安装实战

老服务器网卡驱动排错:BCM5709/5716/5722选型与安装实战

简介:压缩包内含博通 BCM5709、BCM5716、BCM5722 三款网卡的完整驱动与配套工具,适用于服务器、工作站和企业级网络设备在操作系统下的驱动安装、更新与排错。BCM5709 千兆、BCM5716 双口千兆、BCM5722 双口万兆控制器均覆盖其中,并附带驱动说…

2026/10/2 22:21:34 阅读更多 →
深度学习人脸表情识别系统:从模型训练到答辩完整实践指南

深度学习人脸表情识别系统:从模型训练到答辩完整实践指南

简介:这是一套面向计算机相关专业毕业设计的人脸表情识别系统完整项目,基于深度学习框架实现,涵盖模型训练、实时摄像头识别与图形界面交互等功能模块。资源定位清晰、难度适中,适合需要完成Python毕业设计、课程项目或入门计算机…

2026/10/2 22:20:34 阅读更多 →

日新闻

从零搭建AI工程化:模型之外的完整闭环

从零搭建AI工程化:模型之外的完整闭环

先搞清楚一件事:从零开始做 AI 工程化,难的从来不是调模型、写提示词,而是把一套原型 Demo 变成长得像是“正经系统”的东西。你手里可能已经有了能跑通的代码,也可能刚读完一些概念,但真到了要把它变成可维护、可观测…

2026/10/2 0:00:20 阅读更多 →
大模型训练显存估计与混合精度训练实战指南

大模型训练显存估计与混合精度训练实战指南

1. 大模型训练显存估计与混合精度训练详解显存不够用,几乎是每个做大模型训练的人都会撞上的第一堵墙。你可能也经历过:模型代码写完了,数据管道跑通了,满心欢喜地按下训练启动脚本,结果几秒钟后终端弹出一行红字——C…

2026/10/2 0:00:20 阅读更多 →
小样本学习数据集选型指南:27个真正可用的高质量数据集

小样本学习数据集选型指南:27个真正可用的高质量数据集

1. 小样本学习的“弹药库”:为什么你总在找数据集,却总找不到真正能用的? 小样本、数据集——这两个词最近半年在我处理的200多个AI项目咨询里,出现频率排进前三。不是模型调不好,不是代码写不对,而是卡在…

2026/10/2 0:00:20 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/10/1 19:40:48 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/10/1 19:41:40 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/10/1 20:05:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 10:36:31 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 5:26:06 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 6:09:11 阅读更多 →