原生JS实现代码高亮功能

实现步骤

  • 分析如何实现该功能
  • 了解词法结构
  • Javascript的产生式
  • 少废话,上代码

分析如何实现该功能

  平时我们在使用一些代码编辑器或者Markdown时很好奇它的代码高亮是如何
实现的。其实原理也挺简单的,就是区分代码内容的不同token并加以颜色标识。
我们将以js规则为例来说明这一过程。在对js代码的编译处理中其实只要第一个过程词法法分析即可实现基本的代码
高亮, 这个过程被称为lexer或者tokenizer。
下面介绍一下词法语法结构。

了解词法结构

如何定义一门语言? 想要分析一门语言,必须要从它的结构入手提取出一般化的东西。比如说中文的句式(|是或者的关系):句子->主语 谓语 宾语主语->代词|名词|短语代词->你|我|他我们可以通过迭代的方式来描述该语言。计算机语言也是语言,可以用同样的方式来描述它,而且比自然语言更加的严格。计算机中定义该结构的式子就叫产生式,产生式是由终结符、非终结符、以及关系组成。终结符是无法往下扩展的符号,比如说代词中的“你”,“我”,“他”无法往下划分,非终结符由终结符、非终结符组成。产生式的写法:由巴克斯-若尔提出来的BNF(backs-Naur Form)产生式写法比较流行。上面的中文结构可以写成:<中文>::=<句子>|<中文><句子><句子>::=<主语><谓语><宾语>|<主语><谓语><主语>::=<代词>|<名词>|<名词性短语><代词>::="你"|"我"|"他"还有扩展后的EBNF:中文::={句子}  //{}表示可以重复多次句子::=主语 谓语 [宾语]    //方括号0-1个主语::=代词 | 名词 | 名词性短语代词::="你"|"我"|"他"了解了产生式的写法,再来了解形式化语言的类型。所有的计算机语言都属于形式化语言,根据其表达能力,划分了乔姆斯基谱系,从0-3规则越来越严格。他们也是从属关系,即3型必从属于2型,以此类推。
(下方产生式的?即可指代终结符亦可指代非终结符)
1.0-型文法(无限制文法或短语结构文法)所有的计算机语言都属于此范畴,计算机
语 言必须是图灵完备的,也就是说该语言能完整编写出一个图灵机实现所有功能。
产生式?::=?
2.1-型文法(上下文相关文法)自然语言文法  产生式<上文><A><下文>::=<上文><B><下文>
3.2-型文法生成上下文无关语言 为大多数编程语言提供理论基础 产生式<A>::=?4.3-型 正规语言 表达能力等同有限状态自动机产生式<A>::=<A>?

Javascript的产生式

资料参考来自
链接: acma262 standrad

   我们只要了解当中的Lexical Grammar部分就能完成token解析了,其它语言也相通。但是我们需要精简一下,毕竟对于新的es6语法,以及完全还原JavaScript的解析代码量很大,也要做许多复杂处理,导致主题的思路反而不明确,所以简化下它的Lexical产生式如下:
InputElement ::= WhiteSpace | LineTerminator | Comment | Token
WhiteSpace ::= " " | “ ” (包括Unicode的所有空格类,Effe零宽空格)
LineTerminator::= "\n" | "\r"
Comment::=SignleLineComment | MultilineComment
SignleLineComment::="/" "/" <any>*
MultilineComment::="/" "*" ( [^*]|"*"[^/])* "*" "/"
Token::= Literal | keywords | Identifier | Punctuator
Literal::=NumberLiteral | BooleanLiteral | StringLiteral | NullLiteral
keywords::="if" | "else" | "for" | "function" ......
Punctuator::= "+" | "-" | "*" | "/" | "{" | "}" | ......
我们需要辨识出注释,运算符,数字,变量,关键字这些即可,运用正则的
强大能力对字符串处理。

少废话,上代码

ok,先使用一个文件来管理以及配置要用的正则,通过命名分组方式来分辨不同的token类型。代码如下:

//文件名regExpCompiler.js
var jsLexer = jsLexer || (function(scope){let keywords = /(?:const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|of|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static)(?![a-zA-Z\d\$_\u00a1-\uffff])/;let xregexp = {InputElement:"<WhiteSpace>|<LineTerminator>|<Comments>|<Token>",WhiteSpace:/ /,//空格符//行终结符LineTerminator:/\n/,//匹配注释Comments:/\/\*(?:[^*]|\*[^\/])*\*\/|\/\/[^\n]*/,Token:"<Literal>|<Keywords>|<Constructors>|<Identifier>|<Punctuator>",Literal:"<NumberLiteral>|<BooleanLiteral>|<StringLiteral>|<NullLiteral>",//数字类型NumberLiteral:/(?:[1-9][0-9]*|0)(?:\.[0-9]*)?|\.[0-9]+/,//布尔类型BooleanLiteral:/true|false/,//匹配字符串、字符串模板、正则StringLiteral:/\"(?:[^"\n]|\\[\s\S])*\"|\'(?:[^'\n]|\\[\s\S])*\'|\`(?:[^'\n]|\\[\s\S])*\`|\/[^\/].*\//,NullLiteral:/null/,Keywords:keywords,//Global的构造函数Constructors:/Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|SyntaxError|TypeError|URIError|JSON|Math/,//变量标识符Identifier:/[a-zA-Z\$_\u00a1-\uffff][a-zA-Z\d\$_\u00a1-\uffff]*/,//运算符Punctuator:/\+|\-|\*|\\|\{|\}|\<|\>|\+\+|\-\-|\=|\(|\)|;|\,|\[|\]|\./,};function compileRegExp(xregexp,name){//替换当中的字符串最后变成终结符中的正则if(xregexp[name] instanceof RegExp) return  `(?<${name}>${xregexp[name].source})`;let regexp = xregexp[name].replace(/\<([^>]+)\>/g,function(str,$1){return compileRegExp(xregexp,$1);});return regexp;}let  compiledRegExp = compileRegExp(xregexp,"InputElement");let  lexerRegExp = new RegExp(compiledRegExp,"g");return lexerRegExp;
})(typeof window !== "undefined" && window || this);if(typeof module !== "undefined" && module.exports){module.exports = jsLexer;
}
然后写个函数处理html过来的字符串以及执行正则。
//lexer.js
function escapehtml(str) {console.log(str);var arrEntities={'lt':'<','gt':'>','nbsp':' ','amp':'&','quot':'"'};return str.replace(/&(lt|gt|nbsp|amp|quot);/ig,function(all,t){return arrEntities[t];});
}
function scan(htmlText,callback){let str = escapehtml(htmlText);console.log(str,jsLexer);jsLexer.lastIndex = 0;while(jsLexer.lastIndex < str.length){let r = jsLexer.exec(str);callback(r);// fs.appendFileSync('./data.txt',JSON.stringify(r)+"\n");if(!r[0].length)break;}
}

最后主要的html,带样例:

<html>
<head><style>.code-area{padding: 4px;margin: 0 auto;width: 650px;height: auto;min-height: 96px;background: #000;border: 1px solid #ccc;color: #fff;}.code-area span{white-space: nowrap;}.keywords{color: #c678dd;}.identifier{color:#61aeee;}.white-space{display: inline-block;width: .5em;}.string-literal{color: #d19a66;}.punctuator{color: #fff;}.Constructors{color: #98c379;}.Comments{color:#5c6370;}.number-literal{color: #98c379;}</style>
</head>
<body>
<div class="code-area" id="textEl">
function show(){//render boardlet board=document.getElementById("board");board.innerHTML="";for(let i=0;i&lt;pattern.length;i++){for(let j=0;j&lt;pattern[i].length;j++){let cell=document.createElement("div");cell.classList.add("cell");cell.innerText=["","O","X"][pattern[i][j]];cell.addEventListener("click",()=&gt;{move(i,j)})board.appendChild(cell);}board.appendChild(document.createElement("br"))}
}
/*
* @param xregexp {string|regexp} 输入的要解析的自定义正则
* @param name {string} 在集合中查找的子串
*/
function compileRegExp(xregexp,name){//编译正则if(xregexp[name] instanceof RegExp) return  `(?<${name}>${xregexp[name].source})`;let regexp = xregexp[name].replace(/\<([^>]+)\>/g,function(str,$1){return compileRegExp(xregexp,$1);});return regexp;
}
let  compiledRegExp = compileRegExp(xregexp,"InputElement");
let  lexerRegExp = new RegExp(compiledRegExp,"g");
return lexerRegExp;
</div><script src="./regExpCompiler.js"></script><script src="./lexer.js"></script><script>//just identify token in codetext and high light them.var elem = document.getElementById("textEl");function makeTag(className,text){let tagSpan = document.createElement("span");tagSpan.className = className;tagSpan.innerText = text;return tagSpan;}let htmlText = elem.innerHTML.trim();let fragement = document.createDocumentFragment();scan(htmlText,(r)=>{//将捕获的命名分组替换为带颜色的span标签let tag;console.log(r)const {WhiteSpace,Comments,Keywords,Constructors,Identifier,LineTerminator,Punctuator,NumberLiteral,StringLiteral} =  r.groups;switch(true){case WhiteSpace!== undefined:tag=makeTag("white-space",WhiteSpace);break;case Comments!== undefined:tag=makeTag("Comments",Comments);break;case Keywords!== undefined:tag=makeTag("keywords",Keywords);break;case Constructors!== undefined:tag=makeTag("Constructors",Constructors);break;case Identifier!== undefined:tag=makeTag("identifier",Identifier);break;case LineTerminator!== undefined:tag=makeTag("line-terminator",LineTerminator);break;case Punctuator!== undefined:tag=makeTag("punctuator",Punctuator);break;case NumberLiteral!== undefined:tag=makeTag("number-literal",NumberLiteral);break;case StringLiteral!== undefined:tag=makeTag("string-literal",StringLiteral);break;}fragement.append(tag);});elem.innerHTML = '';elem.append(fragement);</script>
</body>
</html>

完成后的效果图如下:
在这里插入图片描述
如要实现函数声明和调用颜色改变需要外加正则,但是对于字符串模板中 的${}却无能为力,因为其中包的是表达式Expression需要上下文判断超出了正则表达式的能力,只能在词法分析阶段细化。鄙人才疏学浅,如果有谬误还请大佬们指点指点。
最后,学海无涯时刻保持谦虚学习的心态。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/24513.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从六个维度来分析:代码、无代码、低代码、AI提示代码、AI低代码

IT行业最不缺少概念&#xff0c;再多几个也无妨&#xff0c;反正大部分的概念大部分人都不会真正弄懂。AI低代码是我们新创的&#xff0c;AIGC低代码、AI低代码、智能开发、AI生成式开发、AIGS(AI生成软件)等等&#xff0c;这些概念已经呼之欲出了&#xff0c;不过还是觉得AI低…

Transformer:一种图灵完备的神经网络

点击下方卡片&#xff0c;关注“CVer”公众号 AI/CV重磅干货&#xff0c;第一时间送达 点击进入—>【Transformer】微信技术交流群 作者&#xff1a;张晨珩&#xff08;北京大学23级博士生&#xff09;已授权 https://zhuanlan.zhihu.com/p/611257510 论文: Attention is Tu…

什么是元宇宙?元宇宙在 2023 年将走向何方

2023 年&#xff0c;元宇宙是当今技术领域的热门话题。除了新兴的人工智能和物联网技术&#xff0c;元宇宙服务提供商也在争先恐后地进行创新&#xff0c;以提供企业和消费者解决方案。 元宇宙曾经是技术先驱和数据科学家的专属&#xff0c;现在正在扩大到影响每个人。这种环…

Midjourney|文心一格prompt教程[技巧篇]:生成多样性、增加艺术风格、图片二次修改、渐进优化、权重、灯光设置等17个技巧等你来学

Midjourney|文心一格prompt教程[技巧篇]&#xff1a;生成多样性、增加艺术风格、图片二次修改、渐进优化、权重、灯光设置等17个技巧等你来学 1.技巧一&#xff1a;临摹 我认为学习图片类的 prompt&#xff0c;跟学习画画是类似的&#xff0c;最好的学习方法不是直接用模板。…

新旧iphone短信转移,苹果旧手机短信导入新手机

短信携带重要信息内容&#xff0c;新旧iphone短信转移&#xff1f;您可能知道&#xff0c;iOS设备上不支持导出iPhone简讯&#xff0c;更不用说打印iPhone上的短信了。幸运的是&#xff0c;有一些可行的方法可以将iPhone短信导入到另一个iPhone&#xff0c;继续阅读以获得更多帮…

苹果手机怎么发语音短信?

说到语音&#xff0c;大家最熟悉的就是用微信发语音了&#xff0c;但是微信发语音的前提是必须是好友&#xff0c; 对于企业来说&#xff0c;使用范围还是受限&#xff0c;其实比微信语音应用范围广的就是语音短信&#xff0c;通过语音通知的新式&#xff0c;只要用户手机能正常…

iPhone苹果手机短信如何批量删除苹果iPhone手机短信?

iPhone苹果手机短信如何批量删除苹果iPhone手机短信&#xff1f; 1、iPhone苹果手机短信较多&#xff0c;如何才能快捷的批量删除苹果iPhone手机短信。 2、打开苹果iPhone手机设置&#xff1b; 3、在iPhone苹果手机设置内找到通用并点击进入&#xff1b; 4、在苹果iPhone手机设…

苹果手机短信如何转入Android手机,苹果手机怎么将短信备份导入到安卓手机?...

iPhone手机的短信无法直接导入安卓手机&#xff0c;在将苹果手机换为安卓手机时&#xff0c;短信往往无法迁移&#xff0c;这让我们很苦恼。小编试了QQ同步助手&#xff0c;百度网盘等同步类软件&#xff0c;往往只能备份通讯录&#xff0c;而无法备份短信。本文将介绍怎么样通…

小智AI教你制造业中如何应用ChatGPT实现智能化生产

制造业是现代社会经济发展的关键行业之一。然而&#xff0c;在制造业的生产过程中&#xff0c;存在着许多的瓶颈和问题&#xff0c;比如人力资源不足、生产线效率低下、生产成本高昂等等。这些问题导致制造业在生产效率、生产质量等方面面临着诸多挑战&#xff0c;因此&#xf…

ChatGPT Creator 刚刚启动了一个 AI 检测器,我们最终能否检测到 AI 编写的内容?

在过去的几个月里,我们看到许多工具都在尝试检测 AI 编写的文本。 然而,就在昨天,ChatGPT 背后的公司 OpenAI 推出了自己的文本分类器,旨在区分人工智能编写的文本和人类编写的文本。这是一个有一些限制的免费工具,但它仍然可以帮助您检测某些内容是否由 AI 编写。 我已…

Meta带头甩卖 VR头显打起价格战

新春三月&#xff0c;准备入手VR头显的“等等党”终于迎来降价利好。以Meta为首的一众VR厂商们纷纷打折&#xff0c;无论是为了清理库存、回收成本还是让步硬件新品&#xff0c;普通消费者都喜闻乐见。 上周五&#xff0c;Meta 率先官宣Meta Quest Pro与Meta Quest 2 的256GB版…

【青少年编程】【三级】打气球游戏

「青少年编程竞赛交流群」已成立&#xff08;适合6至18周岁的青少年&#xff09;&#xff0c;公众号后台回复【Scratch】或【Python】&#xff0c;即可进入。如果加入了之前的社群不需要重复加入。 微信后台回复“资料下载”可获取以往学习的材料&#xff08;视频、代码、文档&…

android 儿童 游戏,7-10岁儿童游戏大全

亲子游戏是亲子之间交往的重要形式。目的是培养小孩的认知和自理能力。最好的亲子教育,莫过于和宝宝一起玩丰富多彩的亲子游戏了。亲子游戏不仅让宝宝能玩得高兴,也能拉近你和宝宝的距离&#xff0c;何乐而不为&#xff1f; 7-10岁儿童游戏大全top1&#xff1a;春夏秋冬 春夏秋…

小朋友做游戏

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 题目描述 牛牛是一个幼儿园老师&#xff0c;他经常带小朋友们一起做游戏。 现在&#xff0c;牛牛的班里有AAA个安静的小朋友和BBB个闹腾的小朋友&#xff0c;牛牛想要从中选出恰好nnn个人来做…

益智app游戏 android,儿童宝宝益智游戏

儿童宝宝益智游戏app是一款游戏型的幼儿启蒙教育软件&#xff0c;宝宝们在这里可以通过玩游戏的方式学习到各种基础知识&#xff0c;很好地激发他们的学习兴趣。详细内容请感兴趣的朋友前来西西下载体验&#xff01; 应用简介 儿童宝宝系列教育应用是根据教育部新颁布的《3-6岁…

推荐几款适合孩子玩的编程游戏

在上一篇文章《孩子喜欢玩iPad等电子产品怎么办》中&#xff0c;我们鼓励家长合理控制孩子的屏幕时间&#xff0c;与此同时&#xff0c;为孩子挑选合适的电子消费内容同样重要。 今天就给大家推荐几款可以鼓励孩子玩的游戏&#xff0c;让孩子们在玩游戏的过程中学习编程。 1. k…

ChatGPT4已经来了,30秒做一个弹球游戏!

前两周写了关于ChatGPT的文章&#xff0c; 折腾了一晚&#xff01;终于开通了ChatGPT plus版本&#xff01; ChatGPT_Plus的功能有多强&#xff01;3分钟写一个贪吃蛇游戏&#xff01; 然后果断的注册了Plus, 事实证明这个决定是对的&#xff0c;现在只有plus 可以抢先尝鲜GPT4…

TwinCAT3中授权码激活操作的详细步骤和注意事项

前言 倍福的PLC在购买以后通常需要根据自己的需求购买对应的软件授权模块&#xff0c;这样就需要自行进行软件激活操作&#xff0c;本文详细阐述了PLC激活软件模块的操作步骤和注意事项&#xff0c;以供工程师参考。 步骤一 License ID和所购买的对应授权软件模块必须同时提…

chatgpt赋能python:Python中4.5/2:浮点数除法的谬误

Python中4.5/2&#xff1a;浮点数除法的谬误 在Python中&#xff0c;当我们尝试对两个整数进行除法运算时&#xff0c;通常可以得到预期的正确结果。但是&#xff0c;当我们的被除数或者除数是浮点数时&#xff0c;可能会遇到令人疑惑的结果。 例如&#xff0c;执行4.5/2的计…

文心一言的魔性作图,我愣住了

上一篇&#xff1a;985高校副教授晒年薪&#xff0c;公积金顶普通人月薪&#xff0c;网友&#xff1a;不愧是在上海&#xff01; 前几天收到文心一言的测试邀请&#xff0c;使用后感觉部分领域比如历史还是表现的可圈可点&#xff0c;具体可查看文心一言开箱测试。 今天看到网友…