Node進行文件數據處理javascript
Vue.js進行視圖的呈現java
Electron做爲客戶端數組
按照代碼行進行讀取, 每次讀取到一行就進行遍歷分析架構
/** * 關鍵字 */ let keyWords = [ 'asm', 'auto', 'bool', 'break', 'case', 'catch', 'char', 'class', 'const', 'continue', 'cin', 'cout', 'default', 'delete', 'do', 'double', 'define', 'else', 'enum', 'except', 'explicit', 'extern', 'endl', 'false', 'finally', 'float', 'for', 'friend', 'goto', 'if', 'inline', 'int', 'include', 'long', 'mutable', 'main', 'namespace', 'new', 'operator', 'private', 'protectde', 'public', 'printf', 'register', 'return', 'short', 'signed', 'szieof', 'static', 'struct', 'string', 'switch', 'std', 'scanf', 'template', 'this', 'throw', 'true', 'try', 'typedef', 'typename', 'union', 'unsigned', 'using', 'virtual', 'void', 'while' ] /** * 特殊字符 */ let specialWords = [ ',', ';', '(', ')', '{', '}', '#', '^', '?', ':', '.', '[', ']', '+', '-', '*', '/', '%', '=', '>', '<', '!', '~', '|', '&', '&&', '||', '==', '>=', '<=', '!=', '++', '--', '::', '<<', '>>', '+=', '-=', '*=', '/=', '%=', '&=', '^=', '->' ] keyWords.forEach(word => { wordsMap.set(word, 'keyWord') }) specialWords.forEach(word => { wordsMap.set(word, 'specialWord') })
代碼註釋匹配electron
當讀到一行中包含/*的兩個字符時候, 這時把代碼註釋的標誌設置爲true, 而後一直讀取,知道遇到*/的時候就把標誌從新置爲falseide
判斷是否能夠構成單詞,成立的條件是不以數字開頭,而且只包含數字, 下劃線, 以及字母, 能夠經過正則來/[a-z]|[A-z]|_/匹配函數
判斷是否爲字符串或者字符, 成立條件是以",'開頭工具
判斷是否爲數字優化
判斷是否爲特殊字符, 這時就經過創建的映射進行關鍵字查找ui
判斷空格
代碼解釋
判斷工具函數
//判斷是不是字母與下劃線 function judgeWord(word) { let wordPatten = /[a-z]|[A-z]|\_/ return wordPatten.test(word) } //判斷是否爲數字 function judgeNumber(number) { let numberPatten = /[0-9]/ return numberPatten.test(number) } //判斷是否爲特殊字符 function judgeSpecialWord(letter) { return wordsMap.get(letter) === 'specialWord' ? true : false } //判斷是否爲關鍵詞 function judgeKeyWord(letter) { return wordsMap.get(letter) === 'keyWord' ? true : false }
行分析函數
exports.analysisLine = (line, annotation) => { let oneLine = [] let word = '' let i = 0 let includeFile = false while (i < line.length) { //註釋代碼的優先級最高,須要先進行判斷 if (line[i] === '/' && line[i + 1] === '*') { word += line[i] i++ annotation = true; } if (!annotation) { //表示不是註釋內容 if (judgeWord(line[i])) { //表示是屬於字母與下劃線 word += line[i] while (i + 1 < line.length && (judgeNumber(line[i + 1]) || judgeWord(line[i + 1]))) { i++ word += line[i] } if (judgeKeyWord(word)) { //判斷單詞是否屬於關鍵詞 oneLine.push({ word: word, type: 'keyWord' }) } else { //不爲關鍵詞, 則爲標識符 oneLine.push({ word: word, type: 'identifier' }) } //因爲include是屬於頭文件的引入, 須要對頭文件進行特殊處理 if (word === 'include') { includeFile = true } word = '' i++ } else if (line[i] === '"') { //字符串判斷 while (i + 1 < line.length && line[i + 1] !== '"') { word += line[i] i++ } word += (line[i] || '' )+ (line[i + 1] || '') oneLine.push({ word: word, type: 'string' }) word = '' i += 2 } else if (line[i] === '\'') { //字符判斷 while (i + 1 < line.length && line[i + 1] !== '\'') { word += line[i] i++ } word += (line[i] || '' )+ (line[i + 1] || '') oneLine.push({ word: word, type: 'char' }) word = '' i += 2 } else if (judgeNumber(line[i])) { //數字判斷 word += line[i] while (i + 1 < line.length && (judgeNumber(line[i + 1]) || line[i + 1] === '.')) { i++ word += line[i] } oneLine.push({ word: word, type: 'number' }) word = '' i++ } else if (judgeSpecialWord(line[i])) { //特殊字符判斷 if (line[i] === '<' && includeFile) { //處理頭文件的引入 oneLine.push({ word: line[i], type: 'specialWord' }) i++ word += line[i] while (i + 1 < line.length && line[i + 1] !== '>') { i++ word += line[i] } oneLine.push({ word: word, type: 'libraryFile' }) word = '' i++ } else { //處理//的註釋代碼 if (line[i] === '/' && line[i + 1] === '/') { i++ while (i + 1 < line.length) { i++ word += line[i] } oneLine.push({ word: word, type: 'Annotations' }) word = '' i += 3 } else { word += line[i] while (i + 1 < line.length && (judgeSpecialWord(word + line[i + 1]))) { i++ word += line[i] } oneLine.push({ word: word, type: 'specialWord' }) word = '' i++ } } } else if (line[i] === ' ') { oneLine.push({ word: line[i], type: 'space' }) i++ } } else { //表示註釋內容 while (i + 1 < line.length && (line[i + 1] !== '*' && line[i + 2] !== '/')) { word += line[i] i++ } word += line[i] + (line[i + 1] || '') + (line[i + 2] || '') oneLine.push({ word: word, type: 'Annotations' }) annotation = false; word = '' i += 3 } } return oneLine }
Electron, Vue搭建
具體的搭建過程省略, 可查看源碼或自行查找資料
動態編輯顯示
數據綁定, 使用v-model結合watch進行數據監控, 當數據發生變化的時候從新分析每行數據
二維數組保存分析的數據, 使用二維數組保存分析完的詞法, 保留原生的代碼行
經過v-bind:class對不一樣類型的詞法進行代碼高亮的簡單呈現
<template> <div class="container"> <div class="navigator"> <button class="menu-btn" @click="open">open</button> <button class="menu-btn" @click="save">save</button> </div> <div class="content"> <div class="code-input"> <textarea v-model="codeInput"> </textarea> </div> <div class="code-show"> <div v-for="(line, index) in codeShowArr"> <p v-for="word in line" :class="word.type"> <template v-if="word.type==='space'">   </template> <template v-else> {{word.word}} </template> </p> </div> </div> </div> </div> </template> <script> import { analysisLine } from '../controllers/analysis' import fileController from '../controllers/fileController' export default { watch: { codeInput: function() { this.codeShowArr = [] this.codeInput.split(/\r?\n/).forEach(line => { let wordArr = [] analysisLine(line, this.annotation).forEach(word => { wordArr.push(word) }) this.codeShowArr.push(wordArr) }) } }, data () { return { codeInput: '', codeShow: '', codeShowArr: [], annotation: false } }, methods: { open() { fileController.openDialog().then(filePath => { return fileController.readPackage(filePath) }).then(res => { this.codeInput = res }) }, save() { fileController.openDialog().then(filePath => { return fileController.writePackage(filePath, this.codeShowArr) }) } } } </script>
文件的讀取和寫入
使用Electron中引入Node的fs模塊來進行文件讀取寫入處理
使用Electron的Dialog控件選取文件
import Promise from 'bluebird' import electron from 'electron' const {dialog} = electron.remote const fs = Promise.promisifyAll(require('fs')); const writePackage = (filePath, data) => { return fs.writeFileAsync(filePath, JSON.stringify(data, null, 2)).catch(err => { return Promise.reject(err) }); } const readPackage = (filePath) => { return fs.readFileAsync(filePath, 'utf-8').catch(err => { return Promise.reject(err) }); } const openDialog = () => { return new Promise((resolve, reject) => { dialog.showOpenDialog({ properties: [ 'openFile', ] }, (res) => { if(!res) { return reject('404') } return resolve(res[0]) }); }) } export default { writePackage, readPackage, openDialog }
對此, 一個簡單的C++詞法分析就完成了, 過程並不複雜, 只要理解了優先級和並列狀況的區分就比較清晰的寫出, 因爲我的匆匆寫完這個程序, 並無對js部分的代碼進行優化, 質量太低, 僅供參考