实现步骤

  • 分析如何实现该功能
  • 了解词法结构
  • 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需要上下文判断超出了正则表达式的能力,只能在词法分析阶段细化。鄙人才疏学浅,如果有谬误还请大佬们指点指点。
最后,学海无涯时刻保持谦虚学习的心态。

原生JS实现代码高亮功能相关推荐

  1. highlight.js(代码高亮插件)

    highlight.js(代码高亮插件) 官网 用法查看 核心 API Highlight.js 将一些函数导出为hljs对象的方法 强调 highlight(code, {language, ign ...

  2. java循环购物车结算系统_原生JS实现购物车结算功能代码

    html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD ...

  3. 简易灯箱画廊设计html,原生Js实现的画廊功能

    原生Js实现画廊功能,点击图片,在下方出现相应放大图片.给a标签绑定onclick点击事件.这里上方的小图和下方将要展示大图,都是同一张图片,只是上下两个img的style中设置了不同的width和h ...

  4. 使用prismjs为网站添加代码高亮功能

    prismjs 是一款轻量.可扩展的代码语法高亮库,使用现代化的 Web 标准构建,使用 Prismjs 可以快速为网站添加代码高亮功能,支持超过113中编程语言,还支持多种插件,是简洁.高效的代码高 ...

  5. 原生js调用手机拍照功能

    原生js调用手机拍照功能 此功能主要用于混合App开发时手机拍照上传图片的功能! 话不多说直接上代码! 注:此代码在浏览器中运行时会报错,必须打开调试功能在手机上运行才可以! // html部分 // ...

  6. WebStorm 打开Node.js的代码提示功能

    Node.js coding assistance is disabled,Suggests configuring coding assistance for Node.js core module ...

  7. Vue中如何实现代码高亮功能?

    目录 1.下载依赖 2.在main.js文件中导入包 3.同样是在main.js文件中自定义一个指令 4.在vue文件中使用指令 5.效果展示 1.下载依赖 npm install highlight ...

  8. 怎样编写html购物车结算页面,原生JS实现购物车结算功能

    原生JS实现购物车结算功能 @charset "utf-8"; *{margin:0;padding:0;list-style-type:none;} a{color:#666;t ...

  9. 留言板代码 php js,原生JS实现留言板功能

    本文实例为大家分享了JS实现留言板功能的具体代码,供大家参考,具体内容如下 实现这个留言板功能比较简单,所以先上效果图: 实现用户留言内容,留言具体时间. window.onload = functi ...

最新文章

  1. 【android】java.lang.NoClassDefFoundError或classnotfount等异常错误
  2. pandas使用query函数基于dataframe字符串数据列中字符串的长度筛选dataframe的数据行(specific column string length)
  3. android 按下home键执行什么,Android下得到Home键按下的消息
  4. 自学python系列10:python的函数和函数式编程
  5. html登入弹框插件,基于jQuery的弹出框插件
  6. python读写json和txt
  7. 容器环境自建数据库、中间件一键接入阿里云 Prometheus 监控
  8. 【Elasticsearch】 es 排查问题 explain 使用 内容解释
  9. 基于BERT 的中文数据集下的命名实体识别(NER)
  10. ps去水印教程_叫板 PS!去水印、抠图、加滤镜,这款超强修图应用到底什么来头...
  11. polar函数--Matplotlib
  12. 017年美国大学生数学建模竞赛E题优秀论文解读
  13. de4dot 反混淆工具使用
  14. PDF怎么在线免费转换成WORD,亲测可用
  15. oa系统服务器什么意思,OA系统是什么意思
  16. 混合IP-SDN环境的仿真实验
  17. vue-购物车小球抛物线
  18. Advanced控制理论
  19. PostgreSQL 插入数据报错:column “xxx“ does not exist 解决方法
  20. android 仿qq 通讯录,Android端IM应用中的@人功能实现:仿微博、QQ、微信,零入侵、高可扩展[图文+源码]...

热门文章

  1. html清除文本框储存记录值,如何清除edge浏览器中已保存的表单数据
  2. How to remove FAP Filter with Galletto 1260
  3. sqlplus普通用户登录oracle,Oracle学习笔记:sqlplus用户登录
  4. Oracle账号登录
  5. chrome Android 65,Chrome谷歌浏览器稳定版
  6. 利用JAVA SOUND播放mp3,flac,ape格式音乐
  7. 用python做一个随机点名程序(不重复点名)
  8. 文献管理神器:Endnote
  9. c语言右移高位如何补1,c语言负数左移右移
  10. python计算三角形面积