C++語法解析器

架構分析

  1. Node進行文件數據處理javascript

  2. Vue.js進行視圖的呈現java

  3. Electron做爲客戶端數組

C++語法解析

效果展現

cppAnalysis

讀取形式

按照代碼行進行讀取, 每次讀取到一行就進行遍歷分析架構

關鍵字與特殊符號創建映射

/**
 * 關鍵字
 */
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'">
              &nbsp
            </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部分的代碼進行優化, 質量太低, 僅供參考

相關文章
相關標籤/搜索