最近心血来潮,就来学学写语言。
大家可能觉得写一个语言很难,但是其实,怎么说呢,其实理解理解一下会发现没有想象中的难懂。我这里就用javascript 示范写一个lisp语言。
为什么是lisp呢?因为它语法简单,整个语言语言就是围绕()。
(* 3 (+ 1 2))
今天就先看看语言的编译过程。
一个语言从文字到被电脑运行的过程,分为:
1. 扫描 (scanning):就是把文字读入内存,这个很简单,相信大家都会。用linux bash就是
file=$(<code.lisp)
node index.js $file
2. 解析 (parsing):把内存的文字逐一拆解分析,比如
const parser = line => {
return line
.replace(/\(/g, ' ( ')
.replace(/\)/g, ' ) ')
.split(/\s+/g)
.filter(x => x)
}
这样以来就能把
(* 3 (+ 1 2))
分解成
['(', '*', '3', '(', '+', '1', '2',')',')']
当然还不止那么简单,我们这里就要定义我们的语言原件是什么
我们这个简单lisp的话就是
1. 左括弧是开始
2. 右括弧是结束
3. 数字就是号码
4. 不是数字的都是符号
就这。
所以我们写一写这个的处理函数就是这样的
const isNumeric = str => {
if (typeof str != 'string') return false
return !isNaN(str) &&
!isNaN(parseFloat(str))
}
const atomize = (token) => {
if (isNumeric(token)) {
return +token
} else {
return Symbol.for(token)
}
}
这里用了Javascript里es6的Symbol是为了方便,它就是符号的意思,拿来代表别的东西。
然后接下来就是括弧的处理
const LEFT_BRACE = '('
const RIGHT_BRACE = ')'
const reader = tokens => {
if (tokens.length === 0) {
throw new SyntaxError('Unexpected EOF')
}
let token = tokens.shift()
if (token === LEFT_BRACE) {
let context = []
while (tokens[0] !== RIGHT_BRACE && tokens.length > 0) {
context.push(reader(tokens))
}
tokens.shift()
return context
} else if (token === RIGHT_BRACE) {
throw new SyntaxError('Unexpected )')
} else {
return atomize(token)
}
}
就是那么简单,这样的话还能处理嵌套。
结果就是一个
[ Symbol(*), 3, [ Symbol(+), 1, 2 ] ]
就是在代码内有了一个语言的真实模型。
先这里停一停休息一下。
如果点赞超过50个我就继续下一章吧!