Skip to content

Instantly share code, notes, and snippets.

@SoraYeh
Last active August 29, 2015 14:01
Show Gist options
  • Select an option

  • Save SoraYeh/f1f6c77f50f76c02fc19 to your computer and use it in GitHub Desktop.

Select an option

Save SoraYeh/f1f6c77f50f76c02fc19 to your computer and use it in GitHub Desktop.

Revisions

  1. SoraYeh revised this gist May 27, 2014. 1 changed file with 20 additions and 1 deletion.
    21 changes: 20 additions & 1 deletion toylang.js
    Original file line number Diff line number Diff line change
    @@ -130,6 +130,21 @@ specialForms["fun"] = function(args, env) {
    };
    };

    specialForms["set"] = function(args, env) {
    if (args.length != 2 || args[0].type != "word")
    throw new SyntaxError("bad use of set");

    var value = evaluate(args[1], env);
    while (env) {
    if (Object.prototype.hasOwnProperty.call(env, args[0].name)) {
    env[args[0].name] = value;
    return value;
    }
    env = Object.getPrototypeOf(env);
    }
    throw new ReferenceError("Undefined variable: " + args[0].name);
    }

    var topEnv = Object.create(null);

    topEnv["true"] = true;
    @@ -145,7 +160,11 @@ topEnv["print"] = function(value) {
    };

    topEnv["array"] = function(args) {
    return Array.prototype.slice.call(arguments, 0);
    var array = [];
    for (var i = arguments.length - 1; i >= 0; i--) {
    array.push(arguments[i]);
    };
    return array;
    };

    topEnv["length"] = function(array) {
  2. SoraYeh created this gist May 26, 2014.
    163 changes: 163 additions & 0 deletions toylang.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,163 @@
    function parseExpression(program) {
    program = skipSpace(program);
    var match, expr;
    if (match = /^"([^"]*)"/.exec(program))
    expr = {type: "value", value: match[1]};
    else if (match = /^\d+\b/.exec(program))
    expr = {type: "value", value: Number(match[0])};
    else if (match = /^[^\s(),"]+/.exec(program))
    expr = {type: "word", name: match[0]};
    else
    throw new SyntaxError("Unexpected syntax: " + program);

    return parseApply(expr, program.slice(match[0].length));
    }

    function skipSpace(string) {
    var first = string.search(/\S/);
    if (first == -1) return "";
    return string.slice(first);
    }

    function parseApply(left, program) {
    program = skipSpace(program);
    if (program[0] != "(")
    return {expr: left, rest: program};

    program = skipSpace(program.slice(1));
    var expr = {type: "apply", operator: left, args: []};
    while (program[0] != ")") {
    var arg = parseExpression(program);
    expr.args.push(arg.expr);
    program = skipSpace(arg.rest);
    if (program[0] == ",")
    program = skipSpace(program.slice(1));
    else if (program[0] != ")")
    throw new SyntaxError("Expected ',' or ')'");
    }
    return parseApply(expr, program.slice(1));
    }

    function parse(program) {
    var result = parseExpression(program);
    if (skipSpace(result.rest).length > 0)
    throw new SyntaxError("Unexpected text after program");
    return result.expr;
    }

    function evaluate(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 = evaluate(expr.operator, env);
    if (typeof op != "function")
    throw new TypeError("Applying a non-function.");
    return op.apply(null, expr.args.map(function(arg) {
    return evaluate(arg, env);
    }));
    }
    }

    var specialForms = Object.create(null);

    specialForms["if"] = function(args, env) {
    if (args.length != 3)
    throw new SyntaxError("Bad number of args of 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 (evaluate(args[0], env) !== false)
    evaluate(args[1], env);

    // Since undefined does not exit in out language, and this form does not
    // return a meaningful value, we return false.
    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[arg[0].name] = value;
    return value;
    };

    specialForms["fun"] = function(args, env) {
    if (!args.length)
    throw new SyntaxError("Functions 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;
    };

    topEnv["array"] = function(args) {
    return Array.prototype.slice.call(arguments, 0);
    };

    topEnv["length"] = function(array) {
    return array.length;
    };

    topEnv["element"] = function(array, index) {
    return array[index];
    }

    function run() {
    var env = Object.create(topEnv);
    var program = Array.prototype.slice.call(arguments, 0).join("\n");
    return evaluate(parse(program), env);
    }