JavaScript系列(50)--编译器实现详解
JavaScript编译器实现详解 🔨
今天,让我们深入探讨JavaScript编译器的实现。编译器是一个将源代码转换为目标代码的复杂系统,通过理解其工作原理,我们可以更好地理解JavaScript的执行过程。
编译器基础概念 🌟
💡 小知识:编译器通常包括词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成等阶段。每个阶段都有其特定的任务和挑战。
词法分析器实现 📊
// 1. Token类型定义
const TokenType = {// 关键字FUNCTION: 'FUNCTION',RETURN: 'RETURN',IF: 'IF',ELSE: 'ELSE',// 标识符和字面量IDENTIFIER: 'IDENTIFIER',NUMBER: 'NUMBER',STRING: 'STRING',// 运算符PLUS: 'PLUS',MINUS: 'MINUS',MULTIPLY: 'MULTIPLY',DIVIDE: 'DIVIDE',// 分隔符LEFT_PAREN: 'LEFT_PAREN',RIGHT_PAREN: 'RIGHT_PAREN',LEFT_BRACE: 'LEFT_BRACE',RIGHT_BRACE: 'RIGHT_BRACE',SEMICOLON: 'SEMICOLON',// 其他EOF: 'EOF'
};// 2. Token类
class Token {constructor(type, value, line, column) {this.type = type;this.value = value;this.line = line;this.column = column;}
}// 3. 词法分析器
class Lexer {constructor(source) {this.source = source;this.tokens = [];this.start = 0;this.current = 0;this.line = 1;this.column = 1;}scanTokens() {while (!this.isAtEnd()) {this.start = this.current;this.scanToken();}this.tokens.push(new Token(TokenType.EOF, null,this.line, this.column));return this.tokens;}scanToken() {const c = this.advance();switch (c) {// 单字符tokencase '(': this.addToken(TokenType.LEFT_PAREN); break;case ')': this.addToken(TokenType.RIGHT_PAREN); break;case '{': this.addToken(TokenType.LEFT_BRACE); break;case '}': this.addToken(TokenType.RIGHT_BRACE); break;case ';': this.addToken(TokenType.SEMICOLON); break;// 运算符case '+': this.addToken(TokenType.PLUS); break;case '-': this.addToken(TokenType.MINUS); break;case '*': this.addToken(TokenType.MULTIPLY); break;case '/':if (this.match('/')) {// 单行注释while (this.peek() !== '\n' && !this.isAtEnd()) {this.advance();}} else {this.addToken(TokenType.DIVIDE);}break;// 忽略空白字符case ' ':case '\r':case '\t':break;case '\n':this.line++;this.column = 1;break;// 字符串case '"': this.string(); break;default:if (this.isDigit(c)) {this.number();} else if (this.isAlpha(c)) {this.identifier();} else {throw new Error(`Unexpected character: ${c} at line ${this.line}`);}break;}}// 辅助方法advance() {this.column++;return this.source.charAt(this.current++);}match(expected) {if (this.isAtEnd()) return false;if (this.source.charAt(this.current) !== expected) return false;this.current++;return true;}peek() {if (this.isAtEnd()) return '\0';return this.source.charAt(this.current);}isAtEnd() {return this.current >= this.source.length;}addToken(type, literal = null) {const text = this.source.substring(this.start, this.current);this.tokens.push(new Token(type, literal || text, this.line, this.column));}
}
语法分析器实现 🚀
// 1. AST节点类型
class ASTNode {constructor(type) {this.type = type;}
}// 2. 表达式节点
class BinaryExpr extends ASTNode {constructor(left, operator, right) {super('BinaryExpr');this.left = left;this.operator = operator;this.right = right;}
}class UnaryExpr extends ASTNode {constructor(operator, right) {super('UnaryExpr');this.operator = operator;this.right = right;}
}class LiteralExpr extends ASTNode {constructor(value) {super('LiteralExpr');this.value = value;}
}// 3. 语法分析器
class Parser {constructor(tokens) {this.tokens = tokens;this.current = 0;}parse() {try {return this.expression();} catch (error) {console.error('Parse error:', error);return null;}}expression() {return this.term();}term() {let expr = this.factor();while (this.match(TokenType.PLUS, TokenType.MINUS)) {const operator = this.previous();const right = this.factor();expr = new BinaryExpr(expr, operator, right);}return expr;}factor() {let expr = this.unary();while (this.match(TokenType.MULTIPLY, TokenType.DIVIDE)) {const operator = this.previous();const right = this.unary();expr = new BinaryExpr(expr, operator, right);}return expr;}unary() {if (this.match(TokenType.MINUS)) {const operator = this.previous();const right = this.unary();return new UnaryExpr(operator, right);}return this.primary();}primary() {if (this.match(TokenType.NUMBER)) {return new LiteralExpr(parseFloat(this.previous().value));}if (this.match(TokenType.LEFT_PAREN)) {const expr = this.expression();this.consume(TokenType.RIGHT_PAREN,"Expect ')' after expression.");return expr;}throw new Error('Expect expression.');}// 辅助方法match(...types) {for (const type of types) {if (this.check(type)) {this.advance();return true;}}return false;}check(type) {if (this.isAtEnd()) return false;return this.peek().type === type;}advance() {if (!this.isAtEnd()) this.current++;return this.previous();}isAtEnd() {return this.peek().type === TokenType.EOF;}peek() {return this.tokens[this.current];}previous() {return this.tokens[this.current - 1];}
}
代码生成器实现 💻
// 1. 代码生成器
class CodeGenerator {constructor() {this.output = '';this.indent = 0;}generate(ast) {return this.visitNode(ast);}visitNode(node) {switch (node.type) {case 'BinaryExpr':return this.generateBinaryExpr(node);case 'UnaryExpr':return this.generateUnaryExpr(node);case 'LiteralExpr':return this.generateLiteralExpr(node);default:throw new Error(`Unknown node type: ${node.type}`);}}generateBinaryExpr(node) {const left = this.visitNode(node.left);const right = this.visitNode(node.right);return `(${left} ${node.operator.value} ${right})`;}generateUnaryExpr(node) {const right = this.visitNode(node.right);return `(${node.operator.value}${right})`;}generateLiteralExpr(node) {return node.value.toString();}
}// 2. 优化器
class Optimizer {optimize(ast) {return this.visitNode(ast);}visitNode(node) {switch (node.type) {case 'BinaryExpr':return this.optimizeBinaryExpr(node);case 'UnaryExpr':return this.optimizeUnaryExpr(node);case 'LiteralExpr':return node;default:throw new Error(`Unknown node type: ${node.type}`);}}optimizeBinaryExpr(node) {const left = this.visitNode(node.left);const right = this.visitNode(node.right);// 常量折叠if (left.type === 'LiteralExpr' && right.type === 'LiteralExpr') {const result = this.evaluateConstExpr(left.value,node.operator.value,right.value);return new LiteralExpr(result);}return new BinaryExpr(left, node.operator, right);}evaluateConstExpr(left, operator, right) {switch (operator) {case '+': return left + right;case '-': return left - right;case '*': return left * right;case '/': return left / right;default:throw new Error(`Unknown operator: ${operator}`);}}
}// 3. 源码映射生成器
class SourceMapGenerator {constructor() {this.mappings = [];this.sources = [];this.names = [];}addMapping(generated, original, source, name) {this.mappings.push({generated,original,source,name});}generate() {return {version: 3,file: 'output.js',sourceRoot: '',sources: this.sources,names: this.names,mappings: this.encodeMappings()};}encodeMappings() {// 实现VLQ编码return this.mappings.map(mapping => {return [mapping.generated.line,mapping.generated.column,mapping.original.line,mapping.original.column].join(',');}).join(';');}
}
实际应用场景 💼
// 1. 简单计算器编译器
class CalculatorCompiler {constructor() {this.lexer = null;this.parser = null;this.generator = null;}compile(source) {// 词法分析this.lexer = new Lexer(source);const tokens = this.lexer.scanTokens();// 语法分析this.parser = new Parser(tokens);const ast = this.parser.parse();// 优化const optimizer = new Optimizer();const optimizedAst = optimizer.optimize(ast);// 代码生成this.generator = new CodeGenerator();return this.generator.generate(optimizedAst);}
}// 2. DSL编译器
class DSLCompiler {constructor(grammar) {this.grammar = grammar;this.lexer = null;this.parser = null;}compile(source) {// 根据语法规则生成词法分析器this.lexer = this.createLexer(source);const tokens = this.lexer.scanTokens();// 根据语法规则生成语法分析器this.parser = this.createParser(tokens);const ast = this.parser.parse();// 生成目标代码return this.generateCode(ast);}createLexer(source) {// 根据语法规则创建自定义词法分析器return new CustomLexer(source, this.grammar.tokens);}createParser(tokens) {// 根据语法规则创建自定义语法分析器return new CustomParser(tokens, this.grammar.rules);}generateCode(ast) {// 根据AST生成目标代码const generator = new CustomCodeGenerator(this.grammar.target);return generator.generate(ast);}
}// 3. 模板编译器
class TemplateCompiler {constructor() {this.cache = new Map();}compile(template) {if (this.cache.has(template)) {return this.cache.get(template);}const tokens = this.tokenize(template);const ast = this.parse(tokens);const code = this.generate(ast);const render = new Function('data', code);this.cache.set(template, render);return render;}tokenize(template) {const tokens = [];let current = 0;while (current < template.length) {if (template[current] === '{' && template[current + 1] === '{') {// 处理表达式current += 2;let expr = '';while (current < template.length && !(template[current] === '}' && template[current + 1] === '}')) {expr += template[current];current++;}tokens.push({type: 'expression',value: expr.trim()});current += 2;} else {// 处理文本let text = '';while (current < template.length && !(template[current] === '{' && template[current + 1] === '{')) {text += template[current];current++;}tokens.push({type: 'text',value: text});}}return tokens;}
}
性能优化技巧 ⚡
// 1. 缓存优化
class CompilerCache {constructor() {this.tokenCache = new Map();this.astCache = new Map();this.codeCache = new Map();}getTokens(source) {const hash = this.hashSource(source);if (this.tokenCache.has(hash)) {return this.tokenCache.get(hash);}const tokens = new Lexer(source).scanTokens();this.tokenCache.set(hash, tokens);return tokens;}getAST(tokens) {const hash = this.hashTokens(tokens);if (this.astCache.has(hash)) {return this.astCache.get(hash);}const ast = new Parser(tokens).parse();this.astCache.set(hash, ast);return ast;}getCode(ast) {const hash = this.hashAST(ast);if (this.codeCache.has(hash)) {return this.codeCache.get(hash);}const code = new CodeGenerator().generate(ast);this.codeCache.set(hash, code);return code;}hashSource(source) {// 实现源码哈希return source.length + source.slice(0, 100);}hashTokens(tokens) {// 实现tokens哈希return tokens.map(t => t.type + t.value).join('');}hashAST(ast) {// 实现AST哈希return JSON.stringify(ast);}
}// 2. 并行处理
class ParallelCompiler {constructor(workerCount = navigator.hardwareConcurrency) {this.workers = [];this.initWorkers(workerCount);}async initWorkers(count) {for (let i = 0; i < count; i++) {const worker = new Worker('compiler-worker.js');this.workers.push(worker);}}async compile(sources) {const chunks = this.splitSources(sources);const promises = chunks.map((chunk, index) => {return new Promise((resolve, reject) => {const worker = this.workers[index % this.workers.length];worker.onmessage = e => resolve(e.data);worker.onerror = reject;worker.postMessage({ type: 'compile', sources: chunk });});});const results = await Promise.all(promises);return this.mergeResults(results);}splitSources(sources) {// 将源码分割成多个块const chunkSize = Math.ceil(sources.length / this.workers.length);const chunks = [];for (let i = 0; i < sources.length; i += chunkSize) {chunks.push(sources.slice(i, i + chunkSize));}return chunks;}
}// 3. 增量编译
class IncrementalCompiler {constructor() {this.cache = new CompilerCache();this.dependencies = new Map();this.modifiedFiles = new Set();}markFileModified(file) {this.modifiedFiles.add(file);// 标记依赖文件const deps = this.dependencies.get(file) || new Set();for (const dep of deps) {this.markFileModified(dep);}}async compile(files) {const results = new Map();for (const file of files) {if (!this.modifiedFiles.has(file) && this.cache.has(file)) {results.set(file, this.cache.get(file));continue;}const result = await this.compileFile(file);results.set(file, result);this.cache.set(file, result);this.modifiedFiles.delete(file);}return results;}async compileFile(file) {const source = await this.readFile(file);const tokens = this.cache.getTokens(source);const ast = this.cache.getAST(tokens);// 收集依赖this.collectDependencies(file, ast);return this.cache.getCode(ast);}collectDependencies(file, ast) {const deps = new Set();this.traverseAST(ast, node => {if (node.type === 'Import') {deps.add(node.source);}});this.dependencies.set(file, deps);}
}
最佳实践建议 💡
- 错误处理和恢复
// 1. 错误收集器
class ErrorCollector {constructor() {this.errors = [];}addError(error) {this.errors.push({message: error.message,line: error.line,column: error.column,phase: error.phase});}hasErrors() {return this.errors.length > 0;}getErrors() {return this.errors;}clear() {this.errors = [];}
}// 2. 错误恢复策略
class ErrorRecovery {static recoverFromSyntaxError(parser) {// 跳过到下一个同步点while (!parser.isAtEnd()) {if (parser.match(TokenType.SEMICOLON)) return;if (parser.peek().type === TokenType.RIGHT_BRACE) return;parser.advance();}}
}// 3. 诊断信息生成
class DiagnosticReporter {constructor(source) {this.source = source;this.lines = source.split('\n');}report(error) {const line = this.lines[error.line - 1];const pointer = ' '.repeat(error.column - 1) + '^';return [`Error: ${error.message}`,` at line ${error.line}, column ${error.column}`,line,pointer,`Phase: ${error.phase}`].join('\n');}
}
结语 📝
JavaScript编译器的实现是一个复杂但有趣的主题。通过本文,我们学习了:
- 编译器的基本架构和工作原理
- 词法分析和语法分析的实现
- 代码生成和优化技术
- 性能优化策略
- 错误处理和最佳实践
💡 学习建议:在实现编译器时,要注意模块化设计和错误处理。合理使用缓存和优化策略,可以显著提升编译性能。同时,良好的错误提示对于开发者体验至关重要。
如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇
终身学习,共同成长。
咱们下一期见
💻
相关文章:
JavaScript系列(50)--编译器实现详解
JavaScript编译器实现详解 🔨 今天,让我们深入探讨JavaScript编译器的实现。编译器是一个将源代码转换为目标代码的复杂系统,通过理解其工作原理,我们可以更好地理解JavaScript的执行过程。 编译器基础概念 🌟 &…...
大数据相关职位 职业进阶路径
大数据相关职位 & 职业进阶路径 📌 大数据相关职位 & 职业进阶路径 大数据领域涵盖多个方向,包括数据工程、数据分析、数据治理、数据科学等,每个方向的进阶路径有所不同。以下是大数据相关职位的详细解析及其职业进阶关系。 &#…...
基础项目实战——学生管理系统(c++)
目录 前言一、功能菜单界面二、类与结构体的实现三、录入学生信息四、删除学生信息五、更改学生信息六、查找学生信息七、统计学生人数八、保存学生信息九、读取学生信息十、打印所有学生信息十一、退出系统十二、文件拆分结语 前言 这一期我们来一起学习我们在大学做过的课程…...
C++,STL,【目录篇】
文章目录 一、简介二、内容提纲第一部分:STL 概述第二部分:STL 容器第三部分:STL 迭代器第四部分:STL 算法第五部分:STL 函数对象第六部分:STL 高级主题第七部分:STL 实战应用 三、写作风格四、…...
【Rust自学】15.3. Deref trait Pt.2:隐式解引用转化与可变性
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 15.3.1. 函数和方法的隐式解引用转化(Deref Coercion) 隐式解引用转化(Deref Coercion)是为函数和方法提供的一种便捷特性。 它的原理是…...
密码强度验证代码解析:C语言实现与细节剖析
在日常的应用开发中,密码强度验证是保障用户账户安全的重要环节。今天,我们就来深入分析一段用C语言编写的密码强度验证代码,看看它是如何实现对密码强度的多维度检测的。 代码整体结构 这段C语言代码主要实现了对输入密码的一系列规则验证&a…...
arkts bridge使用示例
接上一篇:arkui-x跨平台与android java联合开发-CSDN博客 本篇讲前端arkui如何与后端其他平台进行数据交互,接上一篇,后端os平台为Android java。 arkui-x框架提供了一个独特的机制:bridge。 1、前端接口定义实现 定义一个bri…...
LINUX部署微服务项目步骤
项目简介技术栈 主体技术:SpringCloud,SpringBoot,VUE2, 中间件:RabbitMQ、Redis 创建用户 在linux服务器home下创建用户qshh,用于后续本项目需要的环境进行安装配置 #创建用户 useradd 用户名 #设置登录密…...
zsh安装插件
0 zsh不仅在外观上比较美观,而且其具有强大的插件,如果不使用那就亏大了。 官方插件库 https://github.com/ohmyzsh/ohmyzsh/wiki/Plugins 官方插件库并不一定有所有的插件,比如zsh-autosuggestions插件就不再列表里,下面演示zs…...
网站如何正式上线(运维详解)
因为平台原因,不能有太多链接,所以下文中链接都删除了,想访问的去原文链接:https://www.zhoudongqi.com/ TIPS 这篇文章是我自己运营运维的wordpess站点的经验总结,可以说十分详细,域名,服务器和…...
SQL server 数据库使用整理
标题:SQL server 数据库使用整理 1.字符串表名多次查询 2.读取SQL中Json字段中的值:JSON_VALUE(最新版本支持,属性名大小写敏感) 1.字符串表名多次查询 SELECT ROW_NUMBER() OVER (ORDER BY value ASC) rowid,value…...
【Rust自学】17.2. 使用trait对象来存储不同值的类型
喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 17.2.1. 需求 这篇文章以一个例子来介绍如何在Rust中使用trait对象来存储不同值的类型。 …...
初始化mysql报错cannot open shared object file: No such file or directory
报错展示 我在初始化msyql的时候报错:mysqld: error while loading shared libraries: libaio.so.1: cannot open shared object file: No such file or directory 解读: libaio包的作用是为了支持同步I/O。对于数据库之类的系统特别重要,因此…...
2025年1月22日(网络编程)
系统信息: ubuntu 16.04LTS Raspberry Pi Zero 2W 系统版本: 2024-10-22-raspios-bullseye-armhf Python 版本:Python 3.9.2 已安装 pip3 支持拍摄 1080p 30 (1092*1080), 720p 60 (1280*720), 60/90 (640*480) 已安装 vim 已安装 git 学习…...
Jason配置环境变量
jason官网 https://jason-lang.github.io/ https://github.com/jason-lang/jason/releases 步骤 安装 Java 21 或更高版本 安装 Visual Studio Code 根据操作系统,请按照以下具体步骤操作 视窗 下载 Jason 的最新版本,选择“jason-bin-3.3.0.zip”…...
蓝桥杯python语言基础(7)——自定义排序和二分查找
目录 一、自定义排序 (一)sorted (二)list.sort 二、二分查找 bisect 一、自定义排序 (一)sorted sorted() 函数会返回一个新的已排序列表,而列表的 sort() 方法会直接在原列表上进行排序…...
(开源)基于Django+Yolov8+Tensorflow的智能鸟类识别平台
1 项目简介(开源地址在文章结尾) 系统旨在为了帮助鸟类爱好者、学者、动物保护协会等群体更好的了解和保护鸟类动物。用户群体可以通过平台采集野外鸟类的保护动物照片和视频,甄别分类、实况分析鸟类保护动物,与全世界各地的用户&…...
后盾人JS--闭包明明白白
延伸函数环境生命周期 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> <…...
redis的分片集群模式
redis的分片集群模式 1 主从哨兵集群的问题和分片集群特点 主从哨兵集群可应对高并发写和高可用性,但是还有2个问题没有解决: (1)海量数据存储 (2)高并发写的问题 使用分片集群可解决,分片集群…...
Kiwi 安卓浏览器本月停止维护,扩展功能迁移至 Edge Canary
IT之家 1 月 25 日消息,科技媒体 Android Authority 今天(1 月 25 日)发布博文,报道称 Kiwi 安卓浏览器将于本月停止维护,相关扩展支持功能已整合到微软 Edge Canary 浏览器中。 开发者 Arnaud42 表示 Kiwi 安卓浏览器…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...
