用JS製造一個全新的編程言語

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-…

    原文作者:codeboy
    原文地址: https://segmentfault.com/a/1190000015385337
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞