From 7cd6579be6507d3b9429e986237c505fb4eef9a8 Mon Sep 17 00:00:00 2001 From: Siddharth Narayanaswamy Date: Fri, 9 May 2014 20:59:22 -0700 Subject: [PATCH] NO CAUSE FOR ALARM! webchurch whitespace and indentation cleanup - no changes other than indentation, hanging-whitespace, and excess empty lines cleaned up - for emacs, achieved through js2-mode wizardry (I didn't actually do this by hand. I do have a life...) + appropriate wizardry here - https://gist.github.com/rawsyntax/2846385 js-indent-level 4 (instead of 2 as in the link) + 4 makes things look prettier aligned (with 'for', 'var', etc.) but I'm happy to change to something else (not 8 :P) if people prefer that --- church | 22 +- church_astify.js | 930 +++++++------ church_builtins.js | 2662 +++++++++++++++++++------------------- cm-brackets.js | 434 +++---- cm-church.js | 244 ++-- cm-comments.js | 272 ++-- cm-folding.js | 245 ++-- compile.sh | 24 +- editor.js | 407 +++--- evaluate.js | 499 ++++--- higher_order_builtins.js | 12 +- js_astify.js | 636 +++++---- make-docs.js | 70 +- package.json | 62 +- precompile.js | 787 ++++++----- tester.js | 285 ++-- tokenize.js | 98 +- trace.js | 436 +++---- type-utils.js | 23 +- util.js | 90 +- viz.js | 685 +++++----- wctransform.js | 211 ++- ww-stub.js | 60 +- 23 files changed, 4553 insertions(+), 4641 deletions(-) diff --git a/church b/church index cd3c305..712e4a3 100755 --- a/church +++ b/church @@ -4,8 +4,8 @@ var sys = require('sys'); var optparse = require('optparse'); function abort(msg) { - console.log(msg) - process.exit(0); + console.log(msg) + process.exit(0); } global["evaluate"] = require('./evaluate.js').evaluate @@ -28,27 +28,27 @@ var parser = new optparse.OptionParser(switches); parser.on('precompile', function(x) { - opts.pc = true; + opts.pc = true; }); parser.on("program-args", function(opt, value) { - opts.argstring = value; + opts.argstring = value; }); parser.on("timed", function() { - opts.timed = true; + opts.timed = true; }); parser.on("desugar-only", function() { - opts.desugar = true; + opts.desugar = true; }); parser.on("compile-only", function() { - opts.compile = true; + opts.compile = true; }); parser.on("disable-church-errors", function() { - opts.disable_church_errors = true; + opts.disable_church_errors = true; }); parser.parse(process.argv); @@ -56,9 +56,9 @@ parser.parse(process.argv); code = require('fs').readFileSync(srcfile, "utf8"); try { - result = format_result(evaluate(code, opts)); - console.log(result); + result = format_result(evaluate(code, opts)); + console.log(result); } catch (e) { - console.log(e.message) + console.log(e.message) throw e } diff --git a/church_astify.js b/church_astify.js index 811edbc..21697ab 100644 --- a/church_astify.js +++ b/church_astify.js @@ -6,15 +6,15 @@ var brackets_map = {"(": ")", "[": "]"}; var query_fns = ["rejection-query", "mh-query", "enumeration-query", "conditional"]; var query_fns_to_num_params = { - "rejection-query": 0, - "enumeration-query": 0, - "conditional": 1, - "mh-query": 2 + "rejection-query": 0, + "enumeration-query": 0, + "conditional": 1, + "mh-query": 2 } var condition_fns = ["condition", "factor", "condition-repeat-equals"]; function make_generic_node(head, children) { - return {"head": head, "children": children}; + return {"head": head, "children": children}; } function deep_copy(obj) { return JSON.parse(JSON.stringify(obj)); } @@ -23,507 +23,505 @@ function isquote(ast){return ast.children && ast.children[0].text && ast.childre // TODO: add all kinds of error-checking. function church_astify(tokens) { - // astify changes the opening bracket tokens so the end site is the matching closing bracket - function astify(tokens) { - - function helper(opening_bracket) { - // Tree nodes have keys [children, start, end] - var result = {children: [], start: opening_bracket ? opening_bracket.start : "1:1"}; - while (tokens.length > 0) { - if (tokens[0].text == "(" || tokens[0].text == "[") { - var bracket = tokens[0]; - storage.push(tokens.shift()); - result.children.push(helper(bracket)); - } else if (tokens[0].text == ")" || tokens[0].text == "]") { - if (!opening_bracket || tokens[0].text != brackets_map[opening_bracket.text]) { - throw util.make_church_error("SyntaxError", tokens[0].start, tokens[0].end, "Unexpected close parens"); - } else { - result["end"] = tokens[0].start; - opening_bracket.end = tokens[0].start; - storage.push(tokens.shift()); - return result; - } - } else { - var token = tokens.shift(); - storage.push(token); - result.children.push(token); - } - } - if (!opening_bracket) { - return result; - } else { - throw util.make_church_error("SyntaxError", opening_bracket.start, opening_bracket.end, "Unclosed parens"); - } - } - var storage = []; - var ast = helper(); - for (var i = 0; i < storage.length; i++) { - tokens.push(storage[i]); - } - return ast; - } - - function traverse(ast, fn, stopfn) { - if (!util.is_leaf(ast) && ast.children.length > 0 && (!stopfn || !stopfn(ast))) { - ast = fn(ast); - for (var i = 0; i < ast.children.length; i++) { - ast.children[i] = traverse(ast.children[i], fn, stopfn); - } + // astify changes the opening bracket tokens so the end site is the matching closing bracket + function astify(tokens) { + + function helper(opening_bracket) { + // Tree nodes have keys [children, start, end] + var result = {children: [], start: opening_bracket ? opening_bracket.start : "1:1"}; + while (tokens.length > 0) { + if (tokens[0].text == "(" || tokens[0].text == "[") { + var bracket = tokens[0]; + storage.push(tokens.shift()); + result.children.push(helper(bracket)); + } else if (tokens[0].text == ")" || tokens[0].text == "]") { + if (!opening_bracket || tokens[0].text != brackets_map[opening_bracket.text]) { + throw util.make_church_error("SyntaxError", tokens[0].start, tokens[0].end, "Unexpected close parens"); + } else { + result["end"] = tokens[0].start; + opening_bracket.end = tokens[0].start; + storage.push(tokens.shift()); + return result; + } + } else { + var token = tokens.shift(); + storage.push(token); + result.children.push(token); } - return ast; + } + if (!opening_bracket) { + return result; + } else { + throw util.make_church_error("SyntaxError", opening_bracket.start, opening_bracket.end, "Unclosed parens"); + } } - - function is_special_form(text) { - return ["define", "lambda", "case", "cond", "if", "let"].indexOf(text) != -1; + var storage = []; + var ast = helper(); + for (var i = 0; i < storage.length; i++) { + tokens.push(storage[i]); } - - function assert_not_special_form(node) { - if (is_special_form(node.text)) { - throw util.make_church_error("SyntaxError", node.start, node.end, "Special form " + node.text + " cannot be used as an atom"); - } - } - - function validate_leaves(ast) { - for (var i = 1; i < ast.children.length; i++) { - assert_not_special_form(ast.children[i]); - } - return ast; + return ast; + } + + function traverse(ast, fn, stopfn) { + if (!util.is_leaf(ast) && ast.children.length > 0 && (!stopfn || !stopfn(ast))) { + ast = fn(ast); + for (var i = 0; i < ast.children.length; i++) { + ast.children[i] = traverse(ast.children[i], fn, stopfn); + } } + return ast; + } - // NOTE: Many of the desugar functions don't add range information. - // For now, it seems unlikely they'll be needed. + function is_special_form(text) { + return ["define", "lambda", "case", "cond", "if", "let"].indexOf(text) != -1; + } - function dsgr_define(ast) { - if (ast.children[0].text=="define") { - if (ast.children.length < 3) { - throw util.make_church_error("SyntaxError", ast.start, ast.end, "Invalid define"); - } - // Function define sugar - if (!util.is_leaf(ast.children[1])) { - var lambda_args; - // Variadic sugar - if (ast.children[1].children.length == 3 && ast.children[1].children[1].text == ".") { - lambda_args = ast.children[1].children[2]; - } else { - lambda_args = {children: ast.children[1].children.slice(1)}; - } - var lambda = { - children: [ - {text: "lambda"}, - lambda_args - ].concat(ast.children.slice(2)) - }; - return { - children: [ast.children[0], ast.children[1].children[0], lambda], - start: ast.start, - end: ast.end - }; - } - } - return ast; + function assert_not_special_form(node) { + if (is_special_form(node.text)) { + throw util.make_church_error("SyntaxError", node.start, node.end, "Special form " + node.text + " cannot be used as an atom"); } + } - function dsgr_lambda(ast) { - if (ast.children[0].text=="lambda") { - if (ast.children.length < 3) { - throw util.make_church_error("SyntaxError", ast.start, ast.end, "lambda has no body"); - } - } - return ast; + function validate_leaves(ast) { + for (var i = 1; i < ast.children.length; i++) { + assert_not_special_form(ast.children[i]); } - - function dsgr_let(ast) { - var let_varieties = ["let", "let*"]; - - if (let_varieties.indexOf(ast.children[0].text)!=-1) { - if (ast.children.length < 3) { - throw util.make_church_error("SyntaxError", ast.start, ast.end, ast.children[0].text + " has no body"); - } - var bindings = ast.children[1]; - var valid_bindings = true; - if (util.is_leaf(bindings)) { - valid_bindings = false; - } else { - for (var i = 0; i < bindings.children.length; i++) { - if (util.is_leaf(bindings.children[i]) || bindings.children[i].children.length != 2) { - valid_bindings = false; - break; - } - } - } - if (!valid_bindings) { - throw util.make_church_error_range("SyntaxError", bindings.start, bindings.end, ast.children[0].text + " has invalid bindings"); - } - - switch (ast.children[0].text) { - case "let": - return { - children: [ - { - children: [ - {text: "lambda"}, - {children: bindings.children.map(function(x) {return x.children[0]})}, - ast.children[2] - ] - } - ].concat(bindings.children.map(function(x) {return x.children[1]})) - }; - case "let*": - var new_ast = { - children: [ - { - children: [ - {text: "lambda"}, - {children: []}, - ast.children[2] - ] - } - ] - } - for (var i = bindings.children.length-1; i >= 0; i--) { - // console.log(JSON.stringify(bindings.children[i].children[0],undefined,2)) - new_ast = { - children: [ - { - children: [ - {text: "lambda"}, - {children: [bindings.children[i].children[0]]}, - new_ast - ] - }, - bindings.children[i].children[1] - ] - } - } - return new_ast; - } - - + return ast; + } + + // NOTE: Many of the desugar functions don't add range information. + // For now, it seems unlikely they'll be needed. + + function dsgr_define(ast) { + if (ast.children[0].text=="define") { + if (ast.children.length < 3) { + throw util.make_church_error("SyntaxError", ast.start, ast.end, "Invalid define"); + } + // Function define sugar + if (!util.is_leaf(ast.children[1])) { + var lambda_args; + // Variadic sugar + if (ast.children[1].children.length == 3 && ast.children[1].children[1].text == ".") { + lambda_args = ast.children[1].children[2]; } else { - return ast; + lambda_args = {children: ast.children[1].children.slice(1)}; } + var lambda = { + children: [ + {text: "lambda"}, + lambda_args + ].concat(ast.children.slice(2)) + }; + return { + children: [ast.children[0], ast.children[1].children[0], lambda], + start: ast.start, + end: ast.end + }; + } } + return ast; + } - function dsgr_quote(ast) { - var last = ast.children[ast.children.length-1]; - if (last.text=="'") { - throw util.make_church_error("SyntaxError", last.start, last.end, "Invalid single quote"); - } - for (var i = ast.children.length - 2; i >= 0; i--) { - if (ast.children[i].text == "'") { - ast.children.splice(i, 2, { - children: [{text: "quote", start: ast.children[i].start, end: ast.children[i].end}, ast.children[i+1]], - start: ast.children[i].start, - end: ast.children[i+1].end - }); - } - } - return ast; + function dsgr_lambda(ast) { + if (ast.children[0].text=="lambda") { + if (ast.children.length < 3) { + throw util.make_church_error("SyntaxError", ast.start, ast.end, "lambda has no body"); + } } - - function dsgr_case(ast) { - function case_helper(key, clauses) { - if (clauses.length == 0) { - return undefined; - } - var clause = clauses[0]; - if (util.is_leaf(clause) || clause.children.length != 2 || - (util.is_leaf(clause.children[0]) && clause.children[0].text!="else")) { - throw util.make_church_error("SyntaxError", clause.start, clause.end, "Bad clause for case"); + return ast; + } + + function dsgr_let(ast) { + var let_varieties = ["let", "let*"]; + + if (let_varieties.indexOf(ast.children[0].text)!=-1) { + if (ast.children.length < 3) { + throw util.make_church_error("SyntaxError", ast.start, ast.end, ast.children[0].text + " has no body"); + } + var bindings = ast.children[1]; + var valid_bindings = true; + if (util.is_leaf(bindings)) { + valid_bindings = false; + } else { + for (var i = 0; i < bindings.children.length; i++) { + if (util.is_leaf(bindings.children[i]) || bindings.children[i].children.length != 2) { + valid_bindings = false; + break; + } + } + } + if (!valid_bindings) { + throw util.make_church_error_range("SyntaxError", bindings.start, bindings.end, ast.children[0].text + " has invalid bindings"); + } + + switch (ast.children[0].text) { + case "let": + return { + children: [ + { + children: [ + {text: "lambda"}, + {children: bindings.children.map(function(x) {return x.children[0]})}, + ast.children[2] + ] } - - if (clause.children[0].text=="else") { - if (clauses.length > 1) { - throw util.make_church_error("SyntaxError", clause.start, clause.end, "Bad placement of else clause in case"); - } else { - return clause.children[1]; - } - } else { - for (var i = 0; i < clause.children[0]; i++) { - var datum = clause.children[0].children[i]; - if (util.is_leaf(datum)) { - throw util.make_church_error("SyntaxError", datum.start, datum.end, " for case"); - } - } - - var next = case_helper(key, clauses.slice(1)); - var new_ast = { - children: [ - {text: "if"}, - { - children: [ - {text: "member"}, - key, - {children: [{text: "list"}].concat(clause.children[0].children)} - // {children: [{text: "list"}].concat(clause.children[0])} - ] - }, - clause.children[1] - ] - }; - if (next) { - new_ast.children.push(next); - } - return new_ast; + ].concat(bindings.children.map(function(x) {return x.children[1]})) + }; + case "let*": + var new_ast = { + children: [ + { + children: [ + {text: "lambda"}, + {children: []}, + ast.children[2] + ] } + ] } - - if (ast.children[0].text=="case") { - if (ast.children.length < 3) { - throw util.make_church_error("SyntaxError", ast.start, ast.end, "case is missing clauses"); - } - return case_helper(ast.children[1], ast.children.slice(2)); - } else { - return ast; + for (var i = bindings.children.length-1; i >= 0; i--) { + // console.log(JSON.stringify(bindings.children[i].children[0],undefined,2)) + new_ast = { + children: [ + { + children: [ + {text: "lambda"}, + {children: [bindings.children[i].children[0]]}, + new_ast + ] + }, + bindings.children[i].children[1] + ] + } } + return new_ast; + } + + } else { + return ast; } + } - function dsgr_cond(ast) { - function cond_helper(clauses) { - if (clauses.length == 0) { - return undefined; - } - var clause = clauses[0]; - if (util.is_leaf(clause) || clause.children.length != 2) { - throw util.make_church_error("SyntaxError", clause.start, clause.end, "Bad clause for cond"); - } - if (clause.children[0].text=="else") { - if (clauses.length > 1) { - throw util.make_church_error("SyntaxError", clause.start, clause.end, "Bad placement of else clause in cond"); - } else { - return clause.children[1]; - } - } else { - var next = cond_helper( - clauses.slice(1)); - var new_ast = { - children: [ - {text: "if"}, - clause.children[0], - clause.children[1] - ] - }; - if (next) { - new_ast.children.push(next); - } - return new_ast; - } + function dsgr_quote(ast) { + var last = ast.children[ast.children.length-1]; + if (last.text=="'") { + throw util.make_church_error("SyntaxError", last.start, last.end, "Invalid single quote"); + } + for (var i = ast.children.length - 2; i >= 0; i--) { + if (ast.children[i].text == "'") { + ast.children.splice(i, 2, { + children: [{text: "quote", start: ast.children[i].start, end: ast.children[i].end}, ast.children[i+1]], + start: ast.children[i].start, + end: ast.children[i+1].end + }); + } + } + return ast; + } + + function dsgr_case(ast) { + function case_helper(key, clauses) { + if (clauses.length == 0) { + return undefined; + } + var clause = clauses[0]; + if (util.is_leaf(clause) || clause.children.length != 2 || + (util.is_leaf(clause.children[0]) && clause.children[0].text!="else")) { + throw util.make_church_error("SyntaxError", clause.start, clause.end, "Bad clause for case"); + } + + if (clause.children[0].text=="else") { + if (clauses.length > 1) { + throw util.make_church_error("SyntaxError", clause.start, clause.end, "Bad placement of else clause in case"); + } else { + return clause.children[1]; + } + } else { + for (var i = 0; i < clause.children[0]; i++) { + var datum = clause.children[0].children[i]; + if (util.is_leaf(datum)) { + throw util.make_church_error("SyntaxError", datum.start, datum.end, " for case"); + } } - if (ast.children[0].text=="cond") { - if (ast.children.length < 2) { - throw util.make_church_error("SyntaxError", ast.start, ast.end, "cond is missing clauses"); - } - return cond_helper(ast.children.slice(1)); - } else { - return ast; + var next = case_helper(key, clauses.slice(1)); + var new_ast = { + children: [ + {text: "if"}, + { + children: [ + {text: "member"}, + key, + {children: [{text: "list"}].concat(clause.children[0].children)} + // {children: [{text: "list"}].concat(clause.children[0])} + ] + }, + clause.children[1] + ] + }; + if (next) { + new_ast.children.push(next); } + return new_ast; + } } - function dsgr_eval(ast) { - if (ast.children[0].text == "eval") { - return { - children: [ - {text: "eval"}, - { - children: [ - // churchToBareJs: for use in eval only - {text: "churchToBareJs"}, - { - children: [ - {text: "formatResult"}, - ast.children[1] - ] - } - ] - } - ] - } - + if (ast.children[0].text=="case") { + if (ast.children.length < 3) { + throw util.make_church_error("SyntaxError", ast.start, ast.end, "case is missing clauses"); + } + return case_helper(ast.children[1], ast.children.slice(2)); + } else { + return ast; } - return ast; - }; - - function dsgr_query(ast) { - // Makes the lambda that's passed to the query function - function query_helper(statements, condition, args) { - if (util.is_leaf(condition) || condition_fns.indexOf(condition.children[0].text) == -1) { - condition = { - children: [{text: "condition"}, condition], - start: condition.start, - end: condition.end - }; - } - args = args || {children: []}; - return { - children: [ - {text: "lambda"}, - args - ].concat(statements.slice(0, -1)).concat(condition).concat(statements[statements.length-1]) - }; + } + + function dsgr_cond(ast) { + function cond_helper(clauses) { + if (clauses.length == 0) { + return undefined; + } + var clause = clauses[0]; + if (util.is_leaf(clause) || clause.children.length != 2) { + throw util.make_church_error("SyntaxError", clause.start, clause.end, "Bad clause for cond"); + } + if (clause.children[0].text=="else") { + if (clauses.length > 1) { + throw util.make_church_error("SyntaxError", clause.start, clause.end, "Bad placement of else clause in cond"); + } else { + return clause.children[1]; } - - if (query_fns.indexOf(ast.children[0].text) != -1) { - var num_params = query_fns_to_num_params[ast.children[0].text]; - if (ast.children.length < num_params + 4) { - throw util.make_church_error("SyntaxError", ast.start, ast.end, ast.children[0].text + " has the wrong number of arguments"); - } - return { - children: [ - ast.children[0], - query_helper(ast.children.slice(num_params+1, -1), ast.children[ast.children.length-1]) - ].concat(ast.children.slice(1, num_params+1)), - start: ast.start, - end: ast.end - }; + } else { + var next = cond_helper( + clauses.slice(1)); + var new_ast = { + children: [ + {text: "if"}, + clause.children[0], + clause.children[1] + ] + }; + if (next) { + new_ast.children.push(next); } + return new_ast; + } + } - return ast; + if (ast.children[0].text=="cond") { + if (ast.children.length < 2) { + throw util.make_church_error("SyntaxError", ast.start, ast.end, "cond is missing clauses"); + } + return cond_helper(ast.children.slice(1)); + } else { + return ast; + } + } + + function dsgr_eval(ast) { + if (ast.children[0].text == "eval") { + return { + children: [ + {text: "eval"}, + { + children: [ + // churchToBareJs: for use in eval only + {text: "churchToBareJs"}, + { + children: [ + {text: "formatResult"}, + ast.children[1] + ] + } + ] + } + ] + } + } + return ast; + }; + + function dsgr_query(ast) { + // Makes the lambda that's passed to the query function + function query_helper(statements, condition, args) { + if (util.is_leaf(condition) || condition_fns.indexOf(condition.children[0].text) == -1) { + condition = { + children: [{text: "condition"}, condition], + start: condition.start, + end: condition.end + }; + } + args = args || {children: []}; + return { + children: [ + {text: "lambda"}, + args + ].concat(statements.slice(0, -1)).concat(condition).concat(statements[statements.length-1]) + }; } - function validate_if(ast) { - if (ast.children[0].text=="if") { - if (ast.children.length < 3 || ast.children.length > 4) { - throw util.make_church_error("SyntaxError", ast.start, ast.end, "if has the wrong number of arguments"); - } - } - return ast; + if (query_fns.indexOf(ast.children[0].text) != -1) { + var num_params = query_fns_to_num_params[ast.children[0].text]; + if (ast.children.length < num_params + 4) { + throw util.make_church_error("SyntaxError", ast.start, ast.end, ast.children[0].text + " has the wrong number of arguments"); + } + return { + children: [ + ast.children[0], + query_helper(ast.children.slice(num_params+1, -1), ast.children[ast.children.length-1]) + ].concat(ast.children.slice(1, num_params+1)), + start: ast.start, + end: ast.end + }; } - - function transform_equals_condition(ast) { - function is_equals_conditionable(ast) { - if (util.is_leaf(ast)) return false; - var fn = church_builtins.__annotations__[rename_map[ast.children[0].text]]; - return fn && fn.erp && ast.children[fn.numArgs[fn.numArgs.length-1]] == undefined; - } - function transform_erp(erp, conditioned_value) { - erp.children.push(conditioned_value); - } + return ast; + } - function try_transform(left, right) { - if (left == undefined) return false; - if (is_equals_conditionable(left)) { - transform_erp(left, right); - statements.splice(i, 1, left); - return true; - } else if (util.is_leaf(left) && define_table[left.text] && is_equals_conditionable(define_table[left.text].def)) { - var left_entry = define_table[left.text]; - if (!util.is_identifier(right.text) || ( - define_table[right.text] && left_entry.index > define_table[right.text].index)) { - transform_erp(left_entry.def, right); - statements.splice(i, 1); - return true; - } - } - return false; - } + function validate_if(ast) { + if (ast.children[0].text=="if") { + if (ast.children.length < 3 || ast.children.length > 4) { + throw util.make_church_error("SyntaxError", ast.start, ast.end, "if has the wrong number of arguments"); + } + } + return ast; + } - var transformed; - if (query_fns.indexOf(ast.children[0].text) != -1) { - var define_table = {}; - // Assumes preprocessing through dsgr_query - var statements = ast.children[1].children.slice(2); - var i = 0; - // Iterate through each lambda statement - for (var i = 0; i < statements.length; i++) { - if (!util.is_leaf(statements[i])) { - // If statement is a define and an ERP without an existing condition, put it in a table - if (statements[i].children[0].text == "define") { - define_table[statements[i].children[1].text] = { - index: i, - def: statements[i].children[2] - }; - // If statement is a condition, check if it's an equality and attempt to transform - } else if (statements[i].children[0].text == "condition") { - var condition = statements[i].children[1]; - if (!util.is_leaf(condition) && ["=", "equal?"].indexOf(condition.children[0].text) != -1 && condition.children.length == 3) { - var left = condition.children[1]; - var right = condition.children[2]; - if (!try_transform(left, right)) try_transform(right, left); - } - } - - } - } + function transform_equals_condition(ast) { + function is_equals_conditionable(ast) { + if (util.is_leaf(ast)) return false; + var fn = church_builtins.__annotations__[rename_map[ast.children[0].text]]; + return fn && fn.erp && ast.children[fn.numArgs[fn.numArgs.length-1]] == undefined; + } - } - return ast; + function transform_erp(erp, conditioned_value) { + erp.children.push(conditioned_value); } - function transform_repeat_equals_condition(ast) { - function try_transform(left, right) { - if (!util.is_leaf(left) && left.children[0].text == "repeat") { - ast.children[1].children[i+2] = { - children: [ - {"text": "multi-equals-condition"}, - left.children[2], - left.children[1], - right], - start: ast.children[1].children[i+2].children[0].start, - end: ast.children[1].children[i+2].children[0].end - }; - return true; - } - return false; + function try_transform(left, right) { + if (left == undefined) return false; + if (is_equals_conditionable(left)) { + transform_erp(left, right); + statements.splice(i, 1, left); + return true; + } else if (util.is_leaf(left) && define_table[left.text] && is_equals_conditionable(define_table[left.text].def)) { + var left_entry = define_table[left.text]; + if (!util.is_identifier(right.text) || ( + define_table[right.text] && left_entry.index > define_table[right.text].index)) { + transform_erp(left_entry.def, right); + statements.splice(i, 1); + return true; } + } + return false; + } - if (query_fns.indexOf(ast.children[0].text) != -1) { - var statements = ast.children[1].children.slice(2); - for (var i = 0; i < statements.length; i++) { - if (!util.is_leaf(statements[i]) && statements[i].children[0].text == "condition") { - var condition = statements[i].children[1]; - if (!util.is_leaf(condition) && condition.children[0].text == "equal?" && condition.children.length == 3) { - var left = condition.children[1]; - var right = condition.children[2]; - if (!try_transform(left, right)) try_transform(right, left); - } - } + var transformed; + if (query_fns.indexOf(ast.children[0].text) != -1) { + var define_table = {}; + // Assumes preprocessing through dsgr_query + var statements = ast.children[1].children.slice(2); + var i = 0; + // Iterate through each lambda statement + for (var i = 0; i < statements.length; i++) { + if (!util.is_leaf(statements[i])) { + // If statement is a define and an ERP without an existing condition, put it in a table + if (statements[i].children[0].text == "define") { + define_table[statements[i].children[1].text] = { + index: i, + def: statements[i].children[2] + }; + // If statement is a condition, check if it's an equality and attempt to transform + } else if (statements[i].children[0].text == "condition") { + var condition = statements[i].children[1]; + if (!util.is_leaf(condition) && ["=", "equal?"].indexOf(condition.children[0].text) != -1 && condition.children.length == 3) { + var left = condition.children[1]; + var right = condition.children[2]; + if (!try_transform(left, right)) try_transform(right, left); } + } + } - return ast; + } + + } + return ast; + } + + function transform_repeat_equals_condition(ast) { + function try_transform(left, right) { + if (!util.is_leaf(left) && left.children[0].text == "repeat") { + ast.children[1].children[i+2] = { + children: [ + {"text": "multi-equals-condition"}, + left.children[2], + left.children[1], + right], + start: ast.children[1].children[i+2].children[0].start, + end: ast.children[1].children[i+2].children[0].end + }; + return true; + } + return false; } - // Break out conditions with ands into multiple condition statements - function transform_and_condition(ast) { - if (["rejection-query", "enumeration-query", "mh-query"].indexOf(ast.children[0].text) != -1) { - var lambda = ast.children[1]; - var stmts = lambda.children.splice(2); - for (var i = 0; i < stmts.length; i++) { - if (!util.is_leaf(stmts[i]) && stmts[i].children[0].text == "condition") { - var condition_stmt = stmts[i]; - var condition = condition_stmt.children[1]; - if (!util.is_leaf(condition) && condition.children[0].text == "and") { - for (var j=1;j= 0; - }, - 'positive real': function(x) { - return typeof x == 'number' && x > 0; - }, - real: function(x) { - return typeof x == 'number'; - }, - function: function(x) { - return typeof x == 'function'; - }, - pair: function(x) { - return Array.isArray(x) && x.length >= 2; - }, - list: function(x) { - return Array.isArray(x) && x[x.length - 1] == null; - }, - 'boolean': function(x) { - return typeof x == 'boolean'; - }, - 'string': function(x) { - return typeof x == 'string'; - } + 'integer': function(x) { + return typeof x == 'number' && Math.floor(x) == x; + }, + nat: function(x) { + return typeof x == 'number' && Math.floor(x) == x && x >= 0; + }, + 'positive real': function(x) { + return typeof x == 'number' && x > 0; + }, + real: function(x) { + return typeof x == 'number'; + }, + function: function(x) { + return typeof x == 'function'; + }, + pair: function(x) { + return Array.isArray(x) && x.length >= 2; + }, + list: function(x) { + return Array.isArray(x) && x[x.length - 1] == null; + }, + 'boolean': function(x) { + return typeof x == 'boolean'; + }, + 'string': function(x) { + return typeof x == 'string'; + } }; // handle simple parameterized types like // List real, Pair real // TODO: test this function parseTypeString(s) { - if (/list|pair/.test(s)) { - var baseType = /list/.test(s) ? 'list' : 'pair'; + if (/list|pair/.test(s)) { + var baseType = /list/.test(s) ? 'list' : 'pair'; - var uStart = s.indexOf("<"); - var uEnd = s.lastIndexOf(">"); + var uStart = s.indexOf("<"); + var uEnd = s.lastIndexOf(">"); - var baseChecker = typeCheckers[baseType]; + var baseChecker = typeCheckers[baseType]; - if (uStart == -1 || uEnd == -1) { - return baseChecker; - } + if (uStart == -1 || uEnd == -1) { + return baseChecker; + } - var u = s.slice(uStart + 1, uEnd); - var uChecker = parseTypeString(u); + var u = s.slice(uStart + 1, uEnd); + var uChecker = parseTypeString(u); - if (baseType == 'pair') { - return function(x) { - if (!baseChecker(x)) { - return false; - } + if (baseType == 'pair') { + return function(x) { + if (!baseChecker(x)) { + return false; + } - return uChecker(x[0]) && uChecker(_rest(x)); - }; + return uChecker(x[0]) && uChecker(_rest(x)); + }; - } + } - // otherwise, return checker for list<...> - return function(x) { - if (!baseChecker(x)) { - return false; - } - var x_array = listToArray(x); - for(var i = 0, ii = x_array.length; i < ii; i++) { - if (!uChecker(x_array[i])) { - return false; + // otherwise, return checker for list<...> + return function(x) { + if (!baseChecker(x)) { + return false; + } + var x_array = listToArray(x); + for(var i = 0, ii = x_array.length; i < ii; i++) { + if (!uChecker(x_array[i])) { + return false; + }; + } + return true; }; - } - return true; - }; - - } else { - return typeCheckers[s]; - } + } else { + return typeCheckers[s]; + } } // TODO: underscore is too heavy weight // replace with mustache. or maybe something even dumber var _ = require('underscore'); _.templateSettings = { - interpolate: /\{\{(.+?)\}\}/g + interpolate: /\{\{(.+?)\}\}/g }; var util = require('./util.js'); @@ -112,7 +111,7 @@ var arrayToList = typeUtils.arrayToList; // determine whether we're running inside a browser var inBrowser = false; if (typeof document !== 'undefined') { - inBrowser = true; + inBrowser = true; } // var seed = require('seed-random'); @@ -123,11 +122,11 @@ if (typeof document !== 'undefined') { module.exports.__annotations__ = {}; var addBuiltin = function(dict) { - var fWrapped = wrapAsserts(dict); + var fWrapped = wrapAsserts(dict); - module.exports[dict.name] = fWrapped; - module.exports.__annotations__[dict.name] = dict; - return fWrapped; + module.exports[dict.name] = fWrapped; + module.exports.__annotations__[dict.name] = dict; + return fWrapped; }; var $b = addBuiltin; @@ -139,1570 +138,1567 @@ function sizeof(obj) { return Object.keys(obj).length; } // but users shouldn't need to directly call this function // so don't add it to annotations var args_to_array = module.exports.args_to_list = function(args) { - return Array.prototype.slice.call(args, 0 ); + return Array.prototype.slice.call(args, 0 ); }; // needs to live in global scope // but users shouldn't need to directly call this function // so don't add it to annotations var args_to_list = module.exports.args_to_list = function (args) { - return arrayToList(args_to_array(args)); + return arrayToList(args_to_array(args)); }; var plus = $b({ - name: 'plus', - alias: '+', - desc: "Add numbers", - params: [{name: '[x ...]', type: 'real', desc: 'Numbers to add'}], - fn: function () { - var sum = 0; - for (var i = 0, ii = arguments.length; i < ii; i++) { - sum = sum + arguments[i]; - } - return sum; - } + name: 'plus', + alias: '+', + desc: "Add numbers", + params: [{name: '[x ...]', type: 'real', desc: 'Numbers to add'}], + fn: function () { + var sum = 0; + for (var i = 0, ii = arguments.length; i < ii; i++) { + sum = sum + arguments[i]; + } + return sum; + } }); var minus = $b({ - name: 'minus', - alias: '-', - desc: "Subtract numbers", - params: [{name: '[x ...]', type: 'real', desc: 'Numbers to subtract'}], - fn: function() { - var numArgs = arguments.length; - if (numArgs == 0) { - return 0; - } else if (numArgs == 1) { - return -arguments[0]; - } else { - var r = arguments[0]; - for (var i = 1; i < numArgs; i++) { - r -= arguments[i]; - } - return r; + name: 'minus', + alias: '-', + desc: "Subtract numbers", + params: [{name: '[x ...]', type: 'real', desc: 'Numbers to subtract'}], + fn: function() { + var numArgs = arguments.length; + if (numArgs == 0) { + return 0; + } else if (numArgs == 1) { + return -arguments[0]; + } else { + var r = arguments[0]; + for (var i = 1; i < numArgs; i++) { + r -= arguments[i]; + } + return r; + } } - } }); var mult = $b({ - name: 'mult', - alias: '*', - desc: "Multiply numbers", - params: [{name: '[x ...]', type: 'real', desc: 'Numbers to multiply'}], - fn: function() { - var numArgs = arguments.length; - var prod = 1; - for (var i = 0; i < numArgs; i++) { - prod = prod * arguments[i]; - } - return prod; - } + name: 'mult', + alias: '*', + desc: "Multiply numbers", + params: [{name: '[x ...]', type: 'real', desc: 'Numbers to multiply'}], + fn: function() { + var numArgs = arguments.length; + var prod = 1; + for (var i = 0; i < numArgs; i++) { + prod = prod * arguments[i]; + } + return prod; + } }); var div = $b({ - name: 'div', - alias: '/', - desc: "Divide numbers. Returns x / (y1 * y2 * ... )", - params: [{name: '[x]', type: 'real', desc: 'Numerator'}, - {name: '[y ...]', type: 'real', desc: 'Denominator values'} - ], - fn: function() { - var numerator = arguments[0]; + name: 'div', + alias: '/', + desc: "Divide numbers. Returns x / (y1 * y2 * ... )", + params: [{name: '[x]', type: 'real', desc: 'Numerator'}, + {name: '[y ...]', type: 'real', desc: 'Denominator values'} + ], + fn: function() { + var numerator = arguments[0]; + + var numArgs = arguments.length; + if (numArgs == 0) { + return 1; + } - var numArgs = arguments.length; - if (numArgs == 0) { - return 1; - } + if (numArgs == 1) { + return 1 / arguments[0]; + } + var denominator = 1; - if (numArgs == 1) { - return 1 / arguments[0]; + for (var i = 1; i < numArgs; i++) { + denominator *= arguments[i]; + } + return numerator / denominator; } - var denominator = 1; - - for (var i = 1; i < numArgs; i++) { - denominator *= arguments[i]; - } - return numerator / denominator; - } }); var mod = $b({ - name: 'mod', - alias: 'modulo', - desc: "Modulo. Returns x mod y", - params: [{name: 'x', type: 'real'}, - {name: 'x', type: 'real'}], - fn: function(x,y) { - return x % y; - } + name: 'mod', + alias: 'modulo', + desc: "Modulo. Returns x mod y", + params: [{name: 'x', type: 'real'}, + {name: 'x', type: 'real'}], + fn: function(x,y) { + return x % y; + } }); var round = $b({ - name: 'round', - desc: 'Round a number', - params: [{name: 'x', type: 'real'}], - fn: function(x) { - return Math.round(x); - } + name: 'round', + desc: 'Round a number', + params: [{name: 'x', type: 'real'}], + fn: function(x) { + return Math.round(x); + } }); var abs = $b({ - name: 'abs', - desc: 'Absolute value', - params: [{name: 'x', type: 'real'}], - fn: function(x) { - return Math.abs(x); - } + name: 'abs', + desc: 'Absolute value', + params: [{name: 'x', type: 'real'}], + fn: function(x) { + return Math.abs(x); + } }); var log = $b({ - name: 'log', - desc: 'Natural logarithm', - params: [{name: 'x', type: 'real'}], - fn: function(x) { - return Math.log(x); - } + name: 'log', + desc: 'Natural logarithm', + params: [{name: 'x', type: 'real'}], + fn: function(x) { + return Math.log(x); + } }); var exp = $b({ - name: 'exp', - desc: 'Exponential', - params: [{name: 'x', type: 'real'}], - fn: function(x) { - return Math.exp(x); - } + name: 'exp', + desc: 'Exponential', + params: [{name: 'x', type: 'real'}], + fn: function(x) { + return Math.exp(x); + } }); var expt = $b({ - name: 'expt', - alias: ['pow','expt'], - desc: 'Compute x raised to the power y', - params: [{name: 'x', type: 'real'}, - {name: 'y', type: 'real'} - ], - fn: function(x, y) { - return Math.pow(x, y); - } + name: 'expt', + alias: ['pow','expt'], + desc: 'Compute x raised to the power y', + params: [{name: 'x', type: 'real'}, + {name: 'y', type: 'real'} + ], + fn: function(x, y) { + return Math.pow(x, y); + } }); var sqrt = $b({ - name: 'sqrt', - desc: 'Square root', - params: [{name: 'x', type: 'real'}], - fn: function(x) { - return Math.sqrt(x); - } + name: 'sqrt', + desc: 'Square root', + params: [{name: 'x', type: 'real'}], + fn: function(x) { + return Math.sqrt(x); + } }); var sum = $b({ - name: 'sum', - desc: 'Sum a list of numbers', - params: [{name: 'lst', type: 'list', desc: 'List of numbers to sum'}], - fn: function(lst) { - var arr = listToArray(lst); - var r = 0; - for(var i = 0, ii = arr.length; i < ii; i++) { - r += arr[i]; - } + name: 'sum', + desc: 'Sum a list of numbers', + params: [{name: 'lst', type: 'list', desc: 'List of numbers to sum'}], + fn: function(lst) { + var arr = listToArray(lst); + var r = 0; + for(var i = 0, ii = arr.length; i < ii; i++) { + r += arr[i]; + } - return r; - } + return r; + } }); var prod = $b({ - name: 'prod', - desc: 'Multiply a list of numbers', - params: [{name: 'lst', type: 'list', desc: 'List of numbers to multiply'}], - fn: function(lst) { - var arr = listToArray(lst); - var r = 1; - for(var i = 0, ii = arr.length; i < ii; i++) { - r *= arr[i]; - } + name: 'prod', + desc: 'Multiply a list of numbers', + params: [{name: 'lst', type: 'list', desc: 'List of numbers to multiply'}], + fn: function(lst) { + var arr = listToArray(lst); + var r = 1; + for(var i = 0, ii = arr.length; i < ii; i++) { + r *= arr[i]; + } - return r; - } + return r; + } }); // check whether y \in (x - tol, x + tol) var soft_equal = $b({ - name: 'soft_equal', - desc: 'Check whether y is in the interval [x - tol, x + tol]', - params: [{name: 'y', type: 'real'}, - {name: 'x', type: 'real'}, - {name: 'tol', type: 'real'} - ], - fn: function(y, x, tol) { - // FIXME: assert upper > lower - return (y > x - tol && y < x + tol); - } + name: 'soft_equal', + desc: 'Check whether y is in the interval [x - tol, x + tol]', + params: [{name: 'y', type: 'real'}, + {name: 'x', type: 'real'}, + {name: 'tol', type: 'real'} + ], + fn: function(y, x, tol) { + // FIXME: assert upper > lower + return (y > x - tol && y < x + tol); + } }); var and = $b({ - name: 'and', - desc: 'Logical conjunction', - params: [{name: '[b ...]', type: 'boolean', desc: 'Boolean values'}], - fn: function() { - var numArgs = arguments.length; - for (var i = 0; i < numArgs; i++) { - if (!arguments[i]) { - return false; - } - } - return true; - } + name: 'and', + desc: 'Logical conjunction', + params: [{name: '[b ...]', type: 'boolean', desc: 'Boolean values'}], + fn: function() { + var numArgs = arguments.length; + for (var i = 0; i < numArgs; i++) { + if (!arguments[i]) { + return false; + } + } + return true; + } }); var or = $b({ - name: 'or', - desc: 'Logical disjunction', - params: [{name: '[b ...]', type: 'boolean', desc: 'Boolean values'}], - fn: function() { - var numArgs = arguments.length; - for (var i = 0; i < numArgs; i++) { - if (arguments[i]) { - return true; - } - } - return false; - } + name: 'or', + desc: 'Logical disjunction', + params: [{name: '[b ...]', type: 'boolean', desc: 'Boolean values'}], + fn: function() { + var numArgs = arguments.length; + for (var i = 0; i < numArgs; i++) { + if (arguments[i]) { + return true; + } + } + return false; + } }); var not = $b({ - name: 'not', - desc: 'Logical negation', - params: [{name: 'b', type: 'boolean', desc: 'Boolean value'}], - fn: function(b) { - return !b; - } + name: 'not', + desc: 'Logical negation', + params: [{name: 'b', type: 'boolean', desc: 'Boolean value'}], + fn: function(b) { + return !b; + } }); var all = $b({ - name: 'all', - desc: 'Test whether all of the values in a list are true', - params: [{name: 'lst', type: 'list', desc: 'List of boolean values'}], - fn: function(lst) { - return and.apply(null, listToArray(lst)); - } + name: 'all', + desc: 'Test whether all of the values in a list are true', + params: [{name: 'lst', type: 'list', desc: 'List of boolean values'}], + fn: function(lst) { + return and.apply(null, listToArray(lst)); + } }); var none = $b({ - name: 'none', - desc: 'Test whether none of the values in a list are true', - params: [{name: 'lst', type: 'list', desc: 'List of boolean values'}], - fn: function(lst) { - return !or.apply(null, listToArray(lst)); - } + name: 'none', + desc: 'Test whether none of the values in a list are true', + params: [{name: 'lst', type: 'list', desc: 'List of boolean values'}], + fn: function(lst) { + return !or.apply(null, listToArray(lst)); + } }); var some = $b({ - name: 'some', - alias: 'any', - desc: 'Test whether some of the values in a list are true', - params: [{name: 'lst', type: 'list', desc: 'List of boolean values'}], - fn: function(lst) { - return or.apply(null, listToArray(lst)); - } + name: 'some', + alias: 'any', + desc: 'Test whether some of the values in a list are true', + params: [{name: 'lst', type: 'list', desc: 'List of boolean values'}], + fn: function(lst) { + return or.apply(null, listToArray(lst)); + } }); var greater = $b({ - name: 'greater', - alias: '>', - desc: 'Test whether x is greater than all y\'s', - params: [{name: 'x', type: 'real'}, - {name: '[y ...]', type: 'real'} - ], - fn: function() { - var numArgs = arguments.length; - var x = arguments[0]; - for (var i = 1; i < numArgs ; i++) { - if (!(x > arguments[i])) { - return false; - } - } - return true; - } + name: 'greater', + alias: '>', + desc: 'Test whether x is greater than all y\'s', + params: [{name: 'x', type: 'real'}, + {name: '[y ...]', type: 'real'} + ], + fn: function() { + var numArgs = arguments.length; + var x = arguments[0]; + for (var i = 1; i < numArgs ; i++) { + if (!(x > arguments[i])) { + return false; + } + } + return true; + } }); var less = $b({ - name: 'less', - alias: '<', - desc: 'Test whether x is less than all y\'s', - params: [{name: 'x', type: 'real'}, - {name: '[y ...]', type: 'real'} - ], - fn: function() { - var numArgs = arguments.length; - var x = arguments[0]; - for (var i = 1; i < numArgs ; i++) { - if (!(x < arguments[i])) { - return false; - } - } - return true; - } + name: 'less', + alias: '<', + desc: 'Test whether x is less than all y\'s', + params: [{name: 'x', type: 'real'}, + {name: '[y ...]', type: 'real'} + ], + fn: function() { + var numArgs = arguments.length; + var x = arguments[0]; + for (var i = 1; i < numArgs ; i++) { + if (!(x < arguments[i])) { + return false; + } + } + return true; + } }); var geq = $b({ - name: 'geq', - alias: '>=', - desc: 'Test whether x is greater than or equal to all y\'s', - params: [{name: 'x', type: 'real'}, - {name: '[y ...]', type: 'real'} - ], - fn: function() { - var numArgs = arguments.length; - var x = arguments[0]; - for (var i = 1; i < numArgs ; i++) { - if (x < arguments[i]) { - return false; - } - } - return true; - } + name: 'geq', + alias: '>=', + desc: 'Test whether x is greater than or equal to all y\'s', + params: [{name: 'x', type: 'real'}, + {name: '[y ...]', type: 'real'} + ], + fn: function() { + var numArgs = arguments.length; + var x = arguments[0]; + for (var i = 1; i < numArgs ; i++) { + if (x < arguments[i]) { + return false; + } + } + return true; + } }); var leq = $b({ - name: 'leq', - alias: '<=', - desc: 'Test whether x is less than or equal to all y\'s', - params: [{name: 'x', type: 'real'}, - {name: '[y ...]', type: 'real'} - ], - fn: function() { - var numArgs = arguments.length; - var x = arguments[0]; - for (var i = 1; i < numArgs ; i++) { - if (x > arguments[i]) { - return false; - } - } - return true; - } + name: 'leq', + alias: '<=', + desc: 'Test whether x is less than or equal to all y\'s', + params: [{name: 'x', type: 'real'}, + {name: '[y ...]', type: 'real'} + ], + fn: function() { + var numArgs = arguments.length; + var x = arguments[0]; + for (var i = 1; i < numArgs ; i++) { + if (x > arguments[i]) { + return false; + } + } + return true; + } }); var eq = $b({ - name: 'eq', - alias: '=', - desc: 'Test whether all arguments are equal', - params: [{name: '[x ...]', type: 'real'}], - fn: function() { - var numArgs = arguments.length; - var x = arguments[0]; - for (var i = 1; i < numArgs ; i++) { - if (x != arguments[i]) { - return false; - } - } - return true; - } + name: 'eq', + alias: '=', + desc: 'Test whether all arguments are equal', + params: [{name: '[x ...]', type: 'real'}], + fn: function() { + var numArgs = arguments.length; + var x = arguments[0]; + for (var i = 1; i < numArgs ; i++) { + if (x != arguments[i]) { + return false; + } + } + return true; + } }); var is_null = $b({ - name: 'is_null', - desc: 'Test whether x is null', - params: [{name: 'x'}], - fn: function(x) { - return Array.isArray(x) && x.length == 1 && x[0] == null; - } + name: 'is_null', + desc: 'Test whether x is null', + params: [{name: 'x'}], + fn: function(x) { + return Array.isArray(x) && x.length == 1 && x[0] == null; + } }); // use uppercase to indicate that it's a constructor var List = $b({ - name: 'list', - desc: 'List constructor', - params: [{name: '[...]'}], - fn: function() { - var args = args_to_array(arguments); - return arrayToList(args, true); - } + name: 'list', + desc: 'List constructor', + params: [{name: '[...]'}], + fn: function() { + var args = args_to_array(arguments); + return arrayToList(args, true); + } }); var is_list = $b({ - name: 'is_list', - desc: 'Test whether x is a list', - params: [{name: 'x'}], - fn: function(x) { - return Array.isArray(x) && x[x.length-1] == null; - } + name: 'is_list', + desc: 'Test whether x is a list', + params: [{name: 'x'}], + fn: function(x) { + return Array.isArray(x) && x[x.length-1] == null; + } }); var Pair = $b({ - name: 'pair', - alias: 'cons', - desc: 'Pair constructor', - params: [{name: 'head'}, - {name: 'tail'} - ], - fn: function(head, tail) { - return [head].concat(tail); - } + name: 'pair', + alias: 'cons', + desc: 'Pair constructor', + params: [{name: 'head'}, + {name: 'tail'} + ], + fn: function(head, tail) { + return [head].concat(tail); + } }); var is_pair = $b({ - name: 'is_pair', - desc: 'Test whether x is a pair', - params: [{name: 'x'}], - fn: function(x) { - return Array.isArray(x) && x.length >= 2; - } + name: 'is_pair', + desc: 'Test whether x is a pair', + params: [{name: 'x'}], + fn: function(x) { + return Array.isArray(x) && x.length >= 2; + } }); var first = $b({ - name: 'first', - alias: 'car', - desc: 'Get the first item of a list (or pair)', - params: [{name: 'lst', type: 'pair'}], - fn: function(lst) { - var arr = listToArray(lst); - if (arr.length < 1) { - throw new Error('Tried to get the first element of an empty list'); + name: 'first', + alias: 'car', + desc: 'Get the first item of a list (or pair)', + params: [{name: 'lst', type: 'pair'}], + fn: function(lst) { + var arr = listToArray(lst); + if (arr.length < 1) { + throw new Error('Tried to get the first element of an empty list'); + } + return lst[0]; } - return lst[0]; - } }); var second = $b({ - name: 'second', - desc: 'Get the second item of a list', - params: [{name: 'lst', type: 'list'}], - fn: function(lst) { - var arr = listToArray(lst); - if (arr.length < 2) { - throw new Error('Tried to get the 2nd element of a list with only ' + arr.length + ' item'); + name: 'second', + desc: 'Get the second item of a list', + params: [{name: 'lst', type: 'list'}], + fn: function(lst) { + var arr = listToArray(lst); + if (arr.length < 2) { + throw new Error('Tried to get the 2nd element of a list with only ' + arr.length + ' item'); + } + return lst[1]; } - return lst[1]; - } }); var third = $b({ - name: 'third', - desc: 'Get the third item of a list', - params: [{name: 'lst', type: 'list'}], - fn: function(lst) { - var arr = listToArray(lst); - if (arr.length < 3) { - throw new Error('Tried to get the 3rd element of list with only ' + arr.length + ' elements'); + name: 'third', + desc: 'Get the third item of a list', + params: [{name: 'lst', type: 'list'}], + fn: function(lst) { + var arr = listToArray(lst); + if (arr.length < 3) { + throw new Error('Tried to get the 3rd element of list with only ' + arr.length + ' elements'); + } + return lst[2]; } - return lst[2]; - } }); var fourth = $b({ - name: 'fourth', - desc: 'Get the fourth item of a list', - params: [{name: 'lst', type: 'list'}], - fn: function(lst) { - var arr = listToArray(lst); - if (arr.length < 4) { - throw new Error('Tried to get the 4th element of list with only ' + arr.length + ' elements'); + name: 'fourth', + desc: 'Get the fourth item of a list', + params: [{name: 'lst', type: 'list'}], + fn: function(lst) { + var arr = listToArray(lst); + if (arr.length < 4) { + throw new Error('Tried to get the 4th element of list with only ' + arr.length + ' elements'); + } + return lst[3]; } - return lst[3]; - } }); var fifth = $b({ - name: 'fifth', - desc: 'Get the fifth item of a list', - params: [{name: 'lst', type: 'list'}], - fn: function(lst) { - var arr = listToArray(lst); - if (arr.length < 5) { - throw new Error('Tried to get the 5th element of list with only ' + arr.length + ' elements'); + name: 'fifth', + desc: 'Get the fifth item of a list', + params: [{name: 'lst', type: 'list'}], + fn: function(lst) { + var arr = listToArray(lst); + if (arr.length < 5) { + throw new Error('Tried to get the 5th element of list with only ' + arr.length + ' elements'); + } + return lst[4]; } - return lst[4]; - } }); var sixth = $b({ - name: 'sixth', - desc: 'Get the sixth item of a list', - params: [{name: 'lst', type: 'list'}], - fn: function(lst) { - var arr = listToArray(lst); - if (arr.length < 6) { - throw new Error('Tried to get the 6th element of list with only ' + arr.length + ' elements'); + name: 'sixth', + desc: 'Get the sixth item of a list', + params: [{name: 'lst', type: 'list'}], + fn: function(lst) { + var arr = listToArray(lst); + if (arr.length < 6) { + throw new Error('Tried to get the 6th element of list with only ' + arr.length + ' elements'); + } + return lst[5]; } - return lst[5]; - } }); var seventh = $b({ - name: 'seventh', - desc: 'Get the seventh item of a list', - params: [{name: 'lst', type: 'list'}], - fn: function(lst) { - var arr = listToArray(lst); - if (arr.length < 7) { - throw new Error('Tried to get the 7th element of list with only ' + arr.length + ' elements'); + name: 'seventh', + desc: 'Get the seventh item of a list', + params: [{name: 'lst', type: 'list'}], + fn: function(lst) { + var arr = listToArray(lst); + if (arr.length < 7) { + throw new Error('Tried to get the 7th element of list with only ' + arr.length + ' elements'); + } + return lst[6]; } - return lst[6]; - } }); // pulled out into its own function because we use it elsewhere var _rest = function(x) { - if (x.length == 2 && x[1] != null) { - return x[1]; - } else { - return x.slice(1); - } + if (x.length == 2 && x[1] != null) { + return x[1]; + } else { + return x.slice(1); + } }; var rest = $b({ - name: 'rest', - alias: 'cdr', - desc: 'Get everything after the first item in a pair or list', - params: [{name: 'x', type: 'pair'}], - fn: _rest + name: 'rest', + alias: 'cdr', + desc: 'Get everything after the first item in a pair or list', + params: [{name: 'x', type: 'pair'}], + fn: _rest }); var list_ref = $b({ - name: 'list_ref', - desc: 'Get the nth item of a list (0-indexed)', - params: [{name: 'lst', type: 'list'}, - {name: 'n', type: 'nat'}], - fn: function(lst, n) { - var array = listToArray(lst); - if (n >= array.length) { + name: 'list_ref', + desc: 'Get the nth item of a list (0-indexed)', + params: [{name: 'lst', type: 'list'}, + {name: 'n', type: 'nat'}], + fn: function(lst, n) { + var array = listToArray(lst); + if (n >= array.length) { throw new Error("Tried to the " + (n+1) + "th item in a list that only contains " + array.length + ' items'); - } else { - return array[n]; - } - } + } else { + return array[n]; + } + } }); var list_elt = $b({ - name: 'list_elt', - desc: 'Get the nth item of a list (1-indexed)', - params: [{name: 'lst', type: 'list'}, - {name: 'n', type: 'nat'}], - fn: function(lst, n) { - if (n < 1) { - throw new Error('The n argument to list-elt should be an integer >= 1'); + name: 'list_elt', + desc: 'Get the nth item of a list (1-indexed)', + params: [{name: 'lst', type: 'list'}, + {name: 'n', type: 'nat'}], + fn: function(lst, n) { + if (n < 1) { + throw new Error('The n argument to list-elt should be an integer >= 1'); + } + return list_ref(lst, n-1); } - return list_ref(lst, n-1); - } }); var take = $b({ - name: 'take', - desc: 'Get the first n items in a list. If there are fewer than n items in the list, returns just the list.', - params: [{name: 'lst', type: 'list'}, - {name: 'n', type: 'nat'} - ], - fn: function(lst,n) { - return arrayToList(listToArray(lst).slice(0,n)); - } + name: 'take', + desc: 'Get the first n items in a list. If there are fewer than n items in the list, returns just the list.', + params: [{name: 'lst', type: 'list'}, + {name: 'n', type: 'nat'} + ], + fn: function(lst,n) { + return arrayToList(listToArray(lst).slice(0,n)); + } }); var drop = $b({ - name: 'drop', - desc: 'Drop the first n items from a list. If there are fewer than n items in the list, return the empty list.', - params: [{name: 'lst', type: 'list'}, - {name: 'n', type: 'nat'}], - fn: function(lst,n) { - return arrayToList(listToArray(lst).slice(n)); - } + name: 'drop', + desc: 'Drop the first n items from a list. If there are fewer than n items in the list, return the empty list.', + params: [{name: 'lst', type: 'list'}, + {name: 'n', type: 'nat'}], + fn: function(lst,n) { + return arrayToList(listToArray(lst).slice(n)); + } }); var sort = $b({ - name: 'sort', - desc: 'Sort a list according to a comparator function cmp(a,b) that returns a number greater than 0 if a > b, 0 if a == b, and a number less than 0 if a < b', - params: [{name: "lst", type: "list"}, - {name: "[cmp]", type: "function", default: ">"}], - fn: function(lst, cmp) { - var arr = listToArray(lst); - var sortedArr; - if (cmp === undefined ) { - sortedArr = arr.sort(); - } else { - sortedArr = arr.sort( cmp ); - } - return arrayToList( sortedArr, true ); + name: 'sort', + desc: 'Sort a list according to a comparator function cmp(a,b) that returns a number greater than 0 if a > b, 0 if a == b, and a number less than 0 if a < b', + params: [{name: "lst", type: "list"}, + {name: "[cmp]", type: "function", default: ">"}], + fn: function(lst, cmp) { + var arr = listToArray(lst); + var sortedArr; + if (cmp === undefined ) { + sortedArr = arr.sort(); + } else { + sortedArr = arr.sort( cmp ); + } + return arrayToList( sortedArr, true ); - } + } }); var unique = $b({ - name: 'unique', - desc: 'Get the unique items in a list', - params: [{name: "lst", type: "list"}, - {name: "[eq]", type: "function", desc: "Optional equality comparison function", default: "equal?"} - ], - fn: function(lst, eq) { - eq = eq || is_equal; - - var arr = listToArray(lst); - var uniques = []; - for(var i = 0, ii = arr.length ; i < ii; i++) { - var v = arr[i]; - var alreadySeen = false; - for(var j = 0, jj = uniques.length; j < jj; j++) { - if (eq(v, uniques[j])) { - alreadySeen = true; - break; + name: 'unique', + desc: 'Get the unique items in a list', + params: [{name: "lst", type: "list"}, + {name: "[eq]", type: "function", desc: "Optional equality comparison function", default: "equal?"} + ], + fn: function(lst, eq) { + eq = eq || is_equal; + + var arr = listToArray(lst); + var uniques = []; + for(var i = 0, ii = arr.length ; i < ii; i++) { + var v = arr[i]; + var alreadySeen = false; + for(var j = 0, jj = uniques.length; j < jj; j++) { + if (eq(v, uniques[j])) { + alreadySeen = true; + break; + } + } + if (!alreadySeen) { + uniques.push(v); + } } - } - if (!alreadySeen) { - uniques.push(v); - } - } - return arrayToList(uniques, true); + return arrayToList(uniques, true); - } + } }); var list_index = $b({ - name: 'list_index', - desc: '', - params: [{name: "lst", type: "list"}, - {name: "x"}], - fn: function(lst, x) { - var arr = listToArray(lst); - return arr.indexOf(x); + name: 'list_index', + desc: '', + params: [{name: "lst", type: "list"}, + {name: "x"}], + fn: function(lst, x) { + var arr = listToArray(lst); + return arr.indexOf(x); - } + } }); var map_at = $b({ - name: 'map_at', - desc: '', - params: [{name: "lst", type: "list"}, - {name: "i", type: "nat"}, - {name: "f", type: "function"}], - fn: function(lst, i, f) { - var arr = listToArray(lst); - arr[i] = f(arr[i]); - return arrayToList(arr, true); + name: 'map_at', + desc: '', + params: [{name: "lst", type: "list"}, + {name: "i", type: "nat"}, + {name: "f", type: "function"}], + fn: function(lst, i, f) { + var arr = listToArray(lst); + arr[i] = f(arr[i]); + return arrayToList(arr, true); - } + } }); var max = $b({ - name: 'max', - desc: 'Maximum of arguments', - params: [{name: "[x ...]", type: "real", desc: ""}], - fn: function(x) { - var args = args_to_array(arguments); - return Math.max.apply(Math, args); + name: 'max', + desc: 'Maximum of arguments', + params: [{name: "[x ...]", type: "real", desc: ""}], + fn: function(x) { + var args = args_to_array(arguments); + return Math.max.apply(Math, args); - } + } }); var min = $b({ - name: 'min', - desc: 'Minimum of arguments', - params: [{name: "[x ...]", type: "real", desc: ""}], - fn: function() { - var args = args_to_array(arguments); - return Math.min.apply(Math, args); + name: 'min', + desc: 'Minimum of arguments', + params: [{name: "[x ...]", type: "real", desc: ""}], + fn: function() { + var args = args_to_array(arguments); + return Math.min.apply(Math, args); - } + } }); var mean = $b({ - name: 'mean', - desc: 'Mean of a list', - params: [{name: "lst", type: "list", desc: ""}], - fn: function(lst) { - var vals = listToArray(lst), - sum = 0, - n = vals.length; + name: 'mean', + desc: 'Mean of a list', + params: [{name: "lst", type: "list", desc: ""}], + fn: function(lst) { + var vals = listToArray(lst), + sum = 0, + n = vals.length; + + for (var i=0; i < n; i++) { + sum += vals[i]; + } + return sum / n; - for (var i=0; i < n; i++) { - sum += vals[i]; } - return sum / n; - - } }); var append = $b({ - name: 'append', - desc: 'Merge an arbitrary number of lists', - params: [ - {name: '[lst ...]', type: 'list'} - ], - fn: function() { - - // not ideal because we're crossing the list abstraction barrier - var r = []; - for(var i = 0, ii = arguments.length; i < ii; i++) { - r = r.concat(listToArray(arguments[i])); + name: 'append', + desc: 'Merge an arbitrary number of lists', + params: [ + {name: '[lst ...]', type: 'list'} + ], + fn: function() { + + // not ideal because we're crossing the list abstraction barrier + var r = []; + for(var i = 0, ii = arguments.length; i < ii; i++) { + r = r.concat(listToArray(arguments[i])); + } + return arrayToList(r, true); } - return arrayToList(r, true); - } }); var flatten = $b({ - name: 'flatten', - desc: '', - params: [{name: "lst", type: "list", desc: ""}], - fn: function(lst) { - var flattened = []; - var arr = listToArray(lst); - for (var i=0, ii = arr.length; i < ii; i++) { - var elem = arr[i]; - if (is_list(elem)) { - flattened = flattened.concat((listToArray(flatten(elem)))); - } else { - flattened.push(elem); - } - } - return arrayToList(flattened); - - } + name: 'flatten', + desc: '', + params: [{name: "lst", type: "list", desc: ""}], + fn: function(lst) { + var flattened = []; + var arr = listToArray(lst); + for (var i=0, ii = arr.length; i < ii; i++) { + var elem = arr[i]; + if (is_list(elem)) { + flattened = flattened.concat((listToArray(flatten(elem)))); + } else { + flattened.push(elem); + } + } + return arrayToList(flattened); + + } }); var fold = $b({ - name: 'fold', - desc: 'Accumulate the result of applying a function to a list', - mathy: "f(lst_0, f(lst_1, f(..., f(lst_n, init)))))", - params: [ - {name: 'f', type: 'function', desc: 'Function to apply'}, - {name: 'init', desc: 'Seed value for function'}, - {name: '[lst ...]', type: 'list', desc: 'List to apply the fold over'} - ], - fn: function(fn, initialValue /*, ... */ ) { - var args = args_to_array(arguments); - - var lists = args.slice(2); - var arrs = []; - for (var i=0; i", desc: ""}], - fn: function(x, alist) { - alist = listToArray(alist); - for (var i=0; i", desc: ""}], + fn: function(x, alist) { + alist = listToArray(alist); + for (var i=0; i", desc: ""}], - fn: function(weights, isStructural, conditionedValue) { - return multinomialDraw( listToArray(iota(weights.length)), - listToArray(weights), - isStructural, conditionedValue); - } + name: 'sample_discrete', + desc: 'Takes a list of weights and samples an index between 0 and (number of weights - 1) with probability proportional to the weights.', + numArgs: [1,3], + params: [{name: "weights", type: "list", desc: ""}], + fn: function(weights, isStructural, conditionedValue) { + return multinomialDraw( listToArray(iota(weights.length)), + listToArray(weights), + isStructural, conditionedValue); + } }) var multi_equals_condition = $b({ - name: 'multi_equals_condition', - desc: '', - numArgs: [3], - params: [{name: "fn", type: "function", desc: ""}, - {name: "n", type: "nat", desc: ""}, - {name: "value", type: "list", desc: ""}], - fn: function (fn, n, values) { - if (values.length != n+1) condition(false); - var marg = enumerateDist(fn); - - try { - var marg = enumerateDist(fn); - } catch (e) { - throw new Error("Function in a repeated condition must be enumerable to be computed"); - } - for (var i=0;i", desc: ""}], - erp: true, - fn: function(lst, probs, conditionedValue) { - if (lst.length != probs.length) { - throw new Error("For multinomial, lists of items and probabilities must be of equal length"); - } - return multinomialDraw(listToArray(lst), listToArray(probs), undefined, conditionedValue); + name: 'wrapped_multinomial', + desc: 'Sample an element from lst with the probability specified in probs', + numArgs: [2,3], + params: [{name: "lst", type: "list", desc: ""}, + {name: "probs", type: "list", desc: ""}], + erp: true, + fn: function(lst, probs, conditionedValue) { + if (lst.length != probs.length) { + throw new Error("For multinomial, lists of items and probabilities must be of equal length"); + } + return multinomialDraw(listToArray(lst), listToArray(probs), undefined, conditionedValue); - } + } }); // TODO: make sure p is less than 1 var wrapped_flip = $b({ - name: 'wrapped_flip', - desc: 'Flip a weighted coin. Returns true or false', - numArgs: [0,1,2], - params: [{name: "[p]", type: "real", desc: "", default: "0.5"}, - {name: "[conditionedValue]", type: "", desc: "", noexport: true} - ], - erp: true, - fn: function(p, conditionedValue) { - return flip(p, undefined, conditionedValue) == 1; + name: 'wrapped_flip', + desc: 'Flip a weighted coin. Returns true or false', + numArgs: [0,1,2], + params: [{name: "[p]", type: "real", desc: "", default: "0.5"}, + {name: "[conditionedValue]", type: "", desc: "", noexport: true} + ], + erp: true, + fn: function(p, conditionedValue) { + return flip(p, undefined, conditionedValue) == 1; - } + } }); var wrapped_uniform = $b({ - name: 'wrapped_uniform', - desc: 'Sample a random real uniformly from the interval [a,b]', - numArgs: [2,3], - params: [{name: "a", type: "real", desc: ""}, - {name: "b", type: "real", desc: ""}, - {name: "[conditionedValue]", type: "", desc: "", noexport: true} - ], - erp: true, - fn: function(a, b, conditionedValue) { - return uniform(a, b, undefined, conditionedValue); + name: 'wrapped_uniform', + desc: 'Sample a random real uniformly from the interval [a,b]', + numArgs: [2,3], + params: [{name: "a", type: "real", desc: ""}, + {name: "b", type: "real", desc: ""}, + {name: "[conditionedValue]", type: "", desc: "", noexport: true} + ], + erp: true, + fn: function(a, b, conditionedValue) { + return uniform(a, b, undefined, conditionedValue); - } + } }); var wrapped_random_integer = $b({ - name: 'wrapped_random_integer', - desc: '', - alias: ['random-integer','sample-integer'], - numArgs: [1,2], - params: [{name: "n", type: "nat", desc: ""}, - {name: "[conditionedValue]", type: "", desc: "", noexport: true} - ], - erp: true, - fn: function(n, conditionedValue) { - var probs = [], p = 1/n; - for (var i = 0; i < n; i++){ - probs[i] = p; - }; - return multinomial(probs, undefined, conditionedValue); - } + name: 'wrapped_random_integer', + desc: '', + alias: ['random-integer','sample-integer'], + numArgs: [1,2], + params: [{name: "n", type: "nat", desc: ""}, + {name: "[conditionedValue]", type: "", desc: "", noexport: true} + ], + erp: true, + fn: function(n, conditionedValue) { + var probs = [], p = 1/n; + for (var i = 0; i < n; i++){ + probs[i] = p; + }; + return multinomial(probs, undefined, conditionedValue); + } }); var wrapped_gaussian = $b({ - name: 'wrapped_gaussian', - desc: 'Sample from the Gaussian distribution N(mu, sigma)', - numArgs: [0,1,2,3], - params: [{name: "[mu]", type: "real", desc: "", default: 0}, - {name: "[sigma]", type: "real", desc: "", default: 1}, - {name: "[isStructural]", type: "", desc: "", noexport: true}, - {name: "[conditionedValue]", type: "", desc: "", noexport: true} - ], - erp: true, - fn: function(mu, sigma, conditionedValue) { - mu = mu || 0; - sigma = sigma || 1; - return gaussian(mu, sigma, undefined, conditionedValue); - } + name: 'wrapped_gaussian', + desc: 'Sample from the Gaussian distribution N(mu, sigma)', + numArgs: [0,1,2,3], + params: [{name: "[mu]", type: "real", desc: "", default: 0}, + {name: "[sigma]", type: "real", desc: "", default: 1}, + {name: "[isStructural]", type: "", desc: "", noexport: true}, + {name: "[conditionedValue]", type: "", desc: "", noexport: true} + ], + erp: true, + fn: function(mu, sigma, conditionedValue) { + mu = mu || 0; + sigma = sigma || 1; + return gaussian(mu, sigma, undefined, conditionedValue); + } }); var wrapped_gamma = $b({ - name: 'wrapped_gamma', - desc: 'Sample from the gamma distribution G(a,b)', - numArgs: [2,3], - params: [{name: "a", type: "real", desc: ""}, - {name: "b", type: "real", desc: ""}, - {name: "[conditionedValue]", type: "", desc: "", noexport: true}], - erp: true, - fn: function(a, b, conditionedValue) { - return gamma(a, b, undefined, conditionedValue); - } + name: 'wrapped_gamma', + desc: 'Sample from the gamma distribution G(a,b)', + numArgs: [2,3], + params: [{name: "a", type: "real", desc: ""}, + {name: "b", type: "real", desc: ""}, + {name: "[conditionedValue]", type: "", desc: "", noexport: true}], + erp: true, + fn: function(a, b, conditionedValue) { + return gamma(a, b, undefined, conditionedValue); + } }); var wrapped_beta = $b({ - name: 'wrapped_beta', - desc: 'Sample from the beta distribution B(a,b). Returns only the first element.', - numArgs: [2,3], - params: [{name: "a", type: "positive real", desc: ""}, - {name: "b", type: "positive real", desc: ""}, - {name: "[conditionedValue]", type: "", desc: "", noexport: true}], - erp: true, - fn: function(a, b, conditionedValue) { - if (a <= 0) { - throw new Error('The a argument to beta must be greater than 0'); - } - if (b <= 0) { - throw new Error('The b argument to beta must be greater than 0'); - } - return beta(a, b, undefined, conditionedValue); + name: 'wrapped_beta', + desc: 'Sample from the beta distribution B(a,b). Returns only the first element.', + numArgs: [2,3], + params: [{name: "a", type: "positive real", desc: ""}, + {name: "b", type: "positive real", desc: ""}, + {name: "[conditionedValue]", type: "", desc: "", noexport: true}], + erp: true, + fn: function(a, b, conditionedValue) { + if (a <= 0) { + throw new Error('The a argument to beta must be greater than 0'); + } + if (b <= 0) { + throw new Error('The b argument to beta must be greater than 0'); + } + return beta(a, b, undefined, conditionedValue); - } + } }); var wrapped_dirichlet = $b({ - name: 'wrapped_dirichlet', - desc: 'Sample from the Dirichlet distribution Dir(alpha).', - numArgs: [1,2], - params: [{name: "alpha", type: "list", desc: ""}, - {name: "[conditionedValue]", type: "", desc: "", noexport: true}], - erp: true, - fn: function(alpha, conditionedValue) { - alpha = listToArray(alpha); - return arrayToList(dirichlet(alpha, undefined, conditionedValue)); + name: 'wrapped_dirichlet', + desc: 'Sample from the Dirichlet distribution Dir(alpha).', + numArgs: [1,2], + params: [{name: "alpha", type: "list", desc: ""}, + {name: "[conditionedValue]", type: "", desc: "", noexport: true}], + erp: true, + fn: function(alpha, conditionedValue) { + alpha = listToArray(alpha); + return arrayToList(dirichlet(alpha, undefined, conditionedValue)); - } + } }); var DPmem = $b({ - name: 'DPmem', - desc: 'Stochastic memoization using the Dirichlet Process', - params: [{name: 'alpha', type: 'real', desc: 'Concentration parameter of the DP'}, - {name: 'f', type: 'function', desc: 'Function to stochastically memoize'} - ], - fn: function(alpha, f) { - var restaurants = {}; - return function() { - var args = args_to_array(arguments); - var restaurantId = JSON.stringify(args); - - var tables = restaurants[restaurantId]; - var numTables; - - if (tables === undefined) { - numTables = 0; - tables = restaurants[restaurantId] = []; - } else { - numTables = tables.length; - } - - var value; - - // no tables yet or we sample a new one - if (numTables == 0 || wrapped_flip(alpha / (numTables + alpha))) { - value = f.apply(null, arguments); - // store both the count and the un-serialized value - // (so we don't have to run JSON.parse if we later reuse it) - //tables[JSON.stringify(value)] = {count: 1, value: value}; - tables.push({value: value, count: 1}); - } - // reuse existing table - else { - - // construct a multinomial over current tables - var indices = []; - for(var i = 0; i < numTables; i++ ) { - indices.push(i); - } + name: 'DPmem', + desc: 'Stochastic memoization using the Dirichlet Process', + params: [{name: 'alpha', type: 'real', desc: 'Concentration parameter of the DP'}, + {name: 'f', type: 'function', desc: 'Function to stochastically memoize'} + ], + fn: function(alpha, f) { + var restaurants = {}; + return function() { + var args = args_to_array(arguments); + var restaurantId = JSON.stringify(args); + + var tables = restaurants[restaurantId]; + var numTables; + + if (tables === undefined) { + numTables = 0; + tables = restaurants[restaurantId] = []; + } else { + numTables = tables.length; + } + + var value; - var counts = tables.map(function(table) { return table.count }); + // no tables yet or we sample a new one + if (numTables == 0 || wrapped_flip(alpha / (numTables + alpha))) { + value = f.apply(null, arguments); + // store both the count and the un-serialized value + // (so we don't have to run JSON.parse if we later reuse it) + //tables[JSON.stringify(value)] = {count: 1, value: value}; + tables.push({value: value, count: 1}); + } + // reuse existing table + else { - var sampledIndex = wrapped_multinomial(arrayToList(indices, true), - arrayToList(counts, true)); + // construct a multinomial over current tables + var indices = []; + for(var i = 0; i < numTables; i++ ) { + indices.push(i); + } + var counts = tables.map(function(table) { return table.count }); - value = tables[sampledIndex].value; - tables[sampledIndex].count++; - } - return value; + var sampledIndex = wrapped_multinomial(arrayToList(indices, true), + arrayToList(counts, true)); + value = tables[sampledIndex].value; + tables[sampledIndex].count++; + } + return value; + + } } - } }) var wrapped_conditional = $b({ - name: 'wrapped_conditional', - desc: '', - params: [{name: 'comp', type: 'function', desc: ''}, - {name: 'params', type: 'list', - desc: 'List where the first element is the sampling strategy, ' + - 'one of ("enumeration", "rejection, "mh"), if "mh", second element is the lag' - }], - fn: function(comp, params) { - var options = {}; - if (params[0] == "enumeration") { - options.algorithm = "enumerate"; - } else if (params[0] == "mh") { - options.algorithm = "traceMH"; - options.lag = params[1]; - } - return conditional(comp, options); - } + name: 'wrapped_conditional', + desc: '', + params: [{name: 'comp', type: 'function', desc: ''}, + {name: 'params', type: 'list', + desc: 'List where the first element is the sampling strategy, ' + + 'one of ("enumeration", "rejection, "mh"), if "mh", second element is the lag' + }], + fn: function(comp, params) { + var options = {}; + if (params[0] == "enumeration") { + options.algorithm = "enumerate"; + } else if (params[0] == "mh") { + options.algorithm = "traceMH"; + options.lag = params[1]; + } + return conditional(comp, options); + } }); // TODO: try to provide better error handling if // numsamps / lag is not provided. might have to fix this // inside js_astify var wrapped_mh_query = $b({ - name: 'wrapped_mh_query', - desc: '', - params: [{name: 'comp'}, - {name: 'samples', type: 'nat'}, - {name: 'lag', type: 'nat'} - ], - fn: function(comp, samples, lag) { - var inn = traceMH(comp, samples, lag, false, "lessdumb").map(function(x) {return x.sample}); - var res = arrayToList(inn); - return res; + name: 'wrapped_mh_query', + desc: '', + params: [{name: 'comp'}, + {name: 'samples', type: 'nat'}, + {name: 'lag', type: 'nat'} + ], + fn: function(comp, samples, lag) { + var inn = traceMH(comp, samples, lag, false, "lessdumb").map(function(x) {return x.sample}); + var res = arrayToList(inn); + return res; - } + } }); var wrapped_rejection_query = $b({ - name: 'wrapped_rejection_query', - desc: '', - params: [{name: 'comp'}], - fn: function(comp) { - return rejectionSample(comp); - } + name: 'wrapped_rejection_query', + desc: '', + params: [{name: 'comp'}], + fn: function(comp) { + return rejectionSample(comp); + } }); var wrapped_enumeration_query = $b({ - name: 'wrapped_enumeration_query', - desc: '', - params: [{name: 'comp'}], - fn: function(comp) { - var d = enumerateDist(comp); - var p=[],v=[]; - var norm = 0; - for (var x in d) { - p.push(d[x].prob); - v.push(d[x].val); - norm += d[x].prob; - } - var res = List(arrayToList(v, true), - arrayToList(p.map(function(x){return x/norm}), true)); - return res; - - } + name: 'wrapped_enumeration_query', + desc: '', + params: [{name: 'comp'}], + fn: function(comp) { + var d = enumerateDist(comp); + var p=[],v=[]; + var norm = 0; + for (var x in d) { + p.push(d[x].prob); + v.push(d[x].val); + norm += d[x].prob; + } + var res = List(arrayToList(v, true), + arrayToList(p.map(function(x){return x/norm}), true)); + return res; + + } }); var read_file = $b({ - name: 'read_file', - desc: '', - params: [{name: "fileName", type: "string", desc: ""}], - fn: function(fileName) { - return fs.readFileSync(fileName, "utf8"); + name: 'read_file', + desc: '', + params: [{name: "fileName", type: "string", desc: ""}], + fn: function(fileName) { + return fs.readFileSync(fileName, "utf8"); - } + } }); // CSV stuff follows RFC4180 (http://tools.ietf.org/html/rfc4180) // - in double quote-enclosed fields, double quotes are escaped with another double quote var read_csv = $b({ - name: 'read_csv', - desc: '', - params: [{name: "fileName", type: "string", desc: ""}, - {name: "[sep]", type: "string", desc: ""}], - fn: function(fileName, sep) { - sep = sep || ","; - if (sep.indexOf('"') != -1) throw new Error("CSV separator cannot contain a double quote"); - var text = fs.readFileSync(fileName, "utf8"); - var data = []; - var row = []; - var begin = 0; - var i = 0; - var j = 0; - var cell; - while (i= text.length) throw new Error("Malformed CSV file"); - } - j++; - cell = text.slice(i+1, j-1).replace(/""/g, '"'); - } else { - for (; j < text.length && text.slice(j, j + sep.length) != sep && text[j] != "\n"; j++) { - if (text[j] == '"') throw new Error("Malformed CSV file"); + name: 'read_csv', + desc: '', + params: [{name: "fileName", type: "string", desc: ""}, + {name: "[sep]", type: "string", desc: ""}], + fn: function(fileName, sep) { + sep = sep || ","; + if (sep.indexOf('"') != -1) throw new Error("CSV separator cannot contain a double quote"); + var text = fs.readFileSync(fileName, "utf8"); + var data = []; + var row = []; + var begin = 0; + var i = 0; + var j = 0; + var cell; + while (i= text.length) throw new Error("Malformed CSV file"); + } + j++; + cell = text.slice(i+1, j-1).replace(/""/g, '"'); + } else { + for (; j < text.length && text.slice(j, j + sep.length) != sep && text[j] != "\n"; j++) { + if (text[j] == '"') throw new Error("Malformed CSV file"); + } + cell = text.slice(i, j); + } + row.push(cell); + if (j >= text.length || text[j] == "\n") { + data.push(arrayToList(row, true)); + row = []; + j++; + } else if (text.slice(j, j + sep.length) == sep) { + j += sep.length; + } else { + // Only reached if cell was quoted but not properly closed + throw new Error("Malformed CSV file"); + } + i = j; } - cell = text.slice(i, j); - } - row.push(cell); - if (j >= text.length || text[j] == "\n") { - data.push(arrayToList(row, true)); - row = []; - j++; - } else if (text.slice(j, j + sep.length) == sep) { - j += sep.length; - } else { - // Only reached if cell was quoted but not properly closed - throw new Error("Malformed CSV file"); - } - i = j; - } - return arrayToList(data, true); - } + return arrayToList(data, true); + } }); var write_csv = $b({ - name: 'write_csv', - desc: '', - params: [{name: "data", type: "list", desc: ""}, - {name: "fileName", type: "string", desc: ""}, - {name: "[sep]", type: "string", desc: ""}], - fn: function(data, fileName, sep) { - sep = sep || ","; - var stream = fs.createWriteStream(fileName); - for (var i=0;i", desc: ""}, + {name: "fileName", type: "string", desc: ""}, + {name: "[sep]", type: "string", desc: ""}], + fn: function(data, fileName, sep) { + sep = sep || ","; + var stream = fs.createWriteStream(fileName); + for (var i=0;i>'); @@ -1758,94 +1753,93 @@ var sample = $b({ // TODO: add a flag somewhere for turning on/off wrapping function wrapAsserts(annotation) { - var fnName = annotation.name; - var fn = annotation.fn; - var paramProps = annotation.params || []; - - var validArgumentLengths = annotation.numArgs; - - var numParams = paramProps.length; - - // compute number of mandatory arguments - var numMandatoryParams = paramProps.filter(function(prop) { - return !prop.name.match(/\[/); - }).length; - - var wrapped = function() { - // var userArgs = Array.prototype.slice.call(arguments, 0); - var userArgs = arguments; - - var userNumArgs = userArgs.length; - // console.log( 'inside wrapped ' + functionName); - - if (userNumArgs < numMandatoryParams) { - var err = _.template('<> takes {{numArgs}} argument{{plural}}, but {{userNumArgs}} were given', - {userNumArgs: userNumArgs == 0 ? 'none' : 'only ' + userNumArgs, - numArgs: ((numParams == numMandatoryParams) ? '' : '(at least) ') + numMandatoryParams, - plural: numMandatoryParams == 1 ? '' : 's' - } - ); - throw new Error(err); - } - - // make sure that the number of arguments that the - // user supplied is a valid number of arguments - // to this function - if (validArgumentLengths) { - if (validArgumentLengths.indexOf(userNumArgs) == -1) { - throw new Error('Invalid number of arguments to <>'); - } - } - - // for each supplied argument, check type - for(var i = 0, a, props, variadic = false, specType, argName; i < userNumArgs; i++) { - - a = userArgs[i]; - if (!variadic) { - props = paramProps[i]; - specType = props.type; - } - argName = props.name; - if (argName.match(/\.\.\./)) { - variadic = true; - } - - if (specType) { - // run the appropriate type checker on the argument - var checker = parseTypeString(specType); // typeCheckers[specType]; - - if (checker === undefined) { - var errorString = _.template( - 'Bug in Church builtins - annotation for (<> ...) tries to declare the type of the "{{argName}}" argument as "{{specType}}", which is not a recognized type', - { specType: specType, - argName: argName + var fnName = annotation.name; + var fn = annotation.fn; + var paramProps = annotation.params || []; + + var validArgumentLengths = annotation.numArgs; + + var numParams = paramProps.length; + + // compute number of mandatory arguments + var numMandatoryParams = paramProps.filter(function(prop) { + return !prop.name.match(/\[/); + }).length; + + var wrapped = function() { + // var userArgs = Array.prototype.slice.call(arguments, 0); + var userArgs = arguments; + + var userNumArgs = userArgs.length; + // console.log( 'inside wrapped ' + functionName); + + if (userNumArgs < numMandatoryParams) { + var err = _.template('<> takes {{numArgs}} argument{{plural}}, but {{userNumArgs}} were given', + {userNumArgs: userNumArgs == 0 ? 'none' : 'only ' + userNumArgs, + numArgs: ((numParams == numMandatoryParams) ? '' : '(at least) ') + numMandatoryParams, + plural: numMandatoryParams == 1 ? '' : 's' + } + ); + throw new Error(err); + } + + // make sure that the number of arguments that the + // user supplied is a valid number of arguments + // to this function + if (validArgumentLengths) { + if (validArgumentLengths.indexOf(userNumArgs) == -1) { + throw new Error('Invalid number of arguments to <>'); } - ); - throw new Error(errorString); } - var typeChecks = checker(a); + // for each supplied argument, check type + for(var i = 0, a, props, variadic = false, specType, argName; i < userNumArgs; i++) { - if (!typeChecks) { - var errorString = _.template( - // <> will get filled in inside evaluate.js - '{{argName}} to (<> ...) should be a {{specType}}, not a {{userType}}', - { - userType: typeof a, - specType: specType, - argName: variadic ? 'Argument' : 'The ' + argName + ' argument' + a = userArgs[i]; + if (!variadic) { + props = paramProps[i]; + specType = props.type; + } + argName = props.name; + if (argName.match(/\.\.\./)) { + variadic = true; } - ); - throw new Error(errorString); + if (specType) { + // run the appropriate type checker on the argument + var checker = parseTypeString(specType); // typeCheckers[specType]; + + if (checker === undefined) { + var errorString = _.template( + 'Bug in Church builtins - annotation for (<> ...) tries to declare the type of the "{{argName}}" argument as "{{specType}}", which is not a recognized type', + { specType: specType, + argName: argName + } + ); + throw new Error(errorString); + } + + var typeChecks = checker(a); + + if (!typeChecks) { + var errorString = _.template( + // <> will get filled in inside evaluate.js + '{{argName}} to (<> ...) should be a {{specType}}, not a {{userType}}', + { + userType: typeof a, + specType: specType, + argName: variadic ? 'Argument' : 'The ' + argName + ' argument' + } + ); + + throw new Error(errorString); + } + } } - } - } - return fn.apply(null, userArgs); - }; - wrapped.num_args = annotation.numArgs; - - return wrapped; - // return fn; + return fn.apply(null, userArgs); + }; + wrapped.num_args = annotation.numArgs; + return wrapped; + // return fn; } diff --git a/cm-brackets.js b/cm-brackets.js index ac21a8f..2ce525a 100644 --- a/cm-brackets.js +++ b/cm-brackets.js @@ -7,250 +7,250 @@ var CodeMirror = require('codemirror'); // ----------------------------------- (function() { - var DEFAULT_BRACKETS = "()[]{}\"\""; - var DEFAULT_EXPLODE_ON_ENTER = "[]{}"; - var SPACE_CHAR_REGEX = /\s/; + var DEFAULT_BRACKETS = "()[]{}\"\""; + var DEFAULT_EXPLODE_ON_ENTER = "[]{}"; + var SPACE_CHAR_REGEX = /\s/; - var Pos = CodeMirror.Pos; + var Pos = CodeMirror.Pos; - CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) { - if (old != CodeMirror.Init && old) - cm.removeKeyMap("autoCloseBrackets"); - if (!val) return; - var pairs = DEFAULT_BRACKETS, explode = DEFAULT_EXPLODE_ON_ENTER; - if (typeof val == "string") pairs = val; - else if (typeof val == "object") { - if (val.pairs != null) pairs = val.pairs; - if (val.explode != null) explode = val.explode; + CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) { + if (old != CodeMirror.Init && old) + cm.removeKeyMap("autoCloseBrackets"); + if (!val) return; + var pairs = DEFAULT_BRACKETS, explode = DEFAULT_EXPLODE_ON_ENTER; + if (typeof val == "string") pairs = val; + else if (typeof val == "object") { + if (val.pairs != null) pairs = val.pairs; + if (val.explode != null) explode = val.explode; + } + var map = buildKeymap(pairs); + if (explode) map.Enter = buildExplodeHandler(explode); + cm.addKeyMap(map); + }); + + function charsAround(cm, pos) { + var str = cm.getRange(Pos(pos.line, pos.ch - 1), + Pos(pos.line, pos.ch + 1)); + return str.length == 2 ? str : null; } - var map = buildKeymap(pairs); - if (explode) map.Enter = buildExplodeHandler(explode); - cm.addKeyMap(map); - }); - function charsAround(cm, pos) { - var str = cm.getRange(Pos(pos.line, pos.ch - 1), - Pos(pos.line, pos.ch + 1)); - return str.length == 2 ? str : null; - } + function buildKeymap(pairs) { + var map = { + name : "autoCloseBrackets", + Backspace: function(cm) { + if (cm.getOption("disableInput")) return CodeMirror.Pass; + var ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) { + if (!ranges[i].empty()) return CodeMirror.Pass; + var around = charsAround(cm, ranges[i].head); + if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass; + } + for (var i = ranges.length - 1; i >= 0; i--) { + var cur = ranges[i].head; + cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1)); + } + } + }; + var closingBrackets = ""; + for (var i = 0; i < pairs.length; i += 2) (function(left, right) { + if (left != right) closingBrackets += right; + map["'" + left + "'"] = function(cm) { + if (cm.getOption("disableInput")) return CodeMirror.Pass; + var ranges = cm.listSelections(), type, next; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i], cur = range.head, curType; + if (left == "'" && cm.getTokenTypeAt(cur) == "comment") + return CodeMirror.Pass; + var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1)); + if (!range.empty()) + curType = "surround"; + else if (left == right && next == right) { + if (cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == left + left + left) + curType = "skipThree"; + else + curType = "skip"; + } else if (left == right && cur.ch > 1 && + cm.getRange(Pos(cur.line, cur.ch - 2), cur) == left + left && + (cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != left)) + curType = "addFour"; + else if (left == right && CodeMirror.isWordChar(next)) + return CodeMirror.Pass; + else if (cm.getLine(cur.line).length == cur.ch || closingBrackets.indexOf(next) >= 0 || SPACE_CHAR_REGEX.test(next)) + curType = "both"; + else + return CodeMirror.Pass; + if (!type) type = curType; + else if (type != curType) return CodeMirror.Pass; + } - function buildKeymap(pairs) { - var map = { - name : "autoCloseBrackets", - Backspace: function(cm) { - if (cm.getOption("disableInput")) return CodeMirror.Pass; - var ranges = cm.listSelections(); - for (var i = 0; i < ranges.length; i++) { - if (!ranges[i].empty()) return CodeMirror.Pass; - var around = charsAround(cm, ranges[i].head); - if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass; - } - for (var i = ranges.length - 1; i >= 0; i--) { - var cur = ranges[i].head; - cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1)); - } - } - }; - var closingBrackets = ""; - for (var i = 0; i < pairs.length; i += 2) (function(left, right) { - if (left != right) closingBrackets += right; - map["'" + left + "'"] = function(cm) { - if (cm.getOption("disableInput")) return CodeMirror.Pass; - var ranges = cm.listSelections(), type, next; - for (var i = 0; i < ranges.length; i++) { - var range = ranges[i], cur = range.head, curType; - if (left == "'" && cm.getTokenTypeAt(cur) == "comment") - return CodeMirror.Pass; - var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1)); - if (!range.empty()) - curType = "surround"; - else if (left == right && next == right) { - if (cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == left + left + left) - curType = "skipThree"; - else - curType = "skip"; - } else if (left == right && cur.ch > 1 && - cm.getRange(Pos(cur.line, cur.ch - 2), cur) == left + left && - (cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != left)) - curType = "addFour"; - else if (left == right && CodeMirror.isWordChar(next)) - return CodeMirror.Pass; - else if (cm.getLine(cur.line).length == cur.ch || closingBrackets.indexOf(next) >= 0 || SPACE_CHAR_REGEX.test(next)) - curType = "both"; - else - return CodeMirror.Pass; - if (!type) type = curType; - else if (type != curType) return CodeMirror.Pass; - } - - cm.operation(function() { - if (type == "skip") { - cm.execCommand("goCharRight"); - } else if (type == "skipThree") { - for (var i = 0; i < 3; i++) - cm.execCommand("goCharRight"); - } else if (type == "surround") { - var sels = cm.getSelections(); - for (var i = 0; i < sels.length; i++) - sels[i] = left + sels[i] + right; - cm.replaceSelections(sels, "around"); - } else if (type == "both") { - cm.replaceSelection(left + right, null); - cm.execCommand("goCharLeft"); - } else if (type == "addFour") { - cm.replaceSelection(left + left + left + left, "before"); - cm.execCommand("goCharRight"); - } - }); - }; - if (left != right) map["'" + right + "'"] = function(cm) { - var ranges = cm.listSelections(); - for (var i = 0; i < ranges.length; i++) { - var range = ranges[i]; - if (!range.empty() || - cm.getRange(range.head, Pos(range.head.line, range.head.ch + 1)) != right) - return CodeMirror.Pass; - } - cm.execCommand("goCharRight"); - }; - })(pairs.charAt(i), pairs.charAt(i + 1)); - return map; - } + cm.operation(function() { + if (type == "skip") { + cm.execCommand("goCharRight"); + } else if (type == "skipThree") { + for (var i = 0; i < 3; i++) + cm.execCommand("goCharRight"); + } else if (type == "surround") { + var sels = cm.getSelections(); + for (var i = 0; i < sels.length; i++) + sels[i] = left + sels[i] + right; + cm.replaceSelections(sels, "around"); + } else if (type == "both") { + cm.replaceSelection(left + right, null); + cm.execCommand("goCharLeft"); + } else if (type == "addFour") { + cm.replaceSelection(left + left + left + left, "before"); + cm.execCommand("goCharRight"); + } + }); + }; + if (left != right) map["'" + right + "'"] = function(cm) { + var ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + if (!range.empty() || + cm.getRange(range.head, Pos(range.head.line, range.head.ch + 1)) != right) + return CodeMirror.Pass; + } + cm.execCommand("goCharRight"); + }; + })(pairs.charAt(i), pairs.charAt(i + 1)); + return map; + } - function buildExplodeHandler(pairs) { - return function(cm) { - if (cm.getOption("disableInput")) return CodeMirror.Pass; - var ranges = cm.listSelections(); - for (var i = 0; i < ranges.length; i++) { - if (!ranges[i].empty()) return CodeMirror.Pass; - var around = charsAround(cm, ranges[i].head); - if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass; - } - cm.operation(function() { - cm.replaceSelection("\n\n", null); - cm.execCommand("goCharLeft"); - ranges = cm.listSelections(); - for (var i = 0; i < ranges.length; i++) { - var line = ranges[i].head.line; - cm.indentLine(line, null, true); - cm.indentLine(line + 1, null, true); - } - }); - }; - } + function buildExplodeHandler(pairs) { + return function(cm) { + if (cm.getOption("disableInput")) return CodeMirror.Pass; + var ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) { + if (!ranges[i].empty()) return CodeMirror.Pass; + var around = charsAround(cm, ranges[i].head); + if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass; + } + cm.operation(function() { + cm.replaceSelection("\n\n", null); + cm.execCommand("goCharLeft"); + ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) { + var line = ranges[i].head.line; + cm.indentLine(line, null, true); + cm.indentLine(line + 1, null, true); + } + }); + }; + } })(); -// matchbrackets.js +// matchbrackets.js (function() { - var ie_lt8 = /MSIE \d/.test(navigator.userAgent) && - (document.documentMode == null || document.documentMode < 8); + var ie_lt8 = /MSIE \d/.test(navigator.userAgent) && + (document.documentMode == null || document.documentMode < 8); - var Pos = CodeMirror.Pos; + var Pos = CodeMirror.Pos; - var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; + var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; - function findMatchingBracket(cm, where, strict, config) { - var line = cm.getLineHandle(where.line), pos = where.ch - 1; - var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; - if (!match) return null; - var dir = match.charAt(1) == ">" ? 1 : -1; - if (strict && (dir > 0) != (pos == where.ch)) return null; - var style = cm.getTokenTypeAt(Pos(where.line, pos + 1)); + function findMatchingBracket(cm, where, strict, config) { + var line = cm.getLineHandle(where.line), pos = where.ch - 1; + var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; + if (!match) return null; + var dir = match.charAt(1) == ">" ? 1 : -1; + if (strict && (dir > 0) != (pos == where.ch)) return null; + var style = cm.getTokenTypeAt(Pos(where.line, pos + 1)); - var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config); - if (found == null) return null; - return {from: Pos(where.line, pos), to: found && found.pos, - match: found && found.ch == match.charAt(0), forward: dir > 0}; - } + var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config); + if (found == null) return null; + return {from: Pos(where.line, pos), to: found && found.pos, + match: found && found.ch == match.charAt(0), forward: dir > 0}; + } - // bracketRegex is used to specify which type of bracket to scan - // should be a regexp, e.g. /[[\]]/ - // - // Note: If "where" is on an open bracket, then this bracket is ignored. - // - // Returns false when no bracket was found, null when it reached - // maxScanLines and gave up - function scanForBracket(cm, where, dir, style, config) { - var maxScanLen = (config && config.maxScanLineLength) || 10000; - var maxScanLines = (config && config.maxScanLines) || 1000; + // bracketRegex is used to specify which type of bracket to scan + // should be a regexp, e.g. /[[\]]/ + // + // Note: If "where" is on an open bracket, then this bracket is ignored. + // + // Returns false when no bracket was found, null when it reached + // maxScanLines and gave up + function scanForBracket(cm, where, dir, style, config) { + var maxScanLen = (config && config.maxScanLineLength) || 10000; + var maxScanLines = (config && config.maxScanLines) || 1000; - var stack = []; - var re = config && config.bracketRegex ? config.bracketRegex : /[(){}[\]]/; - var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1) - : Math.max(cm.firstLine() - 1, where.line - maxScanLines); - for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) { - var line = cm.getLine(lineNo); - if (!line) continue; - var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1; - if (line.length > maxScanLen) continue; - if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0); - for (; pos != end; pos += dir) { - var ch = line.charAt(pos); - if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) { - var match = matching[ch]; - if ((match.charAt(1) == ">") == (dir > 0)) stack.push(ch); - else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch}; - else stack.pop(); + var stack = []; + var re = config && config.bracketRegex ? config.bracketRegex : /[(){}[\]]/; + var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1) + : Math.max(cm.firstLine() - 1, where.line - maxScanLines); + for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) { + var line = cm.getLine(lineNo); + if (!line) continue; + var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1; + if (line.length > maxScanLen) continue; + if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0); + for (; pos != end; pos += dir) { + var ch = line.charAt(pos); + if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) { + var match = matching[ch]; + if ((match.charAt(1) == ">") == (dir > 0)) stack.push(ch); + else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch}; + else stack.pop(); + } + } } - } + return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null; } - return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null; - } - function matchBrackets(cm, autoclear, config) { - // Disable brace matching in long lines, since it'll cause hugely slow updates - var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000; - var marks = [], ranges = cm.listSelections(); - for (var i = 0; i < ranges.length; i++) { - var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, false, config); - if (match && cm.getLine(match.from.line).length <= maxHighlightLen) { - var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; - marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style})); - if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen) - marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style})); - } - } + function matchBrackets(cm, autoclear, config) { + // Disable brace matching in long lines, since it'll cause hugely slow updates + var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000; + var marks = [], ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) { + var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, false, config); + if (match && cm.getLine(match.from.line).length <= maxHighlightLen) { + var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; + marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style})); + if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen) + marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style})); + } + } + + if (marks.length) { + // Kludge to work around the IE bug from issue #1193, where text + // input stops going to the textare whever this fires. + if (ie_lt8 && cm.state.focused) cm.display.input.focus(); - if (marks.length) { - // Kludge to work around the IE bug from issue #1193, where text - // input stops going to the textare whever this fires. - if (ie_lt8 && cm.state.focused) cm.display.input.focus(); + var clear = function() { + cm.operation(function() { + for (var i = 0; i < marks.length; i++) marks[i].clear(); + }); + }; + if (autoclear) setTimeout(clear, 800); + else return clear; + } + } - var clear = function() { + var currentlyHighlighted = null; + function doMatchBrackets(cm) { cm.operation(function() { - for (var i = 0; i < marks.length; i++) marks[i].clear(); + if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;} + currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets); }); - }; - if (autoclear) setTimeout(clear, 800); - else return clear; } - } - var currentlyHighlighted = null; - function doMatchBrackets(cm) { - cm.operation(function() { - if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;} - currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets); + CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) + cm.off("cursorActivity", doMatchBrackets); + if (val) { + cm.state.matchBrackets = typeof val == "object" ? val : {}; + cm.on("cursorActivity", doMatchBrackets); + } }); - } - CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { - if (old && old != CodeMirror.Init) - cm.off("cursorActivity", doMatchBrackets); - if (val) { - cm.state.matchBrackets = typeof val == "object" ? val : {}; - cm.on("cursorActivity", doMatchBrackets); - } - }); - - CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);}); - CodeMirror.defineExtension("findMatchingBracket", function(pos, strict, config){ - return findMatchingBracket(this, pos, strict, config); - }); - CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){ - return scanForBracket(this, pos, dir, style, config); - }); + CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);}); + CodeMirror.defineExtension("findMatchingBracket", function(pos, strict, config){ + return findMatchingBracket(this, pos, strict, config); + }); + CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){ + return scanForBracket(this, pos, dir, style, config); + }); })() diff --git a/cm-church.js b/cm-church.js index ab17372..0a1e267 100644 --- a/cm-church.js +++ b/cm-church.js @@ -15,7 +15,7 @@ CodeMirror.defineMode("scheme", function () { return obj; } - var keywords = makeKeywords("λ case-lambda call/cc class define-class exit-handler field import inherit init-field interface let*-values let-values let/ec mixin opt-lambda override protect provide public rename require require-for-syntax syntax syntax-case syntax-error unit/sig unless when with-syntax and begin call-with-current-continuation call-with-input-file call-with-output-file case cond define define-syntax delay do dynamic-wind else for-each if lambda let let* let-syntax letrec letrec-syntax map or syntax-rules abs acos angle append apply asin assoc assq assv atan boolean? caar cadr call-with-input-file call-with-output-file call-with-values car cdddar cddddr cdr ceiling char->integer char-alphabetic? char-ci<=? char-ci=? char-ci>? char-downcase char-lower-case? char-numeric? char-ready? char-upcase char-upper-case? char-whitespace? char<=? char=? char>? char? close-input-port close-output-port complex? cons cos current-input-port current-output-port denominator display eof-object? eq? equal? eqv? eval even? exact->inexact exact? exp expt #f floor force gcd imag-part inexact->exact inexact? input-port? integer->char integer? interaction-environment lcm length list list->string list->vector list-ref list-tail list? load log magnitude make-polar make-rectangular make-string make-vector max member memq memv min modulo negative? newline not null-environment null? number->string number? numerator odd? open-input-file open-output-file output-port? pair? peek-char port? positive? procedure? quasiquote quote quotient rational? rationalize read read-char real-part real? remainder reverse round scheme-report-environment set! set-car! set-cdr! sin sqrt string string->list string->number string->symbol string-append string-ci<=? string-ci=? string-ci>? string-copy string-fill! string-length string-ref string-set! string<=? string=? string>? string? substring symbol->string symbol? #t tan transcript-off transcript-on truncate values vector vector->list vector-fill! vector-length vector-ref vector-set! with-input-from-file with-output-to-file write write-char zero? rejection-query mh-query condition fold flatten pair first second third fourth fifth sixth seventh rest flip hist repeat gaussian density multiviz uniform-draw mem sum runPhysics animatePhysics uniform worldWidth worldHeight scatter lineplot dirichlet multinomial beta mean"); + var keywords = makeKeywords("λ case-lambda call/cc class define-class exit-handler field import inherit init-field interface let*-values let-values let/ec mixin opt-lambda override protect provide public rename require require-for-syntax syntax syntax-case syntax-error unit/sig unless when with-syntax and begin call-with-current-continuation call-with-input-file call-with-output-file case cond define define-syntax delay do dynamic-wind else for-each if lambda let let* let-syntax letrec letrec-syntax map or syntax-rules abs acos angle append apply asin assoc assq assv atan boolean? caar cadr call-with-values car cdddar cddddr cdr ceiling char->integer char-alphabetic? char-ci<=? char-ci=? char-ci>? char-downcase char-lower-case? char-numeric? char-ready? char-upcase char-upper-case? char-whitespace? char<=? char=? char>? char? close-input-port close-output-port complex? cons cos current-input-port current-output-port denominator display eof-object? eq? equal? eqv? eval even? exact->inexact exact? exp expt #f floor force gcd imag-part inexact->exact inexact? input-port? integer->char integer? interaction-environment lcm length list list->string list->vector list-ref list-tail list? load log magnitude make-polar make-rectangular make-string make-vector max member memq memv min modulo negative? newline not null-environment null? number->string number? numerator odd? open-input-file open-output-file output-port? pair? peek-char port? positive? procedure? quasiquote quote quotient rational? rationalize read read-char real-part real? remainder reverse round scheme-report-environment set! set-car! set-cdr! sin sqrt string string->list string->number string->symbol string-append string-ci<=? string-ci=? string-ci>? string-copy string-fill! string-length string-ref string-set! string<=? string=? string>? string? substring symbol->string symbol? #t tan transcript-off transcript-on truncate values vector vector->list vector-fill! vector-length vector-ref vector-set! with-input-from-file with-output-to-file write write-char zero? rejection-query mh-query condition fold flatten pair first second third fourth fifth sixth seventh rest flip hist repeat gaussian density multiviz uniform-draw mem sum runPhysics animatePhysics uniform worldWidth worldHeight scatter lineplot dirichlet multinomial beta mean"); var indentKeys = makeKeywords("define let letrec let* lambda"); function stateStack(indent, type, prev) { // represents a state stack object @@ -79,146 +79,146 @@ CodeMirror.defineMode("scheme", function () { var returnType = null; switch(state.mode){ - case "string": // multi-line string parsing mode - var next, escaped = false; - while ((next = stream.next()) != null) { - if (next == "\"" && !escaped) { + case "string": // multi-line string parsing mode + var next, escaped = false; + while ((next = stream.next()) != null) { + if (next == "\"" && !escaped) { - state.mode = false; - break; - } - escaped = !escaped && next == "\\"; + state.mode = false; + break; } - returnType = STRING; // continue on in scheme-string mode - break; - case "comment": // comment parsing mode - var next, maybeEnd = false; - while ((next = stream.next()) != null) { - if (next == "#" && maybeEnd) { - - state.mode = false; - break; - } - maybeEnd = (next == "|"); + escaped = !escaped && next == "\\"; + } + returnType = STRING; // continue on in scheme-string mode + break; + case "comment": // comment parsing mode + var next, maybeEnd = false; + while ((next = stream.next()) != null) { + if (next == "#" && maybeEnd) { + + state.mode = false; + break; } + maybeEnd = (next == "|"); + } + returnType = COMMENT; + break; + case "s-expr-comment": // s-expr commenting mode + state.mode = false; + if(stream.peek() == "(" || stream.peek() == "["){ + // actually start scheme s-expr commenting mode + state.sExprComment = 0; + }else{ + // if not we just comment the entire of the next token + stream.eatWhile(/[^/s]/); // eat non spaces returnType = COMMENT; break; - case "s-expr-comment": // s-expr commenting mode - state.mode = false; - if(stream.peek() == "(" || stream.peek() == "["){ - // actually start scheme s-expr commenting mode - state.sExprComment = 0; - }else{ - // if not we just comment the entire of the next token - stream.eatWhile(/[^/s]/); // eat non spaces + } + default: // default parsing mode + var ch = stream.next(); + + if (ch == "\"") { + state.mode = "string"; + returnType = STRING; + + } else if (ch == "'") { + returnType = ATOM; + } else if (ch == '#') { + if (stream.eat("|")) { // Multi-line comment + state.mode = "comment"; // toggle to comment mode returnType = COMMENT; - break; - } - default: // default parsing mode - var ch = stream.next(); - - if (ch == "\"") { - state.mode = "string"; - returnType = STRING; - - } else if (ch == "'") { + } else if (stream.eat(/[tf]/i)) { // #t/#f (atom) returnType = ATOM; - } else if (ch == '#') { - if (stream.eat("|")) { // Multi-line comment - state.mode = "comment"; // toggle to comment mode - returnType = COMMENT; - } else if (stream.eat(/[tf]/i)) { // #t/#f (atom) - returnType = ATOM; - } else if (stream.eat(';')) { // S-Expr comment - state.mode = "s-expr-comment"; - returnType = COMMENT; + } else if (stream.eat(';')) { // S-Expr comment + state.mode = "s-expr-comment"; + returnType = COMMENT; + } else { + var numTest = null, hasExactness = false, hasRadix = true; + if (stream.eat(/[ei]/i)) { + hasExactness = true; } else { - var numTest = null, hasExactness = false, hasRadix = true; - if (stream.eat(/[ei]/i)) { - hasExactness = true; - } else { - stream.backUp(1); // must be radix specifier - } - if (stream.match(/^#b/i)) { - numTest = isBinaryNumber; - } else if (stream.match(/^#o/i)) { - numTest = isOctalNumber; - } else if (stream.match(/^#x/i)) { - numTest = isHexNumber; - } else if (stream.match(/^#d/i)) { - numTest = isDecimalNumber; - } else if (stream.match(/^[-+0-9.]/, false)) { - hasRadix = false; - numTest = isDecimalNumber; + stream.backUp(1); // must be radix specifier + } + if (stream.match(/^#b/i)) { + numTest = isBinaryNumber; + } else if (stream.match(/^#o/i)) { + numTest = isOctalNumber; + } else if (stream.match(/^#x/i)) { + numTest = isHexNumber; + } else if (stream.match(/^#d/i)) { + numTest = isDecimalNumber; + } else if (stream.match(/^[-+0-9.]/, false)) { + hasRadix = false; + numTest = isDecimalNumber; // re-consume the intial # if all matches failed - } else if (!hasExactness) { - stream.eat('#'); - } - if (numTest != null) { - if (hasRadix && !hasExactness) { - // consume optional exactness after radix - stream.match(/^#[ei]/i); - } - if (numTest(stream)) - returnType = NUMBER; - } + } else if (!hasExactness) { + stream.eat('#'); } - } else if (/^[-+0-9.]/.test(ch) && isDecimalNumber(stream, true)) { // match non-prefixed number, must be decimal - returnType = NUMBER; - } else if (ch == ";") { // comment - stream.skipToEnd(); // rest of the line is a comment - returnType = COMMENT; - } else if (ch == "(" || ch == "[") { - var keyWord = ''; var indentTemp = stream.column(), letter; - /** - Either - (indent-word .. - (non-indent-word .. - (;something else, bracket, etc. - */ - - while ((letter = stream.eat(/[^\s\(\[\;\)\]]/)) != null) { - keyWord += letter; + if (numTest != null) { + if (hasRadix && !hasExactness) { + // consume optional exactness after radix + stream.match(/^#[ei]/i); + } + if (numTest(stream)) + returnType = NUMBER; } + } + } else if (/^[-+0-9.]/.test(ch) && isDecimalNumber(stream, true)) { // match non-prefixed number, must be decimal + returnType = NUMBER; + } else if (ch == ";") { // comment + stream.skipToEnd(); // rest of the line is a comment + returnType = COMMENT; + } else if (ch == "(" || ch == "[") { + var keyWord = ''; var indentTemp = stream.column(), letter; + /** + Either + (indent-word .. + (non-indent-word .. + (;something else, bracket, etc. + */ + + while ((letter = stream.eat(/[^\s\(\[\;\)\]]/)) != null) { + keyWord += letter; + } - if (keyWord.length > 0 && indentKeys.propertyIsEnumerable(keyWord)) { // indent-word - - pushStack(state, indentTemp + INDENT_WORD_SKIP, ch); - } else { // non-indent word - // we continue eating the spaces - stream.eatSpace(); - if (stream.eol() || stream.peek() == ";") { - // nothing significant after - // we restart indentation 1 space after - pushStack(state, indentTemp + 1, ch); - } else { - pushStack(state, indentTemp + stream.current().length, ch); // else we match - } + if (keyWord.length > 0 && indentKeys.propertyIsEnumerable(keyWord)) { // indent-word + + pushStack(state, indentTemp + INDENT_WORD_SKIP, ch); + } else { // non-indent word + // we continue eating the spaces + stream.eatSpace(); + if (stream.eol() || stream.peek() == ";") { + // nothing significant after + // we restart indentation 1 space after + pushStack(state, indentTemp + 1, ch); + } else { + pushStack(state, indentTemp + stream.current().length, ch); // else we match } - stream.backUp(stream.current().length - 1); // undo all the eating + } + stream.backUp(stream.current().length - 1); // undo all the eating - if(typeof state.sExprComment == "number") state.sExprComment++; + if(typeof state.sExprComment == "number") state.sExprComment++; - returnType = BRACKET; - } else if (ch == ")" || ch == "]") { - returnType = BRACKET; - if (state.indentStack != null && state.indentStack.type == (ch == ")" ? "(" : "[")) { - popStack(state); + returnType = BRACKET; + } else if (ch == ")" || ch == "]") { + returnType = BRACKET; + if (state.indentStack != null && state.indentStack.type == (ch == ")" ? "(" : "[")) { + popStack(state); - if(typeof state.sExprComment == "number"){ - if(--state.sExprComment == 0){ - returnType = COMMENT; // final closing bracket - state.sExprComment = false; // turn off s-expr commenting mode - } + if(typeof state.sExprComment == "number"){ + if(--state.sExprComment == 0){ + returnType = COMMENT; // final closing bracket + state.sExprComment = false; // turn off s-expr commenting mode } } - } else { - stream.eatWhile(/[\w\$_\-!$%&*+\.\/:<=>?@\^~]/); - - if (keywords && keywords.propertyIsEnumerable(stream.current())) { - returnType = BUILTIN; - } else returnType = "variable"; } + } else { + stream.eatWhile(/[\w\$_\-!$%&*+\.\/:<=>?@\^~]/); + + if (keywords && keywords.propertyIsEnumerable(stream.current())) { + returnType = BUILTIN; + } else returnType = "variable"; + } } return (typeof state.sExprComment == "number") ? COMMENT : returnType; }, diff --git a/cm-comments.js b/cm-comments.js index 36dcfcf..380f6c6 100644 --- a/cm-comments.js +++ b/cm-comments.js @@ -1,147 +1,147 @@ var CodeMirror = require('codemirror'); (function() { - "use strict"; - - var noOptions = {}; - var nonWS = /[^\s\u00a0]/; - var Pos = CodeMirror.Pos; - - function firstNonWS(str) { - var found = str.search(nonWS); - return found == -1 ? 0 : found; - } - - CodeMirror.commands.toggleComment = function(cm) { - var from = cm.getCursor("start"), to = cm.getCursor("end"); - cm.uncomment(from, to) || cm.lineComment(from, to); - }; - - CodeMirror.defineExtension("lineComment", function(from, to, options) { - if (!options) options = noOptions; - var self = this, mode = self.getModeAt(from); - var commentString = options.lineComment || mode.lineComment; - if (!commentString) { - if (options.blockCommentStart || mode.blockCommentStart) { - options.fullLines = true; - self.blockComment(from, to, options); - } - return; + "use strict"; + + var noOptions = {}; + var nonWS = /[^\s\u00a0]/; + var Pos = CodeMirror.Pos; + + function firstNonWS(str) { + var found = str.search(nonWS); + return found == -1 ? 0 : found; } - var firstLine = self.getLine(from.line); - if (firstLine == null) return; - var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1); - var pad = options.padding == null ? " " : options.padding; - var blankLines = options.commentBlankLines || from.line == to.line; - - self.operation(function() { - if (options.indent) { - var baseString = firstLine.slice(0, firstNonWS(firstLine)); - for (var i = from.line; i < end; ++i) { - var line = self.getLine(i), cut = baseString.length; - if (!blankLines && !nonWS.test(line)) continue; - if (line.slice(0, cut) != baseString) cut = firstNonWS(line); - self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut)); + + CodeMirror.commands.toggleComment = function(cm) { + var from = cm.getCursor("start"), to = cm.getCursor("end"); + cm.uncomment(from, to) || cm.lineComment(from, to); + }; + + CodeMirror.defineExtension("lineComment", function(from, to, options) { + if (!options) options = noOptions; + var self = this, mode = self.getModeAt(from); + var commentString = options.lineComment || mode.lineComment; + if (!commentString) { + if (options.blockCommentStart || mode.blockCommentStart) { + options.fullLines = true; + self.blockComment(from, to, options); + } + return; } - } else { - for (var i = from.line; i < end; ++i) { - if (blankLines || nonWS.test(self.getLine(i))) - self.replaceRange(commentString + pad, Pos(i, 0)); + var firstLine = self.getLine(from.line); + if (firstLine == null) return; + var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1); + var pad = options.padding == null ? " " : options.padding; + var blankLines = options.commentBlankLines || from.line == to.line; + + self.operation(function() { + if (options.indent) { + var baseString = firstLine.slice(0, firstNonWS(firstLine)); + for (var i = from.line; i < end; ++i) { + var line = self.getLine(i), cut = baseString.length; + if (!blankLines && !nonWS.test(line)) continue; + if (line.slice(0, cut) != baseString) cut = firstNonWS(line); + self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut)); + } + } else { + for (var i = from.line; i < end; ++i) { + if (blankLines || nonWS.test(self.getLine(i))) + self.replaceRange(commentString + pad, Pos(i, 0)); + } + } + }); + }); + + CodeMirror.defineExtension("blockComment", function(from, to, options) { + if (!options) options = noOptions; + var self = this, mode = self.getModeAt(from); + var startString = options.blockCommentStart || mode.blockCommentStart; + var endString = options.blockCommentEnd || mode.blockCommentEnd; + if (!startString || !endString) { + if ((options.lineComment || mode.lineComment) && options.fullLines != false) + self.lineComment(from, to, options); + return; } - } + + var end = Math.min(to.line, self.lastLine()); + if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end; + + var pad = options.padding == null ? " " : options.padding; + if (from.line > end) return; + + self.operation(function() { + if (options.fullLines != false) { + var lastLineHasText = nonWS.test(self.getLine(end)); + self.replaceRange(pad + endString, Pos(end)); + self.replaceRange(startString + pad, Pos(from.line, 0)); + var lead = options.blockCommentLead || mode.blockCommentLead; + if (lead != null) for (var i = from.line + 1; i <= end; ++i) + if (i != end || lastLineHasText) + self.replaceRange(lead + pad, Pos(i, 0)); + } else { + self.replaceRange(endString, to); + self.replaceRange(startString, from); + } + }); }); - }); - - CodeMirror.defineExtension("blockComment", function(from, to, options) { - if (!options) options = noOptions; - var self = this, mode = self.getModeAt(from); - var startString = options.blockCommentStart || mode.blockCommentStart; - var endString = options.blockCommentEnd || mode.blockCommentEnd; - if (!startString || !endString) { - if ((options.lineComment || mode.lineComment) && options.fullLines != false) - self.lineComment(from, to, options); - return; - } - - var end = Math.min(to.line, self.lastLine()); - if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end; - - var pad = options.padding == null ? " " : options.padding; - if (from.line > end) return; - - self.operation(function() { - if (options.fullLines != false) { - var lastLineHasText = nonWS.test(self.getLine(end)); - self.replaceRange(pad + endString, Pos(end)); - self.replaceRange(startString + pad, Pos(from.line, 0)); + + CodeMirror.defineExtension("uncomment", function(from, to, options) { + if (!options) options = noOptions; + var self = this, mode = self.getModeAt(from); + var end = Math.min(to.line, self.lastLine()), start = Math.min(from.line, end); + + // Try finding line comments + var lineString = options.lineComment || mode.lineComment, lines = []; + var pad = options.padding == null ? " " : options.padding, didSomething; + lineComment: { + if (!lineString) break lineComment; + for (var i = start; i <= end; ++i) { + var line = self.getLine(i); + var found = line.indexOf(lineString); + if (found == -1 && (i != end || i == start) && nonWS.test(line)) break lineComment; + if (i != start && found > -1 && nonWS.test(line.slice(0, found))) break lineComment; + lines.push(line); + } + self.operation(function() { + for (var i = start; i <= end; ++i) { + var line = lines[i - start]; + var pos = line.indexOf(lineString), endPos = pos + lineString.length; + if (pos < 0) continue; + if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length; + didSomething = true; + self.replaceRange("", Pos(i, pos), Pos(i, endPos)); + } + }); + if (didSomething) return true; + } + + // Try block comments + var startString = options.blockCommentStart || mode.blockCommentStart; + var endString = options.blockCommentEnd || mode.blockCommentEnd; + if (!startString || !endString) return false; var lead = options.blockCommentLead || mode.blockCommentLead; - if (lead != null) for (var i = from.line + 1; i <= end; ++i) - if (i != end || lastLineHasText) - self.replaceRange(lead + pad, Pos(i, 0)); - } else { - self.replaceRange(endString, to); - self.replaceRange(startString, from); - } - }); - }); - - CodeMirror.defineExtension("uncomment", function(from, to, options) { - if (!options) options = noOptions; - var self = this, mode = self.getModeAt(from); - var end = Math.min(to.line, self.lastLine()), start = Math.min(from.line, end); - - // Try finding line comments - var lineString = options.lineComment || mode.lineComment, lines = []; - var pad = options.padding == null ? " " : options.padding, didSomething; - lineComment: { - if (!lineString) break lineComment; - for (var i = start; i <= end; ++i) { - var line = self.getLine(i); - var found = line.indexOf(lineString); - if (found == -1 && (i != end || i == start) && nonWS.test(line)) break lineComment; - if (i != start && found > -1 && nonWS.test(line.slice(0, found))) break lineComment; - lines.push(line); - } - self.operation(function() { - for (var i = start; i <= end; ++i) { - var line = lines[i - start]; - var pos = line.indexOf(lineString), endPos = pos + lineString.length; - if (pos < 0) continue; - if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length; - didSomething = true; - self.replaceRange("", Pos(i, pos), Pos(i, endPos)); + var startLine = self.getLine(start), endLine = end == start ? startLine : self.getLine(end); + var open = startLine.indexOf(startString), close = endLine.lastIndexOf(endString); + if (close == -1 && start != end) { + endLine = self.getLine(--end); + close = endLine.lastIndexOf(endString); } - }); - if (didSomething) return true; - } - - // Try block comments - var startString = options.blockCommentStart || mode.blockCommentStart; - var endString = options.blockCommentEnd || mode.blockCommentEnd; - if (!startString || !endString) return false; - var lead = options.blockCommentLead || mode.blockCommentLead; - var startLine = self.getLine(start), endLine = end == start ? startLine : self.getLine(end); - var open = startLine.indexOf(startString), close = endLine.lastIndexOf(endString); - if (close == -1 && start != end) { - endLine = self.getLine(--end); - close = endLine.lastIndexOf(endString); - } - if (open == -1 || close == -1) return false; - - self.operation(function() { - self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)), - Pos(end, close + endString.length)); - var openEnd = open + startString.length; - if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length; - self.replaceRange("", Pos(start, open), Pos(start, openEnd)); - if (lead) for (var i = start + 1; i <= end; ++i) { - var line = self.getLine(i), found = line.indexOf(lead); - if (found == -1 || nonWS.test(line.slice(0, found))) continue; - var foundEnd = found + lead.length; - if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length; - self.replaceRange("", Pos(i, found), Pos(i, foundEnd)); - } + if (open == -1 || close == -1) return false; + + self.operation(function() { + self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)), + Pos(end, close + endString.length)); + var openEnd = open + startString.length; + if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length; + self.replaceRange("", Pos(start, open), Pos(start, openEnd)); + if (lead) for (var i = start + 1; i <= end; ++i) { + var line = self.getLine(i), found = line.indexOf(lead); + if (found == -1 || nonWS.test(line.slice(0, found))) continue; + var foundEnd = found + lead.length; + if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length; + self.replaceRange("", Pos(i, found), Pos(i, foundEnd)); + } + }); + return true; }); - return true; - }); })(); diff --git a/cm-folding.js b/cm-folding.js index a6b89ea..c9e5bb9 100644 --- a/cm-folding.js +++ b/cm-folding.js @@ -3,152 +3,149 @@ var CodeMirror = require('codemirror'); /* global CodeMirror */ (function() { - "use strict"; - - function doFold(cm, pos, options) { - var finder = options && (options.call ? options : options.rangeFinder); - if (!finder) finder = cm.getHelper(pos, "fold"); - if (!finder) return; - if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0); - var minSize = options && options.minFoldSize || 0; - - function getRange(allowFolded) { - var range = finder(cm, pos); - if (!range || range.to.line - range.from.line < minSize) return null; - var marks = cm.findMarksAt(range.from); - for (var i = 0; i < marks.length; ++i) { - if (marks[i].__isFold) { - if (!allowFolded) return null; - range.cleared = true; - marks[i].clear(); - } - } - return range; - } - - var range = getRange(true); - if (options && options.scanUp) while (!range && pos.line > cm.firstLine()) { - pos = CodeMirror.Pos(pos.line - 1, 0); - range = getRange(false); - } - if (!range || range.cleared) return; - - var myWidget = makeWidget(options); - CodeMirror.on(myWidget, "mousedown", function() { myRange.clear(); }); - var myRange = cm.markText(range.from, range.to, { - replacedWith: myWidget, - clearOnEnter: true, - __isFold: true - }); - myRange.on("clear", function(from, to) { + "use strict"; + + function doFold(cm, pos, options) { + var finder = options && (options.call ? options : options.rangeFinder); + if (!finder) finder = cm.getHelper(pos, "fold"); + if (!finder) return; + if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0); + var minSize = options && options.minFoldSize || 0; + + function getRange(allowFolded) { + var range = finder(cm, pos); + if (!range || range.to.line - range.from.line < minSize) return null; + var marks = cm.findMarksAt(range.from); + for (var i = 0; i < marks.length; ++i) { + if (marks[i].__isFold) { + if (!allowFolded) return null; + range.cleared = true; + marks[i].clear(); + } + } + return range; + } + + var range = getRange(true); + if (options && options.scanUp) while (!range && pos.line > cm.firstLine()) { + pos = CodeMirror.Pos(pos.line - 1, 0); + range = getRange(false); + } + if (!range || range.cleared) return; + + var myWidget = makeWidget(options); + CodeMirror.on(myWidget, "mousedown", function() { myRange.clear(); }); + var myRange = cm.markText(range.from, range.to, { + replacedWith: myWidget, + clearOnEnter: true, + __isFold: true + }); + myRange.on("clear", function(from, to) { CodeMirror.signal(cm, "unfold", cm, from, to); - }); - CodeMirror.signal(cm, "fold", cm, range.from, range.to); - } - - function makeWidget(options) { - var widget = (options && options.widget) || "\u2194"; - if (typeof widget == "string") { - var text = document.createTextNode(widget); - widget = document.createElement("span"); - widget.appendChild(text); - widget.className = "CodeMirror-foldmarker"; - } - return widget; - } - - // Clumsy backwards-compatible interface - CodeMirror.newFoldFunction = function(rangeFinder, widget) { - return function(cm, pos) { doFold(cm, pos, {rangeFinder: rangeFinder, widget: widget}); }; - }; - - // New-style interface - CodeMirror.defineExtension("foldCode", function(pos, options) { doFold(this, pos, options); }); - - CodeMirror.registerHelper("fold", "combine", function() { - var funcs = Array.prototype.slice.call(arguments, 0); - return function(cm, start) { - for (var i = 0; i < funcs.length; ++i) { - var found = funcs[i](cm, start); - if (found) return found; - } - }; - }); - })(); - - var myRangeFinder = function(cm, start) { - + }); + CodeMirror.signal(cm, "fold", cm, range.from, range.to); + } + + function makeWidget(options) { + var widget = (options && options.widget) || "\u2194"; + if (typeof widget == "string") { + var text = document.createTextNode(widget); + widget = document.createElement("span"); + widget.appendChild(text); + widget.className = "CodeMirror-foldmarker"; + } + return widget; + } + + // Clumsy backwards-compatible interface + CodeMirror.newFoldFunction = function(rangeFinder, widget) { + return function(cm, pos) { doFold(cm, pos, {rangeFinder: rangeFinder, widget: widget}); }; + }; + + // New-style interface + CodeMirror.defineExtension("foldCode", function(pos, options) { doFold(this, pos, options); }); + + CodeMirror.registerHelper("fold", "combine", function() { + var funcs = Array.prototype.slice.call(arguments, 0); + return function(cm, start) { + for (var i = 0; i < funcs.length; ++i) { + var found = funcs[i](cm, start); + if (found) return found; + } + }; + }); +})(); + +var myRangeFinder = function(cm, start) { + var line = start.line, lineText = cm.getLine(line); - + //if the line has a comment fold, then do that: if (lineText.indexOf(";;;fold:") != -1) return tripleCommentRangeFinder(cm, start) - - + var startCh, tokenType; - -// function findOpening(openCh) { -// for (var at = start.ch, pass = 0;;) { -// var found = at <= 0 ? -1 : lineText.lastIndexOf(openCh, at - 1); -// if (found == -1) { -// if (pass == 1) break; -// pass = 1; -// at = lineText.length; -// continue; -// } -// if (pass == 1 && found < start.ch) break; -// tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1)); -// if (!/^(comment|string)/.test(tokenType)) return found + 1; -// at = found - 1; -// } -// } - + + // function findOpening(openCh) { + // for (var at = start.ch, pass = 0;;) { + // var found = at <= 0 ? -1 : lineText.lastIndexOf(openCh, at - 1); + // if (found == -1) { + // if (pass == 1) break; + // pass = 1; + // at = lineText.length; + // continue; + // } + // if (pass == 1 && found < start.ch) break; + // tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1)); + // if (!/^(comment|string)/.test(tokenType)) return found + 1; + // at = found - 1; + // } + // } + var startToken = "(", endToken = ")"; var startCh = lineText.lastIndexOf("(", start.ch); if (startCh == -1) return; startCh++; tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, startCh)); - - + var count = 1, lastLine = cm.lastLine(), end, endCh; - -outer: for (var i = line; i <= lastLine; ++i) { - var text = cm.getLine(i), pos = i == line ? startCh : 0; - for (;;) { - var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos); - if (nextOpen < 0) nextOpen = text.length; - if (nextClose < 0) nextClose = text.length; - pos = Math.min(nextOpen, nextClose); - if (pos == text.length) break; - if (cm.getTokenTypeAt(CodeMirror.Pos(i, pos + 1)) == tokenType) { - if (pos == nextOpen) ++count; - else if (!--count) { end = i; endCh = pos; break outer; } + + outer: for (var i = line; i <= lastLine; ++i) { + var text = cm.getLine(i), pos = i == line ? startCh : 0; + for (;;) { + var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos); + if (nextOpen < 0) nextOpen = text.length; + if (nextClose < 0) nextClose = text.length; + pos = Math.min(nextOpen, nextClose); + if (pos == text.length) break; + if (cm.getTokenTypeAt(CodeMirror.Pos(i, pos + 1)) == tokenType) { + if (pos == nextOpen) ++count; + else if (!--count) { end = i; endCh = pos; break outer; } + } + ++pos; } - ++pos; } -} if (end == null || line == end && endCh == startCh) return; return {from: CodeMirror.Pos(line, startCh), - to: CodeMirror.Pos(end, endCh)}; + to: CodeMirror.Pos(end, endCh)}; } //if we want to fold what's between ";;;fold:" and ";;;". //assume that start is already the line with ";;;fold:" //so find the next ";;;" and return the range from start line + 1 to match. var tripleCommentRangeFinder = function(cm, start) { - var lastLine = cm.lastLine(); - var pos; - for (var i = start.line+1; i<=lastLine; i++) { - var text = cm.getLine(i) - pos = text.indexOf(";;;") - if (pos==0) { - var endCh = cm.getLine(i).length - return {from: CodeMirror.Pos(start.line+1, 0), to: CodeMirror.Pos(i, endCh)}; } - } - return; + var lastLine = cm.lastLine(); + var pos; + for (var i = start.line+1; i<=lastLine; i++) { + var text = cm.getLine(i) + pos = text.indexOf(";;;") + if (pos==0) { + var endCh = cm.getLine(i).length + return {from: CodeMirror.Pos(start.line+1, 0), to: CodeMirror.Pos(i, endCh)}; } + } + return; } - module.exports = { - tripleCommentRangeFinder: tripleCommentRangeFinder, - myRangeFinder: myRangeFinder + tripleCommentRangeFinder: tripleCommentRangeFinder, + myRangeFinder: myRangeFinder } diff --git a/compile.sh b/compile.sh index 5bc0cfe..c87d0f0 100755 --- a/compile.sh +++ b/compile.sh @@ -13,18 +13,18 @@ fi echo "- Browserifying" # HT many responses on http://stackoverflow.com/q/16275325/351392 node node_modules/browserify/bin/cmd.js --fast \ - -r ./probabilistic-js \ - -r ./type-utils.js \ - -r ./viz \ - -r ./church_builtins \ - -r ./evaluate \ - -r ./editor \ - -r ./cm-brackets \ - -r ./cm-folding \ - -r ./cm-church \ - -r ./cm-comments \ - -r ./util.js \ - -o online/webchurch.js + -r ./probabilistic-js \ + -r ./type-utils.js \ + -r ./viz \ + -r ./church_builtins \ + -r ./evaluate \ + -r ./editor \ + -r ./cm-brackets \ + -r ./cm-folding \ + -r ./cm-church \ + -r ./cm-comments \ + -r ./util.js \ + -o online/webchurch.js echo "- Add webworkers stub" cat online/webchurch.js ww-stub.js > online/webchurch-ww.js diff --git a/editor.js b/editor.js index 6200def..875af8a 100644 --- a/editor.js +++ b/editor.js @@ -21,10 +21,9 @@ CodeMirror.keyMap.default["Cmd-."] = function(cm){cm.foldCode(cm.getCursor(), fo CodeMirror.keyMap.default["Ctrl-;"] = "toggleComment"; CodeMirror.keyMap.default["Ctrl-."] = function(cm){cm.foldCode(cm.getCursor(), folding.myRangeFinder); }; - var _ = require('underscore'); _.templateSettings = { - interpolate: /\{\{(.+?)\}\}/g + interpolate: /\{\{(.+?)\}\}/g }; var runners = {}; @@ -32,233 +31,233 @@ runners['webchurch'] = makewebchurchrunner(); runners['webchurch-opt'] = makewebchurchrunner({precompile: true}); function wrap(tag, content) { - return _.template("<{{tag}}>{{content}}", - {tag: tag, - content: content - }); + return _.template("<{{tag}}>{{content}}", + {tag: tag, + content: content + }); } function makewebchurchrunner(engineOptions){ - if (engineOptions === undefined) { - engineOptions = {} - } - return function(editorModel) { - var cm = editorModel.get('codeMirror'); - var code = editorModel.get('code'); - - var $results = cm.$results; - - $results.show(); - if (cm.errormark != undefined) { - cm.errormark.clear(); + if (engineOptions === undefined) { + engineOptions = {} } + return function(editorModel) { + var cm = editorModel.get('codeMirror'); + var code = editorModel.get('code'); - editorModel.trigger('run:start'); - try { - var runResult = evaluate(code, engineOptions); - - var underlyingData; + var $results = cm.$results; - // render all side effects - sideEffects.forEach(function(e) { - if (e.type == "string") { - $results.append( $("
"+e.data+"
") ); - } - if (e.type == "svg") { - $results.append( $("
").append(e.data)); - } - if (e.type == "table") { - var tableString = wrap("table", e.data.map(function(row) { - var cols = row.map(function(x) { return wrap('td', x); }).join(""); - return wrap("tr", cols); - }).join("\n")); - $results.append( $(tableString) ); + $results.show(); + if (cm.errormark != undefined) { + cm.errormark.clear(); } - if (e.type == "function") { - e.data($results); + + editorModel.trigger('run:start'); + try { + var runResult = evaluate(code, engineOptions); + + var underlyingData; + + // render all side effects + sideEffects.forEach(function(e) { + if (e.type == "string") { + $results.append( $("
"+e.data+"
") ); + } + if (e.type == "svg") { + $results.append( $("
").append(e.data)); + } + if (e.type == "table") { + var tableString = wrap("table", e.data.map(function(row) { + var cols = row.map(function(x) { return wrap('td', x); }).join(""); + return wrap("tr", cols); + }).join("\n")); + $results.append( $(tableString) ); + } + if (e.type == "function") { + e.data($results); + } + }); + + underlyingData = runResult; + runResult = format_result(runResult); + if (!(runResult == "undefined")) { + $results.append($("
"+runResult+'
')); + } + + editorModel.trigger('run:finish'); + + } catch (e) { + + editorModel.trigger('run:finish'); + + var error = e.message; + $results + .append( $("

") + .addClass('error') + .text(error) ); + + if (e.stackarray != undefined) { + var churchStack = $("
");
+                churchStack.text(e.stackarray
+                                 .map(function(x) { return _.template("{{text}}: {{start}}-{{end}}", x) })
+                                 .join("\n"))
+
+                $results.append( '
Church stack array:
', churchStack); + + var start=e.start.split(":"), end=e.end.split(":"); + cm.errormark = cm.markText({line: Number(start[0])-1, ch: Number(start[1])-1}, + {line: Number(end[0])-1, ch: Number(end[1])}, + {className: "CodeMirrorError", clearOnEnter: true}); + + } + + var jsStack = $("
");
+            jsStack.text(e.jsStack.join('\n'));
+
+            $results.append('
JS stack:
', jsStack ); } - }); + }; +}; - underlyingData = runResult; - runResult = format_result(runResult); - if (!(runResult == "undefined")) { - $results.append($("
"+runResult+'
')); - } +var EditorModel = Backbone.Model.extend({ + initialize: function(options) { + this.set('initialOptions', options); + }, + defaults: { + code: "", + engine: "webchurch" + }, + run: function() { + var engine = runners[this.get('engine')]; + this.set('result', engine( this )); + } +}); - editorModel.trigger('run:finish'); +// return a backbone model +var inject = function(domEl, options) { + options = _(options).defaults({ + code: $(domEl).text(), + engine: "webchurch", + exerciseName: "" + }); - } catch (e) { + var editorModel = new EditorModel(options); + + // editor + var cm = CodeMirror( + // TODO: defer this - we might not want to display immediately... + function(el) { + $(domEl).replaceWith(el); + }, + { + value: options.code, + lineNumbers: false, + matchBrackets: true, + continueComments: "Enter", + viewportMargin: Infinity, + autoCloseBrackets: true, + mode: 'scheme' + }); + + editorModel.set('codeMirror', cm); + + // when text in codemirror changes, update editormodel + cm.on('change', function(cmInstance) { + editorModel.set('code', cmInstance.getValue()) + }); - editorModel.trigger('run:finish'); + //fold ";;;fold:" parts: + var lastLine = cm.lastLine(); + for(var i=0;i<=lastLine;i++) { + var txt = cm.getLine(i), + pos = txt.indexOf(";;;fold:"); + if (pos==0) {cm.foldCode(CodeMirror.Pos(i,pos),folding.tripleCommentRangeFinder);} + } - var error = e.message; - $results - .append( $("

") - .addClass('error') - .text(error) ); + // results div + var $results = $("
"); + $results.hide(); - if (e.stackarray != undefined) { - var churchStack = $("
");
-        churchStack.text(e.stackarray
-                         .map(function(x) { return _.template("{{text}}: {{start}}-{{end}}", x) })
-                         .join("\n"))
+    // engine selector
 
-        $results.append( '
Church stack array:
', churchStack); + var engines = ["webchurch", "webchurch-opt"], + engineSelectorString = "", + $engineSelector = $(engineSelectorString); - } + // when engine selector changes, update model + $engineSelector.change(function(e) { + editorModel.set('engine', $(this).val() ); + }); - var jsStack = $("
");
-      jsStack.text(e.jsStack.join('\n'));
+    // reset button
+    var $resetButton = $("