(Babel 原理?)手把手教你不到200行代码实现属于自己的超mini版编程语言

题目是有点标题党的嫌疑,但是看完这篇文章如果没有收获,评论区写 不低于 500 字 原因。(邪笑)

带着疑问去学习:

  • 各种各样的编程语言层出不穷,进而会产生许多带有不同后缀的 文件(.js,.jsx,.ts,.py,.go,.php,.java),这些文件的内容对程序员来说是逻辑的表达,但是对计算机来说,也就是带着不同后缀的二进制文件,那他们是如何变成机器读得懂的语言呢?
  • 如果我想要 实现一个属于自己后缀的 语言应该怎么实现呢?

编程语言是将字符串转换为各种机器代码输出的规则集。

简而言之,编程语言只是一组预定义的规则。有一定的编程范式,然后通过编译器/解释器 来转换为控制计算机运行的机器语言。而我们今天就是要利用js 来实现一个简单的解释器。

先看效果

新建文件 demo.fjk

内容为:

输出 "你好世界"
输出 "hahaha"

运行得出:

image-20220723164238813.png

核心代码仅仅 120 行

image-20220723164611741.png

这只是其中的一个思路,你完全可以结合webpack使用自己的后缀,使用自己的 loader 去解析和处理。

01. 使用nodejs获取编程文件,并读取 编程数据

这个对各位资深大佬那不是分分钟搞定?

//demo.fjk
输出 "你好世界"
输出 "hahaha"

// index.js

const Fjk = require('./Fjk')
const fs = require('fs')
const path = require('path')

const codes = fs.readFileSync(path.join(__dirname, './demo.fjk'), 'utf8').toString().replace(/\r/g, '/n')

const magenta = new Fjk(codes);

magenta.run();

大致解读一下,我们先读取到 demo.fjk的文件内容,然后替换其中的换行,然后把读取到的代码 交给 由类 Fjk创建的 实例 去 处理,执行 run 方法去执行demo.fjk程序。

02. 去实现这个Fjk 的类

  • 首先接受传递 过来的代码 constructor构造函数
  • 接下来去实现 run函数,去执行 这串代码
class Fjk {
    constructor(codes) {
        this.codes = codes;
    }
  run() {
    console.log(this.codes)
  }
}

到此结束。 哈哈,心里一慌吧? 真正的 实现才刚刚开始

03. 去实现一个简单的词法分析,把 输入的 代码 字符串 变为 可读的 有逻辑的数据结构(AST 语法树)

0 先提取常用的常量

const PrintStr = "输出";

const TokenTypes = {
    String: "string",
    Print: "print",
    Varchar: "varchar",
}

1. 简单分析整体思路

第一步 要创建一个 游标 记录下当前解析的位置

第二步 要创建一个 列表 记录下 解析后的数据

第三步 一个字 一个字的 去解析判断,去分析。

大体的代码结构如下:(文末附有 GitHub 地址,提供完整代码)

    // 词法解析
    tokenize() {
        const length = this.codes.length;
        // 记录当前 解析的 位置
        let pos = 0;
        let tokens = [];
        while (pos < length) {
            let currentChar = this.codes[pos];
        }
    
    }

2. 判断 currentChar 的类型

  • 如果是 空格 和 换行 直接跳过就行
  • 如果是 "那就是 字符串类型
  • 如果是 print 那就是 关键字

伪代码如下:

image-20220723170628774.png

3. 是空格 或者换行的情况 比较简单 一个 continue 直接跳过就行

4. 当currentChar 为 " 的时候,要取出 两个 "包裹的内容 作为字符串。

对当前的 pos 进行移动累加,一直 移动到当前 字符串 的内容 为 ",此时的res 记录的就是 "xxx"之中的 xxx内容,然后放入 tokens 里。

代码如下:

image-20220723171102191.png

5. 最后一个判断,判断是不是系统关键字

第一步还是对字符串进行提取,提取完毕以后 判断是不是 关键字 如果是关键字就存入tokens 里

代码如下:

image-20220723171314079.png

经过词法分析 处理以后的 数据结构如下:(这个是简单的版本的复杂版本的可以去GitHub 里看 v3 版本)

image-20220723174338452.png

04. 获取到token 以后 再次进行语法分析

怎么进行语法分析呢?简单来说 就是 一个完整的句子。

比如说:我学习js。就不能是 我js ,缺少了 关键字 就不能表述出想说的意思了。在者说就是:用js 写了赋值 语句,写成了 const = 123,从语法上就死掉了。话不多说 开搞。

逐个逐个的进行 token 解析

写一个parse 函数 接受 词法解析后的 tokens

  • 从第一个 tokens 开始解析
  • 如果是 输出关键词的token ,就需要 判断 下一个 token 是否是 字符串 这样才满足语法

代码如下

// 语法解析
    parse(tokens) {
        const len = tokens.length;
        let pos = 0;
        while (pos < len) {
            const currentToken = tokens[pos];
            // 如果是 PrintStr 关键字 也就是 `输出`
            if (currentToken.type === TokenTypes.Print && currentToken.value === PrintStr) {
                // 如果下一个 currentToken 不存在
                if (!tokens[pos + 1]) {
                    return console.log('当前行错误,期望的是字符串' + pos);
                }
                // 校验下一个 currentToken 是否是 字符串
                let isString = tokens[pos + 1].type === 'string';
                if (!isString) {
                    return console.log(`currentToken 解析错误 ${tokens[pos + 1].type},期望的是字符串`)
                }
                // 语法没有错误 输出
                console.log('\x1b[35m%s\x1b[0m', tokens[pos + 1].value);
                // pos 的位置增加2,2 代表的 是 PrintStr 一个 currentToken 字符串 一个 currentToken
                pos+= 2
            } else {
                return console.log(`未匹配的token ${currentToken.type}`)
            }
        }
    }

总结

这个mini的编程语言,虽然 语法简单,但是 这个思路是很重要的,把 代码的输入,词法解析 以及 语法解析都涉及到了。还有一个 v3 复杂版本的,增加了 变量的 相关 解析,大家可以自己试试实现。(也可以参考我的GitHub v3 版本)

输入如下:


print "hello world"
var world = "hahaha"
print world

输出如下:

hello world
hahaha

附上完整的 代码地址 :实现一个mini版编程语言

大家看完如果有疑问,欢迎加我 fjk1586237690 一起学习进步。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容