<commit_before>/// <reference path="ast.ts" />
/// <reference path="visit.ts" />

// Dynamic syntax.

type Value = number | Code;

interface Env {
  [key: string]: Value;
}

class Code {
  constructor(public expr: ExpressionNode) {}
}


// Dynamic semantics rules.

let Interp : ASTVisit<Env, [Value, Env]> = {
  visit_literal(tree: LiteralNode, env: Env): [Value, Env] {
    return [tree.value, env];
  },

  visit_seq(tree: SeqNode, env: Env): [Value, Env] {
    let [v, e] = interp(tree.lhs, env);
    return interp(tree.rhs, e);
  },

  visit_let(tree: LetNode, env: Env): [Value, Env] {
    let [v, e] = interp(tree.expr, env);
    // Abuse prototypes to create an overlay environment.
    let e2 = <Env> Object(e);
    e2[tree.ident] = v;
    return [v, e2];
  },

  visit_lookup(tree: LookupNode, env: Env): [Value, Env] {
    let v = env[tree.ident];
    if (v === undefined) {
      throw "error: undefined variable " + tree.ident;
    }
    return [v, env];
  },

  visit_quote(tree: RunNode, env: Env): [Value, Env] {
    return [new Code(tree.expr), env];
  },

  visit_escape(tree: EscapeNode, env: Env): [Value, Env] {
    throw "unimplemented";
  },

  visit_run(tree: RunNode, env: Env): [Value, Env] {
    let [v, e] = interp(tree.expr, env);
    if (v instanceof Code) {
      // Fresh environment for now.
      let res = interpret(v.expr);
      return [res, env];
    } else {
      throw "error: tried to run non-code value";
    }
  },

  visit_binary(tree: BinaryNode, env: Env): [Value, Env] {
    let [v1, e1] = interp(tree.lhs, env);
    let [v2, e2] = interp(tree.rhs, e1);
    if (typeof v1 === 'number' && typeof v2 === 'number') {
      let v: Value;
      switch (tree.op) {
        case "+":
          v = v1 + v2; break;
        case "-":
          v = v1 - v2; break;
        case "*":
          v = v1 * v2; break;
        case "/":
          v = v1 / v2; break;
        default:
          throw "error: unknown binary operator " + tree.op;
      }
      return [v, e2];
    } else {
      throw "error: non-numeric operands to operator";
    }
  }
}

function interp(tree: SyntaxNode, env: Env): [Value, Env] {
  return ast_visit(Interp, tree, env);
}

// Another visitor for scanning over a quoted tree. Returns the tree
// unchanged, except when it reaches an escape.
// TODO Need to walk to children.
let QuoteInterp : ASTVisit<Env, [SyntaxNode, Env]> = {
  visit_literal(tree: LiteralNode, env: Env): [SyntaxNode, Env] {
    return [tree, env];
  },

  visit_seq(tree: SeqNode, env: Env): [SyntaxNode, Env] {
    let [t1, e1] = quote_interp(tree.lhs, env);
    let [t2, e2] = quote_interp(tree.rhs, e1);
    let t : SeqNode = {tag: "seq", lhs: t1, rhs: t2};
    return [t, e2];
  },

  visit_let(tree: LetNode, env: Env): [SyntaxNode, Env] {
    return [tree, env];
  },

  visit_lookup(tree: LookupNode, env: Env): [SyntaxNode, Env] {
    return [tree, env];
  },

  visit_quote(tree: RunNode, env: Env): [SyntaxNode, Env] {
    return [tree, env];
  },

  visit_escape(tree: EscapeNode, env: Env): [SyntaxNode, Env] {
    let [v, e] = interp(tree, env);
    if (typeof v === "code") {
      return [v.expr, e];
    } else {
      throw "error: escaped to non-code value " + v;
    }
  },

  visit_run(tree: RunNode, env: Env): [SyntaxNode, Env] {
    return [tree, env];
  },

  visit_binary(tree: BinaryNode, env: Env): [SyntaxNode, Env] {
    return [tree, env];
  }
}

function quote_interp(tree: SyntaxNode, env: Env): [SyntaxNode, Env] {
  return ast_visit(QuoteInterp, tree, env);
}

// Helper to execute to a value in an empty initial environment.
function interpret(program: SyntaxNode): Value {
  let [v, e] = interp(program, {});
  return v;
}
<commit_msg>Finish most of quote interpreter<commit_after>/// <reference path="ast.ts" />
/// <reference path="visit.ts" />

// Dynamic syntax.

type Value = number | Code;

interface Env {
  [key: string]: Value;
}

class Code {
  constructor(public expr: ExpressionNode) {}
}


// Dynamic semantics rules.

let Interp : ASTVisit<Env, [Value, Env]> = {
  visit_literal(tree: LiteralNode, env: Env): [Value, Env] {
    return [tree.value, env];
  },

  visit_seq(tree: SeqNode, env: Env): [Value, Env] {
    let [v, e] = interp(tree.lhs, env);
    return interp(tree.rhs, e);
  },

  visit_let(tree: LetNode, env: Env): [Value, Env] {
    let [v, e] = interp(tree.expr, env);
    // Abuse prototypes to create an overlay environment.
    let e2 = <Env> Object(e);
    e2[tree.ident] = v;
    return [v, e2];
  },

  visit_lookup(tree: LookupNode, env: Env): [Value, Env] {
    let v = env[tree.ident];
    if (v === undefined) {
      throw "error: undefined variable " + tree.ident;
    }
    return [v, env];
  },

  visit_quote(tree: RunNode, env: Env): [Value, Env] {
    return [new Code(tree.expr), env];
  },

  visit_escape(tree: EscapeNode, env: Env): [Value, Env] {
    throw "unimplemented";
  },

  visit_run(tree: RunNode, env: Env): [Value, Env] {
    let [v, e] = interp(tree.expr, env);
    if (v instanceof Code) {
      // Fresh environment for now.
      let res = interpret(v.expr);
      return [res, env];
    } else {
      throw "error: tried to run non-code value";
    }
  },

  visit_binary(tree: BinaryNode, env: Env): [Value, Env] {
    let [v1, e1] = interp(tree.lhs, env);
    let [v2, e2] = interp(tree.rhs, e1);
    if (typeof v1 === 'number' && typeof v2 === 'number') {
      let v: Value;
      switch (tree.op) {
        case "+":
          v = v1 + v2; break;
        case "-":
          v = v1 - v2; break;
        case "*":
          v = v1 * v2; break;
        case "/":
          v = v1 / v2; break;
        default:
          throw "error: unknown binary operator " + tree.op;
      }
      return [v, e2];
    } else {
      throw "error: non-numeric operands to operator";
    }
  }
}

function interp(tree: SyntaxNode, env: Env): [Value, Env] {
  return ast_visit(Interp, tree, env);
}

// Another visitor for scanning over a quoted tree. Returns the tree
// unchanged, except when it reaches an escape.
// TODO Make a RecursiveASTVisit that encapsulates the walking?
let QuoteInterp : ASTVisit<Env, [SyntaxNode, Env]> = {
  visit_literal(tree: LiteralNode, env: Env): [SyntaxNode, Env] {
    return [tree, env];
  },

  visit_seq(tree: SeqNode, env: Env): [SyntaxNode, Env] {
    let [t1, e1] = quote_interp(tree.lhs, env);
    let [t2, e2] = quote_interp(tree.rhs, e1);
    let out : SeqNode = {tag: "seq", lhs: t1, rhs: t2};
    return [out, e2];
  },

  visit_let(tree: LetNode, env: Env): [SyntaxNode, Env] {
    let [t, e] = quote_interp(tree.expr, env);
    let out : LetNode = {tag: "let", ident: tree.ident, expr: t};
    return [out, e];
  },

  visit_lookup(tree: LookupNode, env: Env): [SyntaxNode, Env] {
    return [tree, env];
  },

  visit_quote(tree: RunNode, env: Env): [SyntaxNode, Env] {
    // TODO
    return [tree, env];
  },

  visit_escape(tree: EscapeNode, env: Env): [SyntaxNode, Env] {
    let [v, e] = interp(tree, env);
    if (typeof v === "code") {
      return [v.expr, e];
    } else {
      throw "error: escape produced non-code value " + v;
    }
  },

  visit_run(tree: RunNode, env: Env): [SyntaxNode, Env] {
    let [t, e] = quote_interp(tree.expr, env);
    let out : RunNode = {tag: "run", expr: t};
    return [out, e];
  },

  visit_binary(tree: BinaryNode, env: Env): [SyntaxNode, Env] {
    let [t1, e1] = quote_interp(tree.lhs, env);
    let [t2, e2] = quote_interp(tree.rhs, e1);
    let out : BinaryNode = {tag: "binary", op: tree.op, lhs: t1, rhs: t2};
    return [out, e2];
  }
}

function quote_interp(tree: SyntaxNode, env: Env): [SyntaxNode, Env] {
  return ast_visit(QuoteInterp, tree, env);
}

// Helper to execute to a value in an empty initial environment.
function interpret(program: SyntaxNode): Value {
  let [v, e] = interp(program, {});
  return v;
}

{'fraction_non_alphanumeric': 0.16418480336006108, 'fraction_numerical': 0.007891052564592084, 'mean_word_length': 2.7943022694350557, 'pattern_counts': {'":': 8, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '565511', 'n_tokens_mistral': 2907, 'n_tokens_neox': 2842, 'n_words': 844}