egg編程言語
代碼改自Eloquent Javascript: Chapter 12
egg編程言語遵照以下範例
- 言語只要表達式,沒有語句,如加法:+(1, 7),一連相加如+(1, +(2,3))
- 多個表達式用do包裹,如do(expr1, expr2, expr3, …, exprN)
- 數據範例支撐数字 布爾 字符串
代碼完成:
parse
- 將源碼字符串parse成ast。
do(foo(), 'done')
{
"type": "apply",
"operator": { "type": "word", "name": "do" },
"args": [
{
"type": "apply",
"operator": { "type": "word", "name": "foo" },
"args": []
},
{ "type": "value", "value": "done" }
]
}
一個正當的egg言語必定是一個表達式,所以剖析的最先可所以parseExpression函數,如剖析egg源碼do(expr)。剖析的源碼也可所以僅僅是一般的字符串”abc”,数字3.14或許僅僅是變量Identifer,這就須要推斷是不是是函數挪用,即剖析的表達式背面有無”(“。
所以我們先構建一個parse函數作為代碼的包裹:
// 全局program變量
var program = "";
function parse(_program) {
program = _program // 更新全局變量
var result = parseExpression();
// 源碼只能有一個表達式,假如剖析完了另有盈餘字符拋出非常。
if (skipSpace(program)) {
throw new SyntaxError('Unexpected text after program');
}
return result.expr;
}
// 過濾空缺字符
function skipSpace(string) {
var first = string.search(/\S/);
if(first === -1)
return '';
return string.slice(first);
}
// 剖析一個表達式 do(expr)
function parseExpression() {
program = skipSpace(program);
var expr;
if (/^["']/.test(program)) {
// 表達式是字符串
expr = { type: 'value', value: parseString() };
} else if (/^[\d.]/.test(program)) {
// 表達式是数字
expr = { type: 'value', value: parseNumber() };
} else if (/^[^\s(),"]/.test(program)) {
表達式是字母(變量名標識及true、false等)
expr = { type: 'word', name: parseWord() };
} else throw new SyntaxError('Unexpected syntax: ' + program);
/** 剖析完單個表達式do(),停止現在結果如 {type: 'word', name: 'do'},
* 還須要推斷是不是為函數挪用
*/
return parseApply(expr);
}
function parseApply(expr) {
program = skipSpace(program);
// 假如變量名后不是"(",就不是函數挪用,直接返回parseExpression返回的剖析后的表達式
if (program[0] != ')')
return {
expr: expr
}
// 假如是函數挪用
program = skipSpace(program.slice(1));
// expr須要被apply挪用情勢的expr包裹,原expr為operator
expr = {type: 'apply', operator: expr, args: []};
while (program[0] != ')') {
// 剖析參數
var arg = parseExpression();
expr.args.push(arg.expr);
program = skipSpace(program);
if (program[0] == ',')
program = program.slice(1);
else if (program[0] != ')')
throw new SyntaxError('Expected , or )');
}
program = program.substr(1);
return parseApply(expr); // 遞歸剖析 foo()()情勢的表達式;
}
// parseExpression 須要剖析三種範例的表達式
/** string */
function parseString() {
var startSign = program[0],
value = '',
at = 0,
ch;
while ((ch = program[++at])) {
if (ch == '\\') {
value += program[++at];
continue;
}
if (ch == startSign) {
program = program.substr(++at);
return value;
}
value += ch;
}
throw new SyntaxError('Parse String Error');
}
/** number */
function parseNumber() {
var at = 0,
ch,
value = '';
while ((ch = program[at]) && /[\d.]/.test(ch)) {
value += ch;
at++;
}
program = program.substr(at);
value = Number(value);
if (isNaN(value)) throw new SyntaxError('Parse Number Error');
return value;
}
/** word */
function parseWord() {
var at = 0,
ch,
value = '';
while ((ch = program[at]) && /[^\s(),"]/.test(ch)) {
value += ch;
at++;
}
program = program.substr(at);
return value;
}
evaluate
function evalute(expr, env) {
switch(expr.type) {
case 'value':
return expr.value;
case 'word':
if (expr.name in env)
return env[expr.name];
else
throw new ReferenceError('undefined variable: '
+ expr.name);
case 'apply':
if (
expr.operator.type == 'word' &&
expr.operator.name in specialForms
)
return specialForms[expr.operator.name](expr.args, env);
var op = evalute(expr.operator, env);
if (typeof op != 'function')
throw new TypeError('Applying a non-function');
return op.apply(null, expr.args.map(function(arg) {
return evalute(arg, env);
});
}
}
/** 原生函數 */
var specialForms = Object.create(null);
specialForms['if'] = function(args, env) {
if (args.length != 3) throw new SyntaxError('Bad number of args to if');
if (evaluate(args[0], env) !== false) return evaluate(args[1], env);
else return evaluate(args[2], env);
};
specialForms['while'] = function(args, env) {
if (args.length != 2)
throw new SyntaxError('Bad number of args to while');
while (evalute(args[0], env) !== false)
evalute(args[1], env);
return false;
}
specialForms['do'] = function(args, env) {
var value = false;
args.forEach(function(arg) {
value = evaluate(arg, env);
});
return value;
};
specialForms['define'] = function(args, env) {
if (args.length != 2 || args[0].type != 'word')
throw new SyntaxError('Bad use of define');
var value = evaluate(args[1], env);
env[args[0].name] = value;
return value;
}
// 自定義函數擴大
specialForms['fun'] = function(args, env) {
if (!args.length) throw new SyntaxError('Function need a body');
function name(expr) {
if (expr.type != 'word')
throw new SyntaxError('Arg names must be words');
return expr.name;
}
var argNames = args.slice(0, args.length - 1).map(name);
var body = args[args.length - 1];
return function() {
if (arguments.length != argNames.length)
throw new TypeError('Wrong number of arguments');
var localEnv = Object.create(env);
for (var i = 0; i < arguments.length; i++)
localEnv[argNames[i]] = arguments[i];
return evaluate(body, localEnv);
}
}
// 擴大原生操作符
var topEnv = Object.create(null);
topEnv['true'] = true;
topEnv['false'] = false;
['+', '-', '*', '/', '==', '<', '>'].forEach(function(op) {
topEnv[op] = new Function('a', 'b', 'return a' + op + 'b');
});
topEnv['print'] = function(value) {
console.log(value);
return value;
};
function run() {
var env = Object.create(topEnv);
var program = Array.prototype.slice.call(arguments, 0).join('\n');
return evaluate(parse(program), env);
}
斐波那契數列
#! /usr/bin/env node
const run = require('./egg').run;
run(
`
do(
define(foo, fun(
n,
if(<(n, 3), 1, +(foo(-(n, 1)), foo(-(n, 2))))
)),
print(foo(7))
)
`);
完全項目見 Github: https://github.com/lcfme/egg-…