题目是有点题目党的嫌疑,然而看完这篇文章如果没有播种,评论区写 不低于 500 字 起因
。(邪笑)
带着疑难去学习:
- 各种各样的编程语言层出不穷,进而会产生许多带有不同后缀的 文件(.js,.jsx,.ts,.py,.go,.php,.java),这些文件的内容对程序员来说是逻辑的表白,然而对计算机来说,也就是带着不同后缀的二进制文件,那他们是如何变成机器读得懂的语言呢?
- 如果我想要 实现一个属于本人后缀的 语言应该怎么实现呢?
编程语言是将字符串转换为各种机器代码输入的规定集。
简而言之,编程语言只是一组预约义的规定。有肯定的编程范式,而后通过编译器 / 解释器 来转换为管制计算机运行的机器语言。而咱们明天就是要利用 js 来实现一个简略的解释器。
先看成果
新建文件 demo.fjk
内容为:
输入 "你好世界"
输入 "hahaha"
运行得出:
外围代码仅仅 120 行
这只是其中的一个思路,你齐全能够联合 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
那就是关键字
伪代码如下:
3. 是空格 或者换行的状况 比较简单 一个 continue 间接跳过就行
4. 当 currentChar 为 "
的时候,要取出 两个 "
包裹的内容 作为字符串。
对以后的 pos 进行挪动累加,始终 挪动到以后 字符串 的内容 为 "
,此时的 res 记录的就是 "xxx"
之中的 xxx 内容,而后放入 tokens 里。
代码如下:
5. 最初一个判断,判断是不是零碎关键字
第一步还是对字符串进行提取,提取结束当前 判断是不是 关键字
如果是关键字就存入 tokens 里
代码如下:
通过 词法剖析 解决当前的 数据结构如下:(这个是简略的版本的简单版本的能够去 GitHub 里看 v3 版本)
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
一起学习提高。