简单解析样式文件并实现属性自动排序

前言

缘起于偶然间看见的同事写的一个CSS书写顺序

通常我们在写css,less,scss样式文件的时候,每次都会书写不同顺序,这样的代码不仅修改麻烦而且一点也不酷。但是css书写本身是一个随机性的开发,如果刻意去注意书写顺序会很累。 当这些杂乱无序的属性在书写时遵从一定规律后,改起来会很方便,会有一种,哲学上的美:)

那为啥要手动实现呢?

  1. 当时没有找到(后来发现是有插件的 ̄□ ̄||)
  2. 工程上的利器babel,webpack,react等都需要字符串解析的能力,为了感受一下词法分析,菜鸟也是有梦想的:)

词法分析

基本概念

在上文中我们提到 词法分析。词法分析是啥呢?

词法分析(英语:lexical analysis)是计算机科学中将字符序列转换为单词(Token)序列的过程。进行词法分析的程序或者函数叫作词法分析器(Lexical analyzer,简称Lexer),也叫扫描器(Scanner)。词法分析器一般以函数的形式存在,供语法分析器调用。 完成词法分析任务的程序称为词法分析程序或词法分析器或扫描器。

	var a = 1;

如上这一行代码,所谓的词法分析,我们就是把他们其中的每一个组成都拆成对应的类别。那么上面一行代码的组成就有如下的成员空格 + var+ a+ =+ 1+ ;
通常情况下组成代码串的成员可以有如下图所示的几种类别

我们为啥要进行词法分析呢?这是一个比较底层的操作,通常我们很多时候需要实现ast。而实现语法树的前置条件就是要拆分出 每一个单元才能进行词法分析,最终产出抽象语法树

抽象语法树(Abstract Syntax Tree,AST)或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。

示例:原始代码

#main {
    
    
    border: 1px solid black;
    background-color:red;
}

解析后的语法树

{
    
    
  "parentStyleSheet": null,
  "cssRules": [
    {
    
    
      "parentRule": null,
      "parentStyleSheet": "[Circular ~]",
      "selectorText": "#main",
      "style": {
    
    
        "0": "border",
        "1": "background-color",
        "length": 2,
        "parentRule": "[Circular ~.cssRules.0]",
        "_importants": {
    
    
          "border": "",
          "background-color": ""
        },
        "__starts": 113,
        "border": "1px solid black",
        "background-color": "red"
      },
      "__starts": 107,
      "__ends": 171
    }
  ]
}

水平有限,只做简单分享
关于词法分析和ast有兴趣可以去看

  • 一个贼好的项目 编译器demo https://github.com/jamiebuilds/the-super-tiny-compiler
  • LangSandbox(演示了如何创造一门语言) https://github.com/ftomassetti/LangSandbox

如何实现

词法分析器 Tokenizer 本质就是一个状态机。
关于状态机我们以Leetcode一道题目来切入理解,去手动做一下会有更深刻的感受
在这里插入图片描述
这个题目如果完全用if else来实现,那边界条件将会极其复杂
但是我们换个思路,所有这些变化可以总结按照下面这张表中的状态进行切换,将会让整个过程变的清晰。
怎么理解下面这个表 举个例子:
当我在存在符号这个状态下遇到数字整个状态机就切换到数字转换状态
当我在存在符号这个状态下遇到整个状态机就切换到结束状态
… 以此类推

+/- 数字 其他
开始 开始 存在符号 数字转换 结束
存在符号 结束 结束 数字转换 结束
数字转换 结束 结束 数字转换 结束
结束 结束 结束 结束 结束

附上一个我的题解,方便理解

/**
 * @param {string} str
 * @return {number}
 */
var myAtoi = function(str) {
    
    
    let map = [
        [0,1,2,3],
        [3,3,2,3],
        [3,3,2,3],
    ]
    let reg =/[0-9]/
    let r='';
    let state = 0;
    for(let i=0,l=str.length;i<l;i++){
    
    
        if(str[i]==' '){
    
    
            state = map[state][0];
        }else if(str[i]=='+'||str[i]=='-'){
    
    
            state = map[state][1];
        }else if(reg.test(str[i])){
    
    
            state = map[state][2];
        }else{
    
    
            state = map[state][3];
        }
        if(state==0){
    
    
            continue
        }
        if(state==1||state==2){
    
    
            r +=str[i]
        }
        if(state==3){
    
    
            break
        }
    }
    if(r==''||r=='-'||r=='+') return 0;
    let num = Number(r);
    if (num > 2147483647) {
    
    
        return 2147483647
    }
    if (num < -2147483648) {
    
    
        return -2147483648
    }
    return num
};

实现一个简易的词法分析器

回到正题,我们来分析一下我们需要的简单的词法分析器
不管是less 还是css 还是scss 它的语法结构大致上都如下图所示。因为我们的目标是仅仅是对css属性进行一定的规则排序,所以可以忽略掉大量的细节。

在遍历字符串的时候我们以{ ,}这两个作为标识符使用上面所提到的状态机先提取我们需要的部分
然后用状态机去识别 图中的几个不同的单元。进行策略上的排序就可以了。
排序的方式可以是设定一张map表,key对应属性,value表示所处位置,值小的排在最前面。排序方式可以选择使用快速排序

最终实现的效果

在这里插入图片描述
可以去vscode插件市场中搜索 compose-style安装体验

猜你喜欢

转载自blog.csdn.net/weixin_38616850/article/details/111630174