diff --git a/packages/desktop-client/src/components/SyncNotifications.js b/packages/desktop-client/src/components/SyncNotifications.js
deleted file mode 100644
index 12563a78f1553e74707b28b46452c317ae94f491..0000000000000000000000000000000000000000
--- a/packages/desktop-client/src/components/SyncNotifications.js
+++ /dev/null
@@ -1,65 +0,0 @@
-export function RepairSyncNotification() {}
-
-// TODO: sync button shouldn't show error status if it's a local file
-// and needs uploading.. should just be grayed out
-//
-// TODO: improve styling of these modals
-
-// export function NeedsUploadNotification({ actions }) {
-//   let [loading, setLoading] = useState(false);
-
-//   return (
-//     <Stack align="center" direction="row">
-//       <Text>
-//         This file is not a cloud file. You need to register it to take advantage
-//         of syncing which allows you to use it across devices and never worry
-//         about losing your data.
-//       </Text>
-//       <ButtonWithLoading
-//         bare
-//         loading={loading}
-//         onClick={async () => {
-//           setLoading(true);
-//           await actions.uploadBudget();
-//           actions.removeNotification('file-needs-upload');
-//           setLoading(false);
-
-//           actions.sync();
-//           actions.loadPrefs();
-//         }}
-//         style={{
-//           backgroundColor: 'rgba(100, 100, 100, .12)',
-//           color: colors.n1,
-//           fontSize: 14,
-//           flexShrink: 0,
-//           '&:hover, &:active': { backgroundColor: 'rgba(100, 100, 100, .25)' }
-//         }}
-//       >
-//         Register
-//       </ButtonWithLoading>
-//     </Stack>
-//   );
-// }
-
-// export function SyncResetNotification({ cloudFileId, actions }) {
-//   return (
-//     <Stack align="center" direction="row">
-//       <Text>
-//       </Text>
-//       <Button
-//         bare
-//         onClick={async () => {
-//           actions.removeNotification('out-of-date-key');
-//         }}
-//         style={{
-//           backgroundColor: colors.r10,
-//           flexShrink: 0,
-//           '&:hover': { backgroundColor: colors.r10 },
-//           '&:active': { backgroundColor: colors.r8 }
-//         }}
-//       >
-//         Revert
-//       </Button>
-//     </Stack>
-//   );
-// }
diff --git a/packages/desktop-client/src/components/debug/index.js b/packages/desktop-client/src/components/debug/index.js
deleted file mode 100644
index 0f12595ae290dccb411b7e9f3c368f4f16ce8a82..0000000000000000000000000000000000000000
--- a/packages/desktop-client/src/components/debug/index.js
+++ /dev/null
@@ -1,143 +0,0 @@
-import React, { Component } from 'react';
-
-import styled from 'styled-components';
-
-import { send } from 'loot-core/src/platform/client/fetch';
-
-const Container = styled.div`
-  width: 100%;
-  overflow: auto;
-`;
-
-const Code = styled.textarea`
-  width: 100%;
-  height: 10em;
-  font-size: 1em;
-`;
-
-const Output = styled.pre`
-  width: 100%;
-  background-color: #333333;
-  color: white;
-  padding: 0.5em;
-`;
-
-class Debug extends Component {
-  constructor() {
-    super();
-    this.state = {
-      value: localStorage.debugValue,
-      outputType: 'ast',
-      ast: null,
-      code: null,
-      sql: null,
-      sqlgenValue: localStorage.sqlgenValue,
-      sqlgenRow: localStorage.sqlgenRow,
-    };
-  }
-
-  componentDidMount() {
-    this.fetchResults(this.state.value);
-    this.fetchSqlGenResult(this.state.value);
-  }
-
-  fetchResults(value) {
-    localStorage.debugValue = value;
-
-    send('debug-ast', { code: value }).then(ast => {
-      this.setState({ ast });
-    });
-    send('debug-code', { code: value }).then(code => {
-      this.setState({ code });
-    });
-    send('debug-query', { code: value }).then(sql => {
-      this.setState({ sql });
-    });
-  }
-
-  async fetchSqlGenResult() {
-    let row = {};
-    try {
-      // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-eval
-      row = (0, eval)('(' + this.state.sqlgenRow + ')');
-    } catch (e) {}
-
-    const res = await send('debug-sqlgen', {
-      expr: this.state.sqlgenValue,
-    });
-    this.setState({ sqlgenResult: res });
-  }
-
-  processInput(e) {
-    this.setState({ value: e.target.value });
-    this.fetchResults(e.target.value);
-  }
-
-  processSqlGen(value, field) {
-    localStorage[field] = value;
-    this.setState({ [field]: value }, () => {
-      this.fetchSqlGenResult();
-    });
-  }
-
-  onInputType(e) {
-    this.setState({ outputType: e.target.value });
-  }
-
-  render() {
-    const {
-      // value,
-      // outputType,
-      // ast,
-      // code,
-      // sql,
-      sqlgenValue,
-      sqlgenRow,
-      sqlgenResult,
-    } = this.state;
-
-    return (
-      <Container>
-        {/*<h2>Debug</h2>
-        <p>Input:</p>
-        <Code value={value} onChange={this.processInput.bind(this)} />
-        <select
-          value={this.state.outputType}
-          onChange={this.onInputType.bind(this)}
-        >
-          <option value="ast">AST</option>
-          <option value="code">code</option>
-          <option value="sql">SQL</option>
-        </select>
-
-        <div style={{ display: outputType === 'ast' ? 'block' : 'none' }}>
-          <p>AST:</p>
-          <Output>{ast ? JSON.stringify(ast, null, 2) : ''}</Output>
-        </div>
-
-        <div style={{ display: outputType === 'code' ? 'block' : 'none' }}>
-          <p>Code:</p>
-          <Output>{code || ''}</Output>
-        </div>
-
-        <div style={{ display: outputType === 'sql' ? 'block' : 'none' }}>
-          <p>SQL:</p>
-          <Output>{sql || ''}</Output>
-        </div>*/}
-
-        <h3>sqlgen</h3>
-        <Code
-          value={sqlgenValue}
-          onChange={e => this.processSqlGen(e.target.value, 'sqlgenValue')}
-        />
-        <Code
-          value={sqlgenRow}
-          onChange={e => this.processSqlGen(e.target.value, 'sqlgenRow')}
-        />
-        <Output>{JSON.stringify(sqlgenResult)}</Output>
-      </Container>
-    );
-  }
-}
-
-export default Debug;
diff --git a/packages/desktop-client/src/util.ts b/packages/desktop-client/src/util.ts
deleted file mode 100644
index e506321203d4fee780dc3424d0f2a05674f2d5e7..0000000000000000000000000000000000000000
--- a/packages/desktop-client/src/util.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export function getModalRoute(name: string): [string, string] {
-  let parts = name.split('/');
-  return [parts[0], parts.slice(1).join('/')];
-}
diff --git a/packages/loot-core/src/server/api-models.ts b/packages/loot-core/src/server/api-models.ts
index 1678ae15af26d7eb0bd20530d5c290f4376e6a24..53a4ae52fc98c7e10b8b229bded0f076cc8a56da 100644
--- a/packages/loot-core/src/server/api-models.ts
+++ b/packages/loot-core/src/server/api-models.ts
@@ -1,82 +1,5 @@
 import * as models from './models';
 
-export const transactionModel = {
-  ...models.transactionModel,
-
-  toExternal(transactions, idx, payees) {
-    return transactions;
-    // function convert(t, payee) {
-    //   return {
-    //     id: t.id,
-    //     account_id: t.acct,
-    //     amount: t.amount,
-    //     payee_id: payee ? payee.id : null,
-    //     payee: payee ? payee.name : null,
-    //     imported_payee: t.imported_description,
-    //     category_id: t.category,
-    //     date: t.date,
-    //     notes: t.notes,
-    //     imported_id: t.financial_id,
-    //     transfer_id: t.transferred_id,
-    //     cleared: t.cleared
-    //   };
-    // }
-
-    // let splits = getAllSplitTransactions(transactions, idx);
-    // if (splits) {
-    //   let payee =
-    //     splits.parent.description && payees[splits.parent.description];
-
-    //   return {
-    //     ...convert(splits.parent, payee),
-    //     subtransactions: splits.children.map(child => convert(child, payee))
-    //   };
-    // }
-
-    // let transaction = transactions[idx];
-    // let payee = transaction.description && payees[transaction.description];
-    // return convert(transaction, payee);
-  },
-
-  fromExternal(transaction) {
-    let result: Record<string, unknown> = {};
-    if ('id' in transaction) {
-      result.id = transaction.id;
-    }
-    if ('account_id' in transaction) {
-      result.acct = transaction.account_id;
-    }
-    if ('amount' in transaction) {
-      result.amount = transaction.amount;
-    }
-    if ('payee_id' in transaction) {
-      result.description = transaction.payee_id;
-    }
-    if ('imported_payee' in transaction) {
-      result.imported_description = transaction.imported_payee;
-    }
-    if ('category_id' in transaction) {
-      result.category = transaction.category_id;
-    }
-    if ('date' in transaction) {
-      result.date = transaction.date;
-    }
-    if ('notes' in transaction) {
-      result.notes = transaction.notes;
-    }
-    if ('imported_id' in transaction) {
-      result.financial_id = transaction.imported_id;
-    }
-    if ('transfer_id' in transaction) {
-      result.transferred_id = transaction.transfer_id;
-    }
-    if ('cleared' in transaction) {
-      result.cleared = transaction.cleared;
-    }
-    return result;
-  },
-};
-
 export const accountModel = {
   ...models.accountModel,
 
diff --git a/packages/loot-core/src/server/aql/compiler.ts b/packages/loot-core/src/server/aql/compiler.ts
index 899cd383dd776edc66818957c3c5e01926cd7915..4898e9715dbcf071c3f52793eb98adede1003956 100644
--- a/packages/loot-core/src/server/aql/compiler.ts
+++ b/packages/loot-core/src/server/aql/compiler.ts
@@ -1118,7 +1118,3 @@ export function generateSQLWithState(
   let { sqlPieces, state } = compileQuery(queryState, schema, schemaConfig);
   return { sql: defaultConstructQuery(queryState, state, sqlPieces), state };
 }
-
-export function generateSQL(queryState) {
-  return generateSQLWithState(queryState).sql;
-}
diff --git a/packages/loot-core/src/server/errors.ts b/packages/loot-core/src/server/errors.ts
index 3d1ddccc511c7378c38fc994a3334957395c37a2..1450c447e66e0b95c2b68714398b41e5c55b9127 100644
--- a/packages/loot-core/src/server/errors.ts
+++ b/packages/loot-core/src/server/errors.ts
@@ -56,7 +56,3 @@ export function FileDownloadError(reason, meta?) {
 export function FileUploadError(reason, meta?) {
   return { type: 'FileUploadError', reason, meta };
 }
-
-export function isCodeError(err) {
-  return err instanceof ReferenceError || err instanceof SyntaxError;
-}
diff --git a/packages/loot-core/src/server/perf.ts b/packages/loot-core/src/server/perf.ts
deleted file mode 100644
index 6182c1f5a170aed7d93475efe89a20c1ead2ace7..0000000000000000000000000000000000000000
--- a/packages/loot-core/src/server/perf.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-let enabled = false;
-let entries = {};
-let counters = {};
-
-export function reset() {
-  entries = {};
-  counters = {};
-}
-
-export function record(name) {
-  const start = Date.now();
-  return () => unrecord(name, start);
-}
-
-function unrecord(name, start) {
-  const end = Date.now();
-
-  if (enabled) {
-    if (entries[name] == null) {
-      entries[name] = [];
-    }
-    entries[name].push(end - start);
-  }
-}
-
-export function increment(name) {
-  if (enabled) {
-    if (counters[name] == null) {
-      counters[name] = 0;
-    }
-    counters[name]++;
-  }
-}
-
-export function start() {
-  enabled = true;
-}
-
-export function stop() {
-  enabled = false;
-
-  console.log('~~ PERFORMANCE REPORT ~~');
-  for (let name in entries) {
-    const records = entries[name];
-    const total = records.reduce((total, n) => total + n / 1000, 0);
-    const avg = total / records.length;
-
-    console.log(
-      `[${name}] count: ${records.length} total: ${total}s avg: ${avg}`,
-    );
-  }
-
-  for (let name in counters) {
-    console.log(`[${name}] ${counters[name]}`);
-  }
-  console.log('~~ END REPORT ~~');
-
-  reset();
-}
diff --git a/packages/loot-core/src/server/spreadsheet/globals.ts b/packages/loot-core/src/server/spreadsheet/globals.ts
index fcbfbc6625149847f6f3506ea1d6b9be8d01cec5..45b65640961f556f55d0512229b8639d26c2c10b 100644
--- a/packages/loot-core/src/server/spreadsheet/globals.ts
+++ b/packages/loot-core/src/server/spreadsheet/globals.ts
@@ -1,12 +1,3 @@
-export function first(arr) {
-  return arr[0];
-}
-
-export function firstValue(arr) {
-  const keys = Object.keys(arr[0]);
-  return arr[0][keys[0]];
-}
-
 export function number(v) {
   if (typeof v === 'number') {
     return v;
@@ -20,11 +11,3 @@ export function number(v) {
 
   return 0;
 }
-
-export function min(x, y) {
-  return Math.min(x, y);
-}
-
-export function max(x, y) {
-  return Math.max(x, y);
-}
diff --git a/packages/loot-core/src/server/spreadsheet/new/REQUIREMENTS.txt b/packages/loot-core/src/server/spreadsheet/new/REQUIREMENTS.txt
deleted file mode 100644
index 4836a96ff50146b40dd6bea2d8d8a8c56c690280..0000000000000000000000000000000000000000
--- a/packages/loot-core/src/server/spreadsheet/new/REQUIREMENTS.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-* Function calls (native hooks)
-* Operators: + - / * > < >= <= =
-* Queries: from t in transactions select { amount  }
-* Types
-** Boolean (true / false)
-** Integer
-** Float
-** String
-* Variables (only global lookup)
-
-Need a stack to hold temporary values since function calls can be
-nested. Instructions:
-
-MOV
-CALL
-QUERY
-BOP
-UOP
-
-Registers:
-
-PC
-SP
-REG1
-
-Query language:
-
-=from transactions
-   where
-     date >= 20170101 and
-     date <= 20170131 and
-     category.is_income = 1
-   calculate sum(amount)
diff --git a/packages/loot-core/src/server/spreadsheet/new/__snapshots__/compiler.test.ts.snap b/packages/loot-core/src/server/spreadsheet/new/__snapshots__/compiler.test.ts.snap
deleted file mode 100644
index b8d56b78d1725c6b2dfb79cd486de4a13da31265..0000000000000000000000000000000000000000
--- a/packages/loot-core/src/server/spreadsheet/new/__snapshots__/compiler.test.ts.snap
+++ /dev/null
@@ -1,734 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Compiler basic 1`] = `
-Array [
-  Array [
-    Symbol(query),
-    "
-      SELECT sum(amount) FROM transactions
-      LEFT JOIN category_mapping __cm ON __cm.id = transactions.category
-      LEFT JOIN accounts t1 ON t1.id = transactions.acct
-LEFT JOIN banks t2 ON t2.id = t1.bank
-       WHERE ((((date >= 20170101) and (date <= 20170131)) and (t2.name = 1))) AND transactions.isParent = 0 AND transactions.tombstone = 0
-     ",
-    true,
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "index": 0,
-      "type": "__stack",
-    },
-  ],
-  Array [
-    Symbol(call),
-    Object {
-      "name": "generated!number",
-      "type": "__var",
-    },
-    Array [
-      Object {
-        "index": 0,
-        "type": "__stack",
-      },
-    ],
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "index": 0,
-      "type": "__stack",
-    },
-  ],
-  Array [
-    Symbol(call),
-    Object {
-      "name": "generated!first",
-      "type": "__var",
-    },
-    Array [
-      Object {
-        "index": 0,
-        "type": "__stack",
-      },
-    ],
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "name": "generated!result",
-      "type": "__var",
-    },
-  ],
-]
-`;
-
-exports[`Compiler basic 2`] = `
-Array [
-  Array [
-    Symbol(mov),
-    "",
-    Object {
-      "name": "generated!result",
-      "type": "__var",
-    },
-  ],
-]
-`;
-
-exports[`Compiler compiler binary ops 1`] = `
-Array [
-  Array [
-    Symbol(mov),
-    Object {
-      "name": "generated!bar",
-      "type": "__var",
-    },
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(bop),
-    "+",
-    Object {
-      "name": "generated!foo",
-      "type": "__var",
-    },
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "index": 0,
-      "type": "__stack",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "name": "generated!baz",
-      "type": "__var",
-    },
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(bop),
-    "+",
-    Object {
-      "index": 0,
-      "type": "__stack",
-    },
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "index": 0,
-      "type": "__stack",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "name": "generated!boo",
-      "type": "__var",
-    },
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(bop),
-    "+",
-    Object {
-      "index": 0,
-      "type": "__stack",
-    },
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "name": "generated!result",
-      "type": "__var",
-    },
-  ],
-]
-`;
-
-exports[`Compiler compiler nested funcs 1`] = `
-Array [
-  Array [
-    Symbol(mov),
-    0,
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "index": 0,
-      "type": "__stack",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    -20000,
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "index": 1,
-      "type": "__stack",
-    },
-  ],
-  Array [
-    Symbol(call),
-    Object {
-      "name": "generated!number",
-      "type": "__var",
-    },
-    Array [
-      Object {
-        "index": 1,
-        "type": "__stack",
-      },
-    ],
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "index": 1,
-      "type": "__stack",
-    },
-  ],
-  Array [
-    Symbol(call),
-    Object {
-      "name": "generated!min",
-      "type": "__var",
-    },
-    Array [
-      Object {
-        "index": 0,
-        "type": "__stack",
-      },
-      Object {
-        "index": 1,
-        "type": "__stack",
-      },
-    ],
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "name": "generated!result",
-      "type": "__var",
-    },
-  ],
-]
-`;
-
-exports[`Compiler compiles boolean types 1`] = `
-Array [
-  Array [
-    Symbol(mov),
-    true,
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "index": 0,
-      "type": "__stack",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    1,
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(bop),
-    "and",
-    Object {
-      "index": 0,
-      "type": "__stack",
-    },
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "index": 0,
-      "type": "__stack",
-    },
-  ],
-  Array [
-    Symbol(jumpf),
-    Object {
-      "index": 0,
-      "type": "__stack",
-    },
-    Object {
-      "get": [Function],
-      "resolve": [Function],
-    },
-  ],
-  Array [
-    Symbol(mov),
-    0,
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(jumpt),
-    Object {
-      "index": 0,
-      "type": "__stack",
-    },
-    Object {
-      "get": [Function],
-      "resolve": [Function],
-    },
-  ],
-  Array [
-    Symbol(mov),
-    1,
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "name": "generated!result",
-      "type": "__var",
-    },
-  ],
-]
-`;
-
-exports[`Compiler complex query expressions 1`] = `
-Array [
-  Array [
-    Symbol(query),
-    "
-      SELECT substr(date,0,7), sum(amount) FROM transactions
-      LEFT JOIN category_mapping __cm ON __cm.id = transactions.category
-      
-      WHERE transactions.isParent = 0 AND transactions.tombstone = 0
-      GROUP BY substr(date,0,7)",
-    false,
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "name": "generated!result",
-      "type": "__var",
-    },
-  ],
-]
-`;
-
-exports[`Compiler field dependencies 1`] = `
-Array [
-  "acct",
-  "category",
-  "description",
-  "isParent",
-  "tombstone",
-  "date",
-]
-`;
-
-exports[`Compiler parens 1`] = `
-Array [
-  Array [
-    Symbol(mov),
-    1,
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "index": 0,
-      "type": "__stack",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    2,
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(bop),
-    "+",
-    Object {
-      "index": 0,
-      "type": "__stack",
-    },
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "name": "generated!result",
-      "type": "__var",
-    },
-  ],
-]
-`;
-
-exports[`Compiler parens 2`] = `
-Array [
-  Array [
-    Symbol(mov),
-    1232,
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "index": 0,
-      "type": "__stack",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    2,
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(bop),
-    "+",
-    Object {
-      "index": 0,
-      "type": "__stack",
-    },
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "index": 0,
-      "type": "__stack",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    3,
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "index": 2,
-      "type": "__stack",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    4,
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(bop),
-    "+",
-    Object {
-      "index": 2,
-      "type": "__stack",
-    },
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(bop),
-    "-",
-    Object {
-      "index": 0,
-      "type": "__stack",
-    },
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "name": "generated!result",
-      "type": "__var",
-    },
-  ],
-]
-`;
-
-exports[`Compiler query expressions 1`] = `
-Array [
-  Array [
-    Symbol(query),
-    "
-      SELECT sum(amount) as a FROM transactions
-      LEFT JOIN category_mapping __cm ON __cm.id = transactions.category
-      
-       WHERE ((amount > 0)) AND transactions.isParent = 0 AND transactions.tombstone = 0
-     ",
-    false,
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "name": "generated!result",
-      "type": "__var",
-    },
-  ],
-]
-`;
-
-exports[`Compiler query expressions with field remapping 1`] = `
-Array [
-  Array [
-    Symbol(query),
-    "
-      SELECT id FROM transactions
-      LEFT JOIN category_mapping __cm ON __cm.id = transactions.category
-      
-       WHERE ((__cm.transferId = \\"50\\")) AND transactions.isParent = 0 AND transactions.tombstone = 0
-     ",
-    false,
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "name": "generated!result",
-      "type": "__var",
-    },
-  ],
-]
-`;
-
-exports[`Compiler query expressions with field remapping 2`] = `
-Array [
-  Array [
-    Symbol(query),
-    "
-      SELECT id FROM transactions
-      LEFT JOIN category_mapping __cm ON __cm.id = transactions.category
-      LEFT JOIN categories t1 ON __cm.transferId = t1.id
-       WHERE ((t1.name = \\"foo\\")) AND transactions.isParent = 0 AND transactions.tombstone = 0
-     ",
-    false,
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "name": "generated!result",
-      "type": "__var",
-    },
-  ],
-]
-`;
-
-exports[`Compiler query expressions with field remapping 3`] = `
-Array [
-  Array [
-    Symbol(query),
-    "
-      SELECT id, t1.name FROM transactions
-      LEFT JOIN category_mapping __cm ON __cm.id = transactions.category
-      LEFT JOIN categories t1 ON __cm.transferId = t1.id
-       WHERE ((__cm.transferId = \\"50\\")) AND transactions.isParent = 0 AND transactions.tombstone = 0
-     ",
-    false,
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "name": "generated!result",
-      "type": "__var",
-    },
-  ],
-]
-`;
-
-exports[`Compiler query expressions with null 1`] = `
-Array [
-  Array [
-    Symbol(query),
-    "
-      SELECT count(amount) FROM transactions
-      LEFT JOIN category_mapping __cm ON __cm.id = transactions.category
-      LEFT JOIN accounts t1 ON t1.id = transactions.acct
-       WHERE (((t1.offbudget = 0) and (__cm.transferId IS NULL))) AND transactions.isParent = 0 AND transactions.tombstone = 0
-     ",
-    true,
-  ],
-  Array [
-    Symbol(mov),
-    Object {
-      "index": 1,
-      "type": "__reg",
-    },
-    Object {
-      "name": "generated!result",
-      "type": "__var",
-    },
-  ],
-]
-`;
diff --git a/packages/loot-core/src/server/spreadsheet/new/__snapshots__/lexer.test.ts.snap b/packages/loot-core/src/server/spreadsheet/new/__snapshots__/lexer.test.ts.snap
deleted file mode 100644
index f3f36d7e4de5c30509e6fa4b717141eb0304390b..0000000000000000000000000000000000000000
--- a/packages/loot-core/src/server/spreadsheet/new/__snapshots__/lexer.test.ts.snap
+++ /dev/null
@@ -1,50 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`lexer basic 1`] = `
-Array [
-  Object {
-    "colno": 0,
-    "lineno": 0,
-    "type": "whitespace",
-    "value": "
-    ",
-  },
-  Object {
-    "colno": 5,
-    "lineno": 1,
-    "type": "symbol",
-    "value": "x",
-  },
-  Object {
-    "colno": 6,
-    "lineno": 1,
-    "type": "whitespace",
-    "value": " ",
-  },
-  Object {
-    "colno": 7,
-    "lineno": 1,
-    "type": "operator",
-    "value": "!=~",
-  },
-  Object {
-    "colno": 10,
-    "lineno": 1,
-    "type": "whitespace",
-    "value": " ",
-  },
-  Object {
-    "colno": 11,
-    "lineno": 1,
-    "type": "int",
-    "value": "4",
-  },
-  Object {
-    "colno": 12,
-    "lineno": 1,
-    "type": "whitespace",
-    "value": "
-  ",
-  },
-]
-`;
diff --git a/packages/loot-core/src/server/spreadsheet/new/__snapshots__/vm.test.ts.snap b/packages/loot-core/src/server/spreadsheet/new/__snapshots__/vm.test.ts.snap
deleted file mode 100644
index 663b19a0f4c6707536b280a67d5f4d23611eda02..0000000000000000000000000000000000000000
--- a/packages/loot-core/src/server/spreadsheet/new/__snapshots__/vm.test.ts.snap
+++ /dev/null
@@ -1,15 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`vm basic 1`] = `
-Object {
-  "firstValue": [Function],
-  "generated!result": -6,
-  "number": [Function],
-}
-`;
-
-exports[`vm boolean types 1`] = `
-Object {
-  "generated!result": 0,
-}
-`;
diff --git a/packages/loot-core/src/server/spreadsheet/new/compiler.test.ts b/packages/loot-core/src/server/spreadsheet/new/compiler.test.ts
deleted file mode 100644
index b1796ebd0a1b86a0d910e43c3171cac5d0995746..0000000000000000000000000000000000000000
--- a/packages/loot-core/src/server/spreadsheet/new/compiler.test.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-import { compile } from './compiler';
-
-describe('Compiler', () => {
-  test('get-query', () => {
-    compile(
-      '=from transactions where acct.offbudget = 0 and category = null and (description.transfer_acct.offbudget = 1 or description.transfer_acct = null) calculate { count(date) }',
-    );
-  });
-
-  test('basic', () => {
-    let ops = compile(`
-    =first(number(from transactions
-       where
-         date >= 20170101 and
-         date <= 20170131 and
-         acct.bank.name = 1
-       calculate { sum(amount) }))
-  `).ops;
-    expect(ops).toMatchSnapshot();
-
-    ops = compile('').ops;
-    expect(ops).toMatchSnapshot();
-  });
-
-  test('parens', () => {
-    let ops = compile('=(1 + 2)').ops;
-    expect(ops).toMatchSnapshot();
-
-    ops = compile('=(1232 + 2) - (3 + 4)').ops;
-    expect(ops).toMatchSnapshot();
-  });
-
-  test('compiler binary ops', () => {
-    let ops = compile('=foo + bar + baz + boo').ops;
-    expect(ops).toMatchSnapshot();
-  });
-
-  test('compiler nested funcs', () => {
-    let ops = compile('=min(0, number(-20000))').ops;
-    expect(ops).toMatchSnapshot();
-  });
-
-  test('compiles boolean types', () => {
-    let ops = compile('=if(true and 1) { 0 } else { 1 } ').ops;
-    expect(ops).toMatchSnapshot();
-  });
-
-  test('query expressions', () => {
-    let ops = compile(`
-      =from transactions
-         where amount > 0
-         select { sum(amount) as a }
-    `).ops;
-    expect(ops).toMatchSnapshot();
-  });
-
-  test('query expressions with null', () => {
-    let ops = compile(`
-  =from transactions where acct.offbudget = 0 and category = null calculate { count(amount) }
-    `).ops;
-    expect(ops).toMatchSnapshot();
-  });
-
-  test('complex query expressions', () => {
-    let ops = compile(`
-      =from transactions groupby substr(date, 0, 7) select { substr(date, 0, 7), sum(amount) }
-    `).ops;
-    expect(ops).toMatchSnapshot();
-  });
-
-  test('query expressions with field remapping', () => {
-    let ops = compile(`
-      =from transactions where category = "50" select { id }
-    `).ops;
-    expect(ops).toMatchSnapshot();
-
-    ops = compile(`
-      =from transactions where category.name = "foo" select { id }
-    `).ops;
-    expect(ops).toMatchSnapshot();
-
-    ops = compile(`
-      =from transactions where category = "50" select { id, category.name }
-    `).ops;
-    expect(ops).toMatchSnapshot();
-  });
-
-  test('field dependencies', () => {
-    let sqlDependencies = compile(
-      '=from transactions where acct.offbudget = 0 and category = null and (description.transfer_acct.offbudget = 1 or description.transfer_acct = null) calculate { count(date) }',
-    ).sqlDependencies;
-
-    expect(sqlDependencies[0].fields).toMatchSnapshot();
-  });
-});
diff --git a/packages/loot-core/src/server/spreadsheet/new/compiler.ts b/packages/loot-core/src/server/spreadsheet/new/compiler.ts
deleted file mode 100644
index cfcf8942622fff41678430c94542c699d815b79e..0000000000000000000000000000000000000000
--- a/packages/loot-core/src/server/spreadsheet/new/compiler.ts
+++ /dev/null
@@ -1,193 +0,0 @@
-import getSqlFields from './get-sql-fields';
-import * as nodes from './nodes';
-import {
-  MOV,
-  CALL,
-  QUERY,
-  UOP,
-  BOP,
-  REG1,
-  SP,
-  VAR,
-  JUMPF,
-  JUMPT,
-  LABEL,
-} from './ops';
-import parse from './parser';
-import generateSql from './sqlgen';
-
-class Compiler {
-  src;
-  scopeName;
-  binding;
-  ops;
-  dependencies;
-  sqlDependencies;
-
-  constructor() {
-    this.ops = [];
-    this.dependencies = [];
-    this.sqlDependencies = [];
-  }
-
-  fail(msg, lineno, colno) {
-    const lines = this.src.split('\n');
-
-    let space = '';
-    for (let i = 0; i < colno; i++) {
-      space += ' ';
-    }
-
-    throw new Error(
-      `[${lineno + 1}, ${colno + 1}] ${msg}:\n${lines[lineno]}\n${space}^`,
-    );
-  }
-
-  resolveVariable(name) {
-    if (name.indexOf('!') === -1) {
-      return this.scopeName + '!' + name;
-    }
-    return name;
-  }
-
-  maybePushStack(node, si) {
-    if (node instanceof nodes.Symbol) {
-      // There's no need to push anything to the stack since it's a
-      // direct variable reference. Just store the referenced variable
-      // and pop the symbol operation off the stack.
-      const op = this.ops.pop();
-      return [si, op[1]];
-    }
-
-    this.ops.push([MOV, REG1, SP(si)]);
-    return [si + 1, SP(si)];
-  }
-
-  compileLiteral(node, si) {
-    this.ops.push([MOV, node.value, REG1]);
-  }
-
-  compileSymbol(node, si) {
-    const resolved = this.resolveVariable(node.value);
-    this.dependencies.push(resolved);
-    this.ops.push([MOV, VAR(resolved), REG1]);
-  }
-
-  compileBinOp(node, si) {
-    this.compile(node.left, si);
-    // TODO: Get rid of all this and add a second pass which optimizes
-    // the opcodes.
-    let left;
-    [si, left] = this.maybePushStack(node.left, si);
-
-    this.compile(node.right, si + 1);
-    this.ops.push([BOP, node.op, left, REG1]);
-  }
-
-  compileUnaryOp(node, si) {
-    this.compile(node.target, si);
-    this.ops.push([UOP, node.op, REG1]);
-  }
-
-  compileFunCall(node, si) {
-    this.compile(node.callee, si);
-    let callee;
-    [si, callee] = this.maybePushStack(node.callee, si);
-
-    const args = node.args.children.map((arg, i) => {
-      this.compile(arg, si + i);
-      this.ops.push([MOV, REG1, SP(si + i)]);
-      return SP(si + i);
-    });
-
-    this.ops.push([CALL, callee, args]);
-  }
-
-  compileQuery(node, si) {
-    let fields = getSqlFields(node.table, node.where)
-      .concat(getSqlFields(node.table, node.groupby))
-      .concat(...node.select.map(s => getSqlFields(node.table, s.expr)));
-
-    const { sql, where } = generateSql(
-      node.table,
-      node.where,
-      node.groupby,
-      node.select,
-    );
-
-    // TODO: This is a hack, but I'm pretty sure we can get rid of all
-    // of this. Just need to think through it.
-    fields = fields.map(f => (f === '__cm.transferId' ? 'category' : f));
-
-    // Uniquify them
-    fields = [...new Set(fields)];
-
-    this.sqlDependencies.push({ table: node.table, where, fields });
-    this.ops.push([QUERY, sql, node.calculated]);
-  }
-
-  compileIf(node, si) {
-    const L0 = LABEL();
-    const L1 = LABEL();
-
-    this.compile(node.cond, si);
-    this.ops.push([MOV, REG1, SP(si)]);
-
-    this.ops.push([JUMPF, SP(si), L0]);
-    this.compile(node.body, si + 1);
-
-    this.ops.push([JUMPT, SP(si), L1]);
-    L0.resolve(this.ops.length - 1);
-    this.compile(node.else_, si + 1);
-    L1.resolve(this.ops.length - 1);
-  }
-
-  compileRoot(node, si) {
-    node.children.forEach(node => {
-      this.compile(node, si);
-    });
-  }
-
-  compile(node, si) {
-    const method = this['compile' + node.getTypeName()];
-    if (!method) {
-      this.fail(
-        'Unknown node type: ' + node.getTypeName(),
-        node.lineno,
-        node.colno,
-      );
-    }
-    return method.call(this, node, si);
-  }
-
-  compileSource(binding, scopeName, src) {
-    this.src = src;
-    this.scopeName = scopeName;
-    this.binding = binding;
-
-    this.compile(parse(src), 0);
-
-    const resolvedBinding = this.resolveVariable(binding);
-
-    if (this.ops.length !== 0) {
-      this.ops.push([MOV, REG1, VAR(resolvedBinding)]);
-    } else {
-      this.ops.push([MOV, '', VAR(resolvedBinding)]);
-    }
-
-    return {
-      ops: this.ops,
-      dependencies: this.dependencies,
-      sqlDependencies: this.sqlDependencies,
-    };
-  }
-}
-
-export function compile(src) {
-  return compileBinding('result', 'generated', src);
-}
-
-export function compileBinding(binding, scopeName, src) {
-  const compiler = new Compiler();
-  return compiler.compileSource(binding, scopeName, src);
-}
diff --git a/packages/loot-core/src/server/spreadsheet/new/get-sql-fields.ts b/packages/loot-core/src/server/spreadsheet/new/get-sql-fields.ts
deleted file mode 100644
index ff71f705cf711690692f3fb859c8c74fddfbb654..0000000000000000000000000000000000000000
--- a/packages/loot-core/src/server/spreadsheet/new/get-sql-fields.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-function traverse(expr, fields) {
-  switch (expr.getTypeName()) {
-    case 'FunCall':
-      expr.args.children.map(arg => traverse(arg, fields));
-      break;
-
-    case 'Member':
-      // Right now we only track dependencies on the top-level table,
-      // and not any of the joined data. This tracks that field itself
-      // that is joined on, but not the joined data yet.
-      traverse(expr.object, fields);
-      break;
-
-    case 'Literal':
-      break;
-
-    case 'Symbol':
-      if (fields.indexOf(expr.value) === -1 && expr.value !== 'null') {
-        fields.push(expr.value);
-      }
-      break;
-
-    case 'BinOp':
-      traverse(expr.left, fields);
-      traverse(expr.right, fields);
-      break;
-    default:
-      throw new Error('Unhandled node type: ' + expr.getTypeName());
-  }
-}
-
-export default function getSqlFields(table, ast) {
-  let fields: string[] = [];
-  if (!ast) {
-    return fields;
-  }
-
-  traverse(ast, fields);
-
-  // These are implicit fields added by the sql generator. Going to
-  // revisit how to track all of this.
-  if (table === 'transactions') {
-    fields.push('isParent');
-    fields.push('tombstone');
-  }
-
-  return fields;
-}
diff --git a/packages/loot-core/src/server/spreadsheet/new/lexer.test.ts b/packages/loot-core/src/server/spreadsheet/new/lexer.test.ts
deleted file mode 100644
index c1feaefd439b3a39bcf2b780f4f33cd724ef5262..0000000000000000000000000000000000000000
--- a/packages/loot-core/src/server/spreadsheet/new/lexer.test.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import lex from './lexer';
-
-function getTokens(tokens) {
-  const toks = [];
-  while (!tokens.is_finished()) {
-    toks.push(tokens.nextToken());
-  }
-  return toks;
-}
-
-test('lexer basic', () => {
-  const tokens = lex(`
-    =x !=~ 4
-  `);
-
-  expect(getTokens(tokens)).toMatchSnapshot();
-});
diff --git a/packages/loot-core/src/server/spreadsheet/new/lexer.ts b/packages/loot-core/src/server/spreadsheet/new/lexer.ts
deleted file mode 100644
index aabd626fef66c51b8955fd7b88c5818bcb369240..0000000000000000000000000000000000000000
--- a/packages/loot-core/src/server/spreadsheet/new/lexer.ts
+++ /dev/null
@@ -1,310 +0,0 @@
-const whitespaceChars = new Set(' \n\t\r\u00A0');
-const delimChars = new Set('()[]{}%*-+~/#,:|.<>=!');
-const whitespaceAndDelimChars = new Set([...whitespaceChars, ...delimChars]);
-const intChars = new Set('0123456789');
-
-const complexOps = new Set(['==', '!=', '<=', '>=', '=~', '!=~']);
-
-export const TOKEN_STRING = 'string';
-export const TOKEN_WHITESPACE = 'whitespace';
-export const TOKEN_LEFT_PAREN = 'left-paren';
-export const TOKEN_RIGHT_PAREN = 'right-paren';
-export const TOKEN_LEFT_BRACKET = 'left-bracket';
-export const TOKEN_RIGHT_BRACKET = 'right-bracket';
-export const TOKEN_LEFT_CURLY = 'left-curly';
-export const TOKEN_RIGHT_CURLY = 'right-curly';
-export const TOKEN_COMMA = 'comma';
-export const TOKEN_INT = 'int';
-export const TOKEN_FLOAT = 'float';
-export const TOKEN_BOOLEAN = 'boolean';
-export const TOKEN_SYMBOL = 'symbol';
-export const TOKEN_DOT = 'dot';
-export const TOKEN_EXCLAIM = 'exclaim';
-export const TOKEN_OPERATOR = 'operator';
-
-function token(type, value, lineno, colno) {
-  return {
-    type: type,
-    value: value,
-    lineno: lineno,
-    colno: colno,
-  };
-}
-
-class Tokenizer {
-  colno;
-  hasCheckedMode;
-  index;
-  len;
-  lineno;
-  str;
-
-  constructor(str, opts = {}) {
-    this.str = str;
-    this.index = 0;
-    this.len = str.length;
-    this.lineno = 0;
-    this.colno = 0;
-    this.hasCheckedMode = false;
-  }
-
-  nextToken() {
-    let lineno = this.lineno;
-    let colno = this.colno;
-    let tok;
-    let cur = this.current();
-
-    if (this.is_finished()) {
-      return null;
-    } else if ((tok = this._extract(whitespaceChars))) {
-      // We hit some whitespace
-      return token(TOKEN_WHITESPACE, tok, lineno, colno);
-    } else if (!this.hasCheckedMode) {
-      this.hasCheckedMode = true;
-
-      if (cur === '=') {
-        this.forward();
-        cur = this.current();
-        return this.nextToken();
-      } else {
-        this.index = this.str.length;
-        return token(TOKEN_STRING, this.str, lineno, colno);
-      }
-      // eslint-disable-next-line rulesdir/typography
-    } else if (cur === '"' || cur === "'") {
-      // We've hit a string
-      return token(TOKEN_STRING, this.parseString(cur), lineno, colno);
-    } else if (delimChars.has(cur)) {
-      // We've hit a delimiter (a special char like a bracket)
-      let type;
-
-      if (complexOps.has(cur + this.next() + this.next(2))) {
-        cur = cur + this.next() + this.next(2);
-        this.forward();
-        this.forward();
-      } else if (complexOps.has(cur + this.next())) {
-        cur = cur + this.next();
-        this.forward();
-      }
-      this.forward();
-
-      switch (cur) {
-        case '(':
-          type = TOKEN_LEFT_PAREN;
-          break;
-        case ')':
-          type = TOKEN_RIGHT_PAREN;
-          break;
-        case '[':
-          type = TOKEN_LEFT_BRACKET;
-          break;
-        case ']':
-          type = TOKEN_RIGHT_BRACKET;
-          break;
-        case '{':
-          type = TOKEN_LEFT_CURLY;
-          break;
-        case '}':
-          type = TOKEN_RIGHT_CURLY;
-          break;
-        case ',':
-          type = TOKEN_COMMA;
-          break;
-        case '.':
-          type = TOKEN_DOT;
-          break;
-        case '!':
-          type = TOKEN_EXCLAIM;
-          break;
-        default:
-          type = TOKEN_OPERATOR;
-      }
-
-      return token(type, cur, lineno, colno);
-    } else {
-      // We are not at whitespace or a delimiter, so extract the
-      // text and parse it
-      tok = this._extractUntil(whitespaceAndDelimChars);
-
-      if (tok.match(/^[-+]?[0-9]+$/)) {
-        if (this.current() === '.') {
-          this.forward();
-          let dec = this._extract(intChars);
-          return token(TOKEN_FLOAT, tok + '.' + dec, lineno, colno);
-        } else {
-          return token(TOKEN_INT, tok, lineno, colno);
-        }
-      } else if (tok.match(/^(true|false)$/)) {
-        return token(TOKEN_BOOLEAN, tok, lineno, colno);
-      } else if (tok.match(/^(or|and|not)$/)) {
-        return token(TOKEN_OPERATOR, tok, lineno, colno);
-      } else if (tok) {
-        return token(TOKEN_SYMBOL, tok, lineno, colno);
-      } else {
-        throw new Error('Unexpected value while parsing: ' + tok);
-      }
-    }
-  }
-
-  parseString(delimiter) {
-    this.forward();
-
-    let str = '';
-
-    while (!this.is_finished() && this.current() !== delimiter) {
-      let cur = this.current();
-
-      if (cur === '\\') {
-        this.forward();
-        switch (this.current()) {
-          case 'n':
-            str += '\n';
-            break;
-          case 't':
-            str += '\t';
-            break;
-          case 'r':
-            str += '\r';
-            break;
-          default:
-            str += this.current();
-        }
-        this.forward();
-      } else {
-        str += cur;
-        this.forward();
-      }
-    }
-
-    this.forward();
-    return str;
-  }
-
-  _matches(str) {
-    if (this.index + str.length > this.len) {
-      return null;
-    }
-
-    let m = this.str.slice(this.index, this.index + str.length);
-    return m === str;
-  }
-
-  _extractString(str) {
-    if (this._matches(str)) {
-      this.index += str.length;
-      return str;
-    }
-    return null;
-  }
-
-  _extractUntil(chars) {
-    // Extract all non-matching chars, with the default matching set
-    // to everything
-    return this._extractMatching(true, chars || new Set());
-  }
-
-  _extract(chars) {
-    // Extract all matching chars (no default, so charString must be
-    // explicit)
-    return this._extractMatching(false, chars);
-  }
-
-  _extractMatching(breakOnMatch, chars) {
-    // Pull out characters until a breaking char is hit.
-    // If breakOnMatch is false, a non-matching char stops it.
-    // If breakOnMatch is true, a matching char stops it.
-
-    if (this.is_finished()) {
-      return null;
-    }
-
-    let matches = chars.has(this.current());
-
-    // Only proceed if the first character meets our condition
-    if ((breakOnMatch && !matches) || (!breakOnMatch && matches)) {
-      let t = this.current();
-      this.forward();
-
-      // And pull out all the chars one at a time until we hit a
-      // breaking char
-      let isMatch = chars.has(this.current());
-
-      while (
-        ((breakOnMatch && !isMatch) || (!breakOnMatch && isMatch)) &&
-        !this.is_finished()
-      ) {
-        t += this.current();
-        this.forward();
-
-        isMatch = chars.has(this.current());
-      }
-
-      return t;
-    }
-
-    return '';
-  }
-
-  is_finished() {
-    return this.index >= this.len;
-  }
-
-  forward() {
-    this.index++;
-
-    if (this.previous() === '\n') {
-      this.lineno++;
-      this.colno = 0;
-    } else {
-      this.colno++;
-    }
-  }
-
-  back() {
-    this.index--;
-
-    if (this.current() === '\n') {
-      this.lineno--;
-
-      let idx = this.str.lastIndexOf('\n', this.index - 1);
-      if (idx === -1) {
-        this.colno = this.index;
-      } else {
-        this.colno = this.index - idx;
-      }
-    } else {
-      this.colno--;
-    }
-  }
-
-  // current returns current character
-  current() {
-    if (!this.is_finished()) {
-      return this.str.charAt(this.index);
-    }
-    return '';
-  }
-
-  next(idx = 1) {
-    if (this.index + idx < this.str.length) {
-      return this.str.charAt(this.index + idx);
-    }
-    return '';
-  }
-
-  // currentStr returns what's left of the unparsed string
-  currentStr() {
-    if (!this.is_finished()) {
-      return this.str.substr(this.index);
-    }
-    return '';
-  }
-
-  previous() {
-    return this.str.charAt(this.index - 1);
-  }
-}
-
-export default function lex(src, opts?: Record<string, unknown>) {
-  return new Tokenizer(src, opts);
-}
diff --git a/packages/loot-core/src/server/spreadsheet/new/nodes.ts b/packages/loot-core/src/server/spreadsheet/new/nodes.ts
deleted file mode 100644
index 54fa0603a815f22cfe4a9163e89edd60d5088490..0000000000000000000000000000000000000000
--- a/packages/loot-core/src/server/spreadsheet/new/nodes.ts
+++ /dev/null
@@ -1,201 +0,0 @@
-class Node {
-  colno;
-  fieldNames;
-  lineno;
-
-  constructor(lineno, colno, fieldNames) {
-    this.lineno = lineno;
-    this.colno = colno;
-    this.fieldNames = fieldNames;
-  }
-
-  getTypeName() {
-    return 'Node';
-  }
-
-  traverseFields(onEnter, onExit) {
-    const fieldNames = this.fieldNames;
-    for (let i = 0; i < fieldNames.length; i++) {
-      const val = this[fieldNames[i]];
-
-      if (val instanceof Node) {
-        const ret = val.traverse(onEnter, onExit);
-        if (ret) {
-          this[fieldNames[i]] = ret;
-        }
-      }
-    }
-  }
-
-  traverse(onEnter, onExit) {
-    if (onEnter) {
-      const val = onEnter(this);
-      if (val === true) {
-        return;
-      } else if (val != null) {
-        return val;
-      }
-    }
-    this.traverseFields(onEnter, onExit);
-    onExit && onExit(this);
-  }
-
-  copy() {
-    const inst = Object.assign(
-      Object.create(Object.getPrototypeOf(this)),
-      this,
-    );
-
-    for (let i = 0; i < inst.fieldNames.length; i++) {
-      const field = inst.fieldNames[i];
-      if (inst[field] instanceof Node) {
-        inst[field] = inst[field].copy();
-      }
-    }
-
-    return inst;
-  }
-}
-
-export class NodeList extends Node {
-  children;
-
-  constructor(lineno, colno, nodes: unknown[] = []) {
-    super(lineno, colno, ['children']);
-    this.children = nodes;
-  }
-
-  getTypeName() {
-    return 'NodeList';
-  }
-
-  addChild(node) {
-    this.children.push(node);
-  }
-
-  traverseFields(onEnter, onExit) {
-    for (let i = 0; i < this.children.length; i++) {
-      this.children[i].traverse(onEnter, onExit);
-    }
-  }
-}
-
-export class Root extends NodeList {
-  getTypeName() {
-    return 'Root';
-  }
-}
-
-export class Value extends Node {
-  value;
-
-  constructor(lineno, colno, value) {
-    super(lineno, colno, ['value']);
-    this.value = value ?? null;
-  }
-  getTypeName() {
-    return 'Value';
-  }
-}
-export class UnaryOp extends Node {
-  op;
-  target;
-
-  constructor(lineno, colno, op, target) {
-    super(lineno, colno, ['op', 'target']);
-    this.op = op ?? null;
-    this.target = target ?? null;
-  }
-  getTypeName() {
-    return 'UnaryOp';
-  }
-}
-export class BinOp extends Node {
-  op;
-  left;
-  right;
-
-  constructor(lineno, colno, op, left, right) {
-    super(lineno, colno, ['op', 'left', 'right']);
-    this.op = op ?? null;
-    this.left = left ?? null;
-    this.right = right ?? null;
-  }
-  getTypeName() {
-    return 'BinOp';
-  }
-}
-
-export class Literal extends Value {
-  getTypeName() {
-    return 'Literal';
-  }
-}
-export class Symbol extends Value {
-  getTypeName() {
-    return 'Symbol';
-  }
-}
-export class FunCall extends Node {
-  callee;
-  args;
-
-  constructor(lineno, colno, callee, args) {
-    super(lineno, colno, ['callee', 'args']);
-    this.callee = callee ?? null;
-    this.args = args ?? null;
-  }
-  getTypeName() {
-    return 'FunCall';
-  }
-}
-
-export class Member extends Node {
-  object;
-  property;
-
-  constructor(lineno, colno, object, property) {
-    super(lineno, colno, ['object', 'property']);
-    this.object = object ?? null;
-    this.property = property ?? null;
-  }
-  getTypeName() {
-    return 'Member';
-  }
-}
-
-export class Query extends Node {
-  table;
-  select;
-  where;
-  groupby;
-  calculated;
-
-  constructor(lineno, colno, table, select, where, groupby, calculated) {
-    super(lineno, colno, ['table', 'select', 'where', 'groupby', 'calculated']);
-    this.table = table ?? null;
-    this.select = select ?? null;
-    this.where = where ?? null;
-    this.groupby = groupby ?? null;
-    this.calculated = calculated ?? null;
-  }
-  getTypeName() {
-    return 'Query';
-  }
-}
-
-export class If extends Node {
-  cond;
-  body;
-  else_;
-
-  constructor(lineno, colno, cond, body, else_) {
-    super(lineno, colno, ['cond', 'body', 'else_']);
-    this.cond = cond ?? null;
-    this.body = body ?? null;
-    this.else_ = else_ ?? null;
-  }
-  getTypeName() {
-    return 'If';
-  }
-}
diff --git a/packages/loot-core/src/server/spreadsheet/new/ops.ts b/packages/loot-core/src/server/spreadsheet/new/ops.ts
deleted file mode 100644
index 897139ad7e51b0dc794f0b91cdb0cd8151f5378c..0000000000000000000000000000000000000000
--- a/packages/loot-core/src/server/spreadsheet/new/ops.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-export const MOV = Symbol('mov');
-export const CALL = Symbol('call');
-export const QUERY = Symbol('query');
-export const UOP = Symbol('uop');
-export const BOP = Symbol('bop');
-export const JUMPF = Symbol('jumpf');
-export const JUMPT = Symbol('jumpt');
-
-export const REG1 = { type: '__reg', index: 1 };
-
-export function SP(n) {
-  return { type: '__stack', index: n };
-}
-
-export function VAR(name) {
-  return { type: '__var', name: name };
-}
-
-export function LABEL() {
-  let idx = null;
-  return {
-    get() {
-      if (idx === null) {
-        throw new Error('Attempted access of unresolved label');
-      }
-      return idx;
-    },
-
-    resolve(n) {
-      idx = n;
-    },
-  };
-}
diff --git a/packages/loot-core/src/server/spreadsheet/new/parser.ts b/packages/loot-core/src/server/spreadsheet/new/parser.ts
deleted file mode 100644
index 635b783f0b4994f981ccbfa964ec7a18670a4acc..0000000000000000000000000000000000000000
--- a/packages/loot-core/src/server/spreadsheet/new/parser.ts
+++ /dev/null
@@ -1,462 +0,0 @@
-import lex, * as types from './lexer';
-import * as nodes from './nodes';
-
-function nextToken(state, withWhitespace?: boolean) {
-  let tok;
-  let { peeked, tokens } = state;
-
-  if (peeked) {
-    if (!withWhitespace && peeked.type === types.TOKEN_WHITESPACE) {
-      state.peeked = null;
-    } else {
-      tok = state.peeked;
-      state.peeked = null;
-      return tok;
-    }
-  }
-
-  tok = tokens.nextToken();
-
-  if (!withWhitespace) {
-    while (tok && tok.type === types.TOKEN_WHITESPACE) {
-      tok = tokens.nextToken();
-    }
-  }
-
-  return tok;
-}
-
-function peekToken(state) {
-  state.peeked = state.peeked || nextToken(state);
-  return state.peeked;
-}
-
-function pushToken(state, tok) {
-  if (state.peeked) {
-    throw new Error('pushToken: can only push one token on between reads');
-  }
-  state.peeked = tok;
-}
-
-function fail(state, msg, lineno?: number, colno?: number) {
-  if (!peekToken(state)) {
-    throw new Error(msg + '\n\nSource:\n' + state.src + '\n');
-  }
-  if (lineno === undefined || colno === undefined) {
-    const tok = peekToken(state);
-    lineno = tok.lineno;
-    colno = tok.colno;
-  }
-
-  const lines = state.src.split('\n');
-
-  let space = '';
-  for (let i = 0; i < colno; i++) {
-    space += ' ';
-  }
-
-  throw new Error(
-    `[${lineno + 1}, ${colno + 1}] ${msg}:\n${lines[lineno]}\n${space}^`,
-  );
-}
-
-function skip(state, type) {
-  let tok = nextToken(state);
-  if (!tok || tok.type !== type) {
-    pushToken(state, tok);
-    return false;
-  }
-  return true;
-}
-
-function expectValue(state, type, value) {
-  let tok = nextToken(state);
-  if (tok.type !== type || tok.value !== value) {
-    fail(
-      state,
-      'expected ' + value + ', got ' + tok.value,
-      tok.lineno,
-      tok.colno,
-    );
-  }
-  return tok;
-}
-
-function expect(state, type) {
-  let tok = nextToken(state);
-  if (tok.type !== type) {
-    fail(
-      state,
-      'expected ' + type + ', got ' + tok.type,
-      tok.lineno,
-      tok.colno,
-    );
-  }
-  return tok;
-}
-
-function skipValue(state, type, val) {
-  let tok = nextToken(state);
-  if (!tok || tok.type !== type || tok.value !== val) {
-    pushToken(state, tok);
-    return false;
-  }
-  return true;
-}
-
-function skipSymbol(state, val) {
-  return skipValue(state, types.TOKEN_SYMBOL, val);
-}
-
-function parseExpression(state) {
-  return parseOr(state);
-}
-
-function parseOr(state) {
-  let left = parseAnd(state);
-  while (skipValue(state, types.TOKEN_OPERATOR, 'or')) {
-    const right = parseAnd(state);
-    left = new nodes.BinOp(left.lineno, left.colno, 'or', left, right);
-  }
-  return left;
-}
-
-function parseAnd(state) {
-  let left = parseNot(state);
-  while (skipValue(state, types.TOKEN_OPERATOR, 'and')) {
-    const right = parseNot(state);
-    left = new nodes.BinOp(left.lineno, left.colno, 'and', left, right);
-  }
-  return left;
-}
-
-function parseNot(state) {
-  let left = parseCompare(state);
-  while (skipValue(state, types.TOKEN_OPERATOR, 'not')) {
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars
-    const right = parseCompare(state);
-    left = new nodes.UnaryOp(left.lineno, left.colno, 'not', parseNot(state));
-  }
-  return left;
-}
-
-function parseCompare(state) {
-  let compareOps = ['=', '!=', '<', '>', '<=', '>=', '=~', '!=~'];
-  let node = parseAdd(state);
-
-  while (1) {
-    let tok = nextToken(state);
-
-    if (!tok) {
-      break;
-    } else if (compareOps.indexOf(tok.value) !== -1) {
-      node = new nodes.BinOp(
-        tok.lineno,
-        tok.colno,
-        tok.value,
-        node,
-        parseAdd(state),
-      );
-    } else {
-      pushToken(state, tok);
-      break;
-    }
-  }
-
-  return node;
-}
-
-function parseAdd(state) {
-  let left = parseSub(state);
-  while (skipValue(state, types.TOKEN_OPERATOR, '+')) {
-    const right = parseSub(state);
-    left = new nodes.BinOp(left.lineno, left.colno, '+', left, right);
-  }
-  return left;
-}
-
-function parseSub(state) {
-  let left = parseMul(state);
-  while (skipValue(state, types.TOKEN_OPERATOR, '-')) {
-    const right = parseMul(state);
-    left = new nodes.BinOp(left.lineno, left.colno, '-', left, right);
-  }
-  return left;
-}
-
-function parseMul(state) {
-  let left = parseDiv(state);
-  while (skipValue(state, types.TOKEN_OPERATOR, '*')) {
-    const right = parseDiv(state);
-    left = new nodes.BinOp(left.lineno, left.colno, '*', left, right);
-  }
-  return left;
-}
-
-function parseDiv(state) {
-  let left = parseUnary(state);
-  while (skipValue(state, types.TOKEN_OPERATOR, '/')) {
-    const right = parseUnary(state);
-    left = new nodes.BinOp(left.lineno, left.colno, '/', left, right);
-  }
-  return left;
-}
-
-function parseUnary(state) {
-  let tok = peekToken(state);
-
-  if (skipValue(state, types.TOKEN_OPERATOR, '-')) {
-    const nextTok = peekToken(state);
-    if (nextTok.type === types.TOKEN_INT) {
-      const number = parseInt(nextToken(state).value);
-      return new nodes.Literal(tok.lineno, tok.colno, -number);
-    } else if (nextTok.type === types.TOKEN_FLOAT) {
-      const number = parseFloat(nextToken(state).value);
-      return new nodes.Literal(tok.lineno, tok.colno, -number);
-    }
-
-    return new nodes.UnaryOp(tok.lineno, tok.colno, '-', parseUnary(state));
-  }
-  return parsePrimary(state);
-}
-
-function parsePrimary(state) {
-  let tok = nextToken(state);
-  let val: number | boolean | null = null;
-
-  if (!tok) {
-    fail(state, 'expected expression, got end of file');
-  } else if (tok.type === types.TOKEN_STRING) {
-    val = tok.value;
-  } else if (tok.type === types.TOKEN_INT) {
-    val = parseInt(tok.value, 10);
-  } else if (tok.type === types.TOKEN_FLOAT) {
-    val = parseFloat(tok.value);
-  } else if (tok.type === types.TOKEN_BOOLEAN) {
-    if (tok.value === 'true') {
-      val = true;
-    } else if (tok.value === 'false') {
-      val = false;
-    }
-  }
-
-  if (val !== null) {
-    return new nodes.Literal(tok.lineno, tok.colno, val);
-  } else if (tok.type === types.TOKEN_SYMBOL) {
-    if (tok.value === 'from') {
-      return parseQueryExpression(state);
-    } else if (tok.value === 'if') {
-      return parseIfExpression(state);
-    }
-
-    return parsePostfix(
-      state,
-      new nodes.Symbol(tok.lineno, tok.colno, tok.value),
-    );
-  } else if (tok.type === types.TOKEN_LEFT_PAREN) {
-    const node = parseExpression(state);
-    expect(state, types.TOKEN_RIGHT_PAREN);
-    return node;
-  }
-
-  fail(state, 'Unexpected token: ' + tok.value, tok.lineno, tok.colno);
-}
-
-function parseIfExpression(state) {
-  const tok = expect(state, types.TOKEN_LEFT_PAREN);
-  const cond = parseExpression(state);
-  expect(state, types.TOKEN_RIGHT_PAREN);
-
-  expect(state, types.TOKEN_LEFT_CURLY);
-  const body = parseExpression(state);
-  expect(state, types.TOKEN_RIGHT_CURLY);
-
-  let else_;
-  if (skipSymbol(state, 'else')) {
-    expect(state, types.TOKEN_LEFT_CURLY);
-    else_ = parseExpression(state);
-    expect(state, types.TOKEN_RIGHT_CURLY);
-  }
-
-  return new nodes.If(tok.lineno, tok.colno, cond, body, else_);
-}
-
-function parseQueryExpression(state) {
-  // The `from` keyword has already been parsed
-  const tok = expect(state, types.TOKEN_SYMBOL);
-  const table = tok.value;
-
-  let where = null;
-  if (skipSymbol(state, 'where')) {
-    where = parseQuerySubExpression(state);
-  }
-
-  let groupby = null;
-  if (skipSymbol(state, 'groupby')) {
-    groupby = parseQuerySubExpression(state);
-  }
-
-  let select: Array<{ expr: unknown; as?: unknown }> = [];
-  let calculated;
-
-  if (skipSymbol(state, 'select')) {
-    let checkComma = false;
-    calculated = false;
-
-    expectValue(state, types.TOKEN_LEFT_CURLY, '{');
-
-    while (!skipValue(state, types.TOKEN_RIGHT_CURLY, '}')) {
-      const tok = peekToken(state);
-
-      if (checkComma && !skip(state, types.TOKEN_COMMA)) {
-        fail(
-          state,
-          'Unexpected token in query select: ' + tok.value,
-          tok.lineno,
-          tok.colno,
-        );
-      }
-
-      const expr = parseQuerySubExpression(state);
-      let as = null;
-
-      if (skipSymbol(state, 'as')) {
-        const tok = expect(state, types.TOKEN_SYMBOL);
-        as = tok.value;
-      }
-
-      select.push({ expr, as });
-
-      checkComma = true;
-    }
-  } else if (skipSymbol(state, 'calculate')) {
-    calculated = true;
-
-    expectValue(state, types.TOKEN_LEFT_CURLY, '{');
-    select.push({ expr: parseQuerySubExpression(state) });
-
-    if (!skipValue(state, types.TOKEN_RIGHT_CURLY, '}')) {
-      fail(state, 'Only one expression allowed for `calculate`');
-    }
-  } else {
-    fail(state, 'Expected either the `select` or `calculate` keyword');
-  }
-
-  return new nodes.Query(
-    tok.lineno,
-    tok.colno,
-    table,
-    select,
-    where,
-    groupby,
-    calculated,
-  );
-}
-
-function parseQuerySubExpression(state) {
-  const node = parseExpression(state);
-  return node;
-}
-
-function parsePostfix(state, node) {
-  let tok;
-
-  while ((tok = nextToken(state))) {
-    if (tok.type === types.TOKEN_LEFT_PAREN) {
-      pushToken(state, tok);
-      let args = parseArgs(state);
-      node = new nodes.FunCall(tok.lineno, tok.colno, node, args);
-    } else if (tok.type === types.TOKEN_DOT) {
-      const val = nextToken(state);
-      node = new nodes.Member(
-        tok.lineno,
-        tok.colno,
-        node,
-        new nodes.Literal(val.lineno, val.colno, val.value),
-      );
-    } else if (tok.type === types.TOKEN_EXCLAIM) {
-      const name = nextToken(state);
-      if (name.type !== types.TOKEN_SYMBOL) {
-        fail(
-          state,
-          'Expected cell name in sheet reference',
-          name.lineno,
-          name.colno,
-        );
-      }
-
-      return new nodes.Symbol(
-        node.lineno,
-        node.colno,
-        node.value + '!' + name.value,
-      );
-    } else {
-      pushToken(state, tok);
-      break;
-    }
-  }
-
-  return node;
-}
-
-function parseArgs(state) {
-  let tok = peekToken(state);
-
-  if (tok.type !== types.TOKEN_LEFT_PAREN) {
-    fail(state, 'Expected arguments', tok.lineno, tok.colno);
-  }
-
-  nextToken(state);
-
-  let args = new nodes.NodeList(tok.lineno, tok.colno);
-  let checkComma = false;
-
-  while (1) {
-    tok = peekToken(state);
-    if (tok.type === types.TOKEN_RIGHT_PAREN) {
-      nextToken(state);
-      break;
-    }
-
-    if (checkComma && !skip(state, types.TOKEN_COMMA)) {
-      fail(
-        state,
-        'Expected comma after function argument',
-        tok.lineno,
-        tok.colno,
-      );
-    }
-
-    args.addChild(parseExpression(state));
-    checkComma = true;
-  }
-
-  return args;
-}
-
-export default function parse(src) {
-  let state = {
-    src: src,
-    tokens: lex(src),
-    peeked: null,
-  };
-
-  if (state.tokens.is_finished()) {
-    // If it's an empty string, return nothing
-    return new nodes.Root(0, 0, []);
-  } else {
-    const expr = parseExpression(state);
-
-    const tok = nextToken(state);
-    if (tok) {
-      fail(
-        state,
-        'Unexpected token after expression: ' + tok.value,
-        tok.lineno,
-        tok.colno,
-      );
-    }
-
-    return new nodes.Root(0, 0, [expr]);
-  }
-}
diff --git a/packages/loot-core/src/server/spreadsheet/new/sqlconvert.ts b/packages/loot-core/src/server/spreadsheet/new/sqlconvert.ts
deleted file mode 100644
index 4316f1e6f940b328670b5f36bc73f5fbf149a791..0000000000000000000000000000000000000000
--- a/packages/loot-core/src/server/spreadsheet/new/sqlconvert.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { SCHEMA_PATHS } from './sqlgen';
-
-export default function convert(table, item) {
-  if (SCHEMA_PATHS[table]) {
-    let fields = SCHEMA_PATHS[table];
-    let updates = {};
-    Object.keys(item).forEach(k => {
-      let mappedField = fields[k] && fields[k].field;
-
-      if (mappedField) {
-        updates[k] = item[mappedField];
-      }
-    });
-
-    return { ...item, ...updates };
-  }
-  return item;
-}
diff --git a/packages/loot-core/src/server/spreadsheet/new/sqlgen.ts b/packages/loot-core/src/server/spreadsheet/new/sqlgen.ts
deleted file mode 100644
index 4e4cd8636c1311997a46c1e552ca97f7b76163f6..0000000000000000000000000000000000000000
--- a/packages/loot-core/src/server/spreadsheet/new/sqlgen.ts
+++ /dev/null
@@ -1,303 +0,0 @@
-import * as nodes from './nodes';
-
-type Lookup = { field: string; tableId?: string };
-
-let _uid = 0;
-function resetUid() {
-  _uid = 0;
-}
-
-function uid() {
-  _uid++;
-  return 't' + _uid;
-}
-
-function fail(node, message) {
-  const err = new Error(message);
-  // @ts-expect-error We should use error.cause to pass node info
-  err.node = node;
-  throw err;
-}
-
-function generateExpression(expr) {
-  if (typeof expr === 'string') {
-    // eslint-disable-next-line rulesdir/typography
-    return `"${expr}"`;
-  } else if (typeof expr === 'number') {
-    return expr;
-  }
-
-  switch (expr.getTypeName()) {
-    case 'FunCall':
-      return (
-        generateExpression(expr.callee) +
-        '(' +
-        expr.args.children.map(node => generateExpression(node)).join(',') +
-        ')'
-      );
-    case 'Member':
-      return (
-        generateExpression(expr.object) +
-        '.' +
-        generateExpression(expr.property)
-      );
-    case 'BinOp':
-      const left = generateExpression(expr.left);
-      let str;
-
-      if (
-        expr.op === '=' &&
-        expr.right.getTypeName() === 'Symbol' &&
-        expr.right.value === 'null'
-      ) {
-        str = left + ' IS NULL';
-      } else {
-        const right = generateExpression(expr.right);
-
-        switch (expr.op) {
-          case '=~':
-            str = `${left} LIKE ${right}`;
-            break;
-          case '!=~':
-            str = `${left} NOT LIKE ${right}`;
-            break;
-          default:
-            str = `${left} ${expr.op} ${right}`;
-        }
-      }
-
-      return '(' + str + ')';
-    case 'Literal':
-      if (typeof expr.value === 'string') {
-        // eslint-disable-next-line rulesdir/typography
-        return `"${expr.value}"`;
-      }
-      return expr.value;
-    case 'Symbol':
-      // if (expr.value.indexOf('!') !== -1) {
-      //   fail(expr, 'SQL variable cannot contain cell lookup');
-      // }
-      return expr.value;
-    default:
-      throw new Error('Unknown query node: ' + expr.getTypeName());
-  }
-}
-
-function transformColumns(node, implicitTable) {
-  let transformed = node.traverse(n => {
-    if (n instanceof nodes.Symbol) {
-      let table = implicitTable;
-      let field = n.value;
-
-      if (SCHEMA_PATHS[table] && SCHEMA_PATHS[table][field]) {
-        let info = SCHEMA_PATHS[table][field];
-        if (info.field) {
-          // Map the field onto something else
-          return new nodes.Symbol(n.lineno, n.colno, info.field);
-        }
-      }
-    }
-  });
-
-  return transformed || node;
-}
-
-function transformLookups(node, implicitTable) {
-  let paths: Lookup[][] = [];
-
-  const transformed = node.traverse(n => {
-    if (n instanceof nodes.Member) {
-      let currentNode = n;
-
-      let lookups: Lookup[] = [];
-      while (currentNode instanceof nodes.Member) {
-        if (!(currentNode.property instanceof nodes.Value)) {
-          fail(currentNode, 'Invalid syntax for SQL reference');
-        }
-
-        lookups.push({ field: currentNode.property.value });
-        currentNode = currentNode.object;
-      }
-
-      // @ts-expect-error Node refinement missing
-      if (!(currentNode instanceof nodes.Symbol)) {
-        fail(currentNode, 'Invalid syntax for SQL reference');
-      }
-      // @ts-expect-error Node refinement missing
-      lookups.push({ field: currentNode.value });
-      lookups.reverse();
-
-      lookups = lookups.map((lookup, idx) => {
-        return {
-          field: lookup.field,
-          tableId: uid(),
-        };
-      });
-
-      let table = implicitTable;
-
-      // Skip the last field as we don't want to resolve to that
-      // table. The syntax to emit is `table.field`.
-      for (let i = 0; i < lookups.length - 1; i++) {
-        const lookup = lookups[i];
-
-        if (!SCHEMA_PATHS[table]) {
-          const err = new Error(
-            `Table “${table}” not joinable for field “${lookup}”`,
-          );
-          // @ts-expect-error We should use error.cause to pass node info
-          err.node = node;
-          throw err;
-        }
-        if (!SCHEMA_PATHS[table][lookup.field]) {
-          const err = new Error(
-            `Unknown field “${lookup}” on table “${table}”`,
-          );
-          // @ts-expect-error We should use error.cause to pass node info
-          err.node = node;
-          throw err;
-        }
-
-        table = SCHEMA_PATHS[table][lookup.field].table;
-      }
-
-      paths.push(lookups);
-
-      let tableId = lookups[lookups.length - 2].tableId;
-      let field = lookups[lookups.length - 1].field;
-
-      return new nodes.Member(
-        node.lineno,
-        node.colno,
-        new nodes.Symbol(node.lineno, node.colno, tableId),
-        new nodes.Symbol(node.lineno, node.colno, field),
-      );
-    }
-  });
-
-  return { paths, node: transformed || node };
-}
-
-export default function generate(table, where, groupby, select) {
-  // Figure out the dep tables here. Return the SQL and dependent
-  // tables
-  let allPaths: Lookup[][] = [];
-
-  resetUid();
-
-  if (!tables[table]) {
-    throw new Error('Table not found: ' + table);
-  }
-
-  const selectStr = select
-    .map(s => {
-      let { paths, node } = transformLookups(s.expr, table);
-      let as = s.as;
-      allPaths = allPaths.concat(paths);
-
-      let newNode = transformColumns(node, table);
-
-      // If the selected field was transformed, select it as the
-      // original name
-      if (node !== newNode && node instanceof nodes.Symbol && !as) {
-        as = node.value;
-      }
-
-      const exprStr = generateExpression(newNode);
-      return as ? `${exprStr} as ${as}` : exprStr;
-    })
-    .join(', ');
-
-  let whereStr = '';
-  let whereTransformed;
-  if (where) {
-    let { paths, node } = transformLookups(where, table);
-    allPaths = allPaths.concat(paths);
-    whereTransformed = node.copy();
-
-    // Where clauses provide a special hook to map a column onto
-    // something different, so you can represent something more
-    // complex internally. You are still required to provide the
-    // original name somehow; all other references use the original
-    // name, so make sure you do `JOIN table <original-name>` or
-    // something like that.
-    node = transformColumns(node, table);
-
-    whereStr = ' WHERE (' + generateExpression(node) + ')';
-  }
-
-  let groupByStr = '';
-  if (groupby) {
-    let { paths, node } = transformLookups(groupby, table);
-    allPaths = allPaths.concat(paths);
-    groupByStr = ' GROUP BY ' + generateExpression(node);
-  }
-
-  let dependencies: string[] = [];
-  let joins: string[] = [];
-
-  allPaths.forEach(path => {
-    let currentTable = { name: table, id: table };
-    for (let i = 0; i < path.length - 1; i++) {
-      let lookup = path[i];
-      let meta = SCHEMA_PATHS[currentTable.name][lookup.field];
-
-      if (meta.sql) {
-        joins.push(meta.sql(lookup.tableId));
-      } else {
-        joins.push(
-          `LEFT JOIN ${meta.table} ${lookup.tableId} ON ${lookup.tableId}.id = ${currentTable.id}.${lookup.field}`,
-        );
-      }
-
-      if (dependencies.indexOf(meta.table) === -1) {
-        dependencies.push(meta.table);
-      }
-
-      currentTable = { name: meta.table, id: lookup.tableId };
-    }
-  });
-
-  const sql =
-    tables[table](selectStr, whereStr, joins.join('\n')) + ' ' + groupByStr;
-
-  return {
-    sql,
-    where: whereTransformed,
-    dependencies,
-  };
-}
-
-export const SCHEMA_PATHS = {
-  transactions: {
-    category: {
-      table: 'categories',
-      sql: id => `LEFT JOIN categories ${id} ON __cm.transferId = ${id}.id`,
-      field: '__cm.transferId',
-    },
-    acct: { table: 'accounts' },
-    description: { table: 'payees' },
-  },
-  payees: {
-    transfer_acct: { table: 'accounts' },
-  },
-  accounts: {
-    bank: { table: 'banks' },
-  },
-};
-
-const tables = {
-  transactions: (select, where, join) => {
-    // Never take into account parent split transactions. Their
-    // children should sum up to be equal to it
-    // prettier-ignore
-    let whereStr = `${where === '' ? 'WHERE' : where + ' AND'} transactions.isParent = 0 AND transactions.tombstone = 0`;
-
-    return `
-      SELECT ${select} FROM transactions
-      LEFT JOIN category_mapping __cm ON __cm.id = transactions.category
-      ${join}
-      ${whereStr}
-    `;
-  },
-};
diff --git a/packages/loot-core/src/server/spreadsheet/new/vm.test.ts b/packages/loot-core/src/server/spreadsheet/new/vm.test.ts
deleted file mode 100644
index c03fa6364672928dd25a06c40a62652c721180f6..0000000000000000000000000000000000000000
--- a/packages/loot-core/src/server/spreadsheet/new/vm.test.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-import { unresolveName } from '../util';
-
-import VM from './vm';
-
-const db = {
-  runQuery: sql => {
-    return Promise.resolve([{ 'sum(t.amount)': 1000 }]);
-  },
-};
-
-function makeScopes(vars) {
-  return {
-    getVariable: resolvedName => {
-      const { name } = unresolveName(resolvedName);
-
-      if (vars[resolvedName] !== undefined) {
-        return vars[resolvedName];
-      } else if (vars[name] !== undefined) {
-        return vars[name];
-      }
-
-      throw new Error(`“${resolvedName}” is not defined`);
-    },
-
-    setVariable: (name, value) => {
-      vars[name] = value;
-    },
-
-    getAll: () => vars,
-  };
-}
-
-function run(src, vars = {}) {
-  const scopes = makeScopes(vars);
-  const vm = new VM(db, scopes);
-
-  return new Promise(resolve => {
-    vm.runSource(src, () => {
-      expect(scopes.getAll()).toMatchSnapshot();
-      resolve(undefined);
-    });
-  });
-}
-
-test('vm basic', async () => {
-  return run(`=-(1 + 2 + 3)`, {
-    number: x => {
-      return x;
-    },
-    firstValue: arr => {
-      return arr[0]['sum(t.amount)'];
-    },
-  });
-});
-
-test('vm boolean types', async () => {
-  return run('=if(true and (1 + 2 + 3 - 5)) { 0 } else { 1 } ');
-});
diff --git a/packages/loot-core/src/server/spreadsheet/new/vm.ts b/packages/loot-core/src/server/spreadsheet/new/vm.ts
deleted file mode 100644
index 8fc2e0b32f69d039d4ef179160c8fccead3ec4d1..0000000000000000000000000000000000000000
--- a/packages/loot-core/src/server/spreadsheet/new/vm.ts
+++ /dev/null
@@ -1,193 +0,0 @@
-import { compile } from './compiler';
-import { MOV, CALL, QUERY, UOP, BOP, JUMPF, JUMPT } from './ops';
-
-export default class VM {
-  _onFinish;
-  db;
-  ops;
-  paused;
-  pc;
-  reg1;
-  scopes;
-  stack;
-
-  constructor(db, scopes) {
-    this.stack = new Array(1000);
-    this.reg1 = null;
-    this.pc = 0;
-    this.db = db;
-    this.scopes = scopes;
-  }
-
-  get(ref) {
-    if (ref && ref.type) {
-      if (ref.type === '__reg') {
-        return this.reg1;
-      } else if (ref.type === '__var') {
-        return this.scopes.getVariable(ref.name);
-      } else if (ref.type === '__stack') {
-        return this.stack[ref.index];
-      }
-    }
-
-    return ref;
-  }
-
-  set(ref, value) {
-    if (ref && ref.type) {
-      if (ref.type === '__reg') {
-        this.reg1 = this.get(value);
-      } else if (ref.type === '__var') {
-        this.scopes.setVariable(ref.name, this.get(value));
-      } else if (ref.type === '__stack') {
-        this.stack[ref.index] = this.get(value);
-      }
-    }
-  }
-
-  binaryOp(op, left, right) {
-    switch (op) {
-      case '+':
-        // TODO: Enforce these to be numbers
-        this.reg1 = this.get(left) + this.get(right);
-        break;
-      case '-':
-        this.reg1 = this.get(left) - this.get(right);
-        break;
-      case '*':
-        this.reg1 = this.get(left) * this.get(right);
-        break;
-      case '/':
-        this.reg1 = this.get(left) / this.get(right);
-        break;
-      case 'and':
-        this.reg1 = this.get(left) && this.get(right);
-        break;
-      case 'or':
-        this.reg1 = this.get(left) || this.get(right);
-        break;
-      default:
-        throw new Error('Unimplemented operator: ' + op);
-    }
-  }
-
-  unaryOp(op, target) {
-    switch (op) {
-      case '-':
-        this.reg1 = -this.get(target);
-        break;
-      default:
-        throw new Error('Unimplemented operator: ' + op);
-    }
-  }
-
-  call(callee, args) {
-    const func = this.get(callee);
-    this.reg1 = func.apply(
-      null,
-      args.map(arg => this.get(arg)),
-    );
-  }
-
-  query(sql, calculated) {
-    this.pause(
-      this.db.runQuery(sql, [], true).then(res => {
-        if (calculated) {
-          const keys = Object.keys(res[0]);
-          return res[0][keys[0]];
-        }
-        return res;
-      }),
-      'Running sql: ' + sql,
-    );
-  }
-
-  jump(value, loc, { test }) {
-    const result = this.get(value);
-    const falsy = result === false || result === 0 || result === '';
-
-    if ((test === 'true' && !falsy) || (test === 'false' && falsy)) {
-      this.pc = loc.get();
-    }
-  }
-
-  pause(promise, activityName) {
-    this.paused = true;
-
-    promise.then(
-      val => {
-        this.resume(val);
-      },
-      err => {
-        console.log('VM caught error during activity: ' + activityName);
-        console.log(err);
-        this.resume(null);
-      },
-    );
-  }
-
-  resume(val) {
-    this.reg1 = val;
-    this.paused = false;
-    this._run();
-  }
-
-  _run() {
-    while (this.pc < this.ops.length) {
-      const op = this.ops[this.pc];
-
-      switch (op[0]) {
-        case MOV:
-          this.set(op[2], op[1]);
-          break;
-        case CALL:
-          this.call(op[1], op[2]);
-          break;
-        case QUERY:
-          this.query(op[1], op[2]);
-          break;
-        case BOP:
-          this.binaryOp(op[1], op[2], op[3]);
-          break;
-        case UOP:
-          this.unaryOp(op[1], op[2]);
-          break;
-        case JUMPF:
-          this.jump(op[1], op[2], { test: 'false' });
-          break;
-        case JUMPT:
-          this.jump(op[1], op[2], { test: 'true' });
-          break;
-        default:
-          throw new Error('Unimplemented opcode: ' + op[0].toString());
-      }
-
-      this.pc++;
-
-      if (this.paused) {
-        break;
-      }
-    }
-
-    if (this.pc === this.ops.length && this._onFinish) {
-      this._onFinish(this.reg1);
-    }
-  }
-
-  onFinish(func) {
-    this._onFinish = func;
-  }
-
-  run(ops, onFinish) {
-    this.pc = 0;
-    this.ops = ops;
-    this._onFinish = onFinish;
-    this._run();
-    return this.reg1;
-  }
-
-  runSource(src, onFinish) {
-    const { ops } = compile(src);
-    return this.run(ops, onFinish);
-  }
-}
diff --git a/packages/loot-core/src/server/spreadsheet/sqlinterp.test.ts b/packages/loot-core/src/server/spreadsheet/sqlinterp.test.ts
deleted file mode 100644
index 7f9eb2b7cc5f2c6b84548047992e76b90d3fbdf5..0000000000000000000000000000000000000000
--- a/packages/loot-core/src/server/spreadsheet/sqlinterp.test.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { compile } from './new/compiler';
-import sqlinterp from './sqlinterp';
-
-test('sql interpretation works', async () => {
-  const transJan = {
-    date: 20170106,
-    amount: -5000,
-    acct: 'boa',
-    category: 1,
-  };
-  const transFeb = {
-    date: 20170215,
-    amount: -7620,
-    acct: 'boa',
-    category: 1,
-  };
-
-  const { sqlDependencies } = compile(`
-    =from transactions
-       where date >= 20170101 and date <= 20170131 and
-         category = 1
-       select { amount }
-  `);
-  const where = sqlDependencies[0].where;
-
-  expect(sqlinterp(where, transJan, 'transactions')).toBe(true);
-  expect(sqlinterp(where, transFeb, 'transactions')).toBe(false);
-});
diff --git a/packages/loot-core/src/server/spreadsheet/util.ts b/packages/loot-core/src/server/spreadsheet/util.ts
index cc69f85894231d1cf3d01601282702b91d39949d..92709cffa52871fe1c75bb6e955fbc7a66ca38dc 100644
--- a/packages/loot-core/src/server/spreadsheet/util.ts
+++ b/packages/loot-core/src/server/spreadsheet/util.ts
@@ -12,28 +12,3 @@ export function unresolveName(name) {
 export function resolveName(sheet, name) {
   return sheet + '!' + name;
 }
-
-export function resolveNamesAsObjects(sheets) {
-  const cells = {};
-  Object.keys(sheets).forEach(sheetName => {
-    const sheet = sheets[sheetName];
-
-    Object.keys(sheet).forEach(name => {
-      const expr = sheet[name];
-      cells[resolveName(sheetName, name)] = expr;
-    });
-  });
-  return cells;
-}
-
-export function resolveNamesAsArrays(sheets) {
-  const cells = [];
-  Object.keys(sheets).forEach(sheetName => {
-    const sheet = sheets[sheetName];
-
-    sheet.forEach(name => {
-      cells.push(resolveName(sheetName, name));
-    });
-  });
-  return cells;
-}
diff --git a/packages/loot-core/src/shared/errors.ts b/packages/loot-core/src/shared/errors.ts
index 56ee3fcb6a074920f5af6c6e5d36203ed9edc2ea..0fec6f41676c776dd9a797d7ef05e807cf9efa75 100644
--- a/packages/loot-core/src/shared/errors.ts
+++ b/packages/loot-core/src/shared/errors.ts
@@ -76,19 +76,6 @@ export function getTestKeyError({ reason }) {
   }
 }
 
-export function getSubscribeError({ reason }) {
-  switch (reason) {
-    case 'network':
-      return 'Unable to reach the server. Check your internet connection';
-    case 'exists':
-      return 'An account with that email already exists. Did you mean to login?';
-    case 'invalid-email':
-      return 'Invalid email';
-    default:
-      return 'An error occurred. Please try again later.';
-  }
-}
-
 export function getSyncError(error, id) {
   if (error === 'out-of-sync-migrations' || error === 'out-of-sync-data') {
     return 'This budget cannot be loaded with this version of the app.';
diff --git a/packages/loot-core/src/shared/rules.ts b/packages/loot-core/src/shared/rules.ts
index 2764becb9c5b9740409aec9ee18679c558552bba..93b2727ff9d54da234c2d0f6595faf7aa0ff9385 100644
--- a/packages/loot-core/src/shared/rules.ts
+++ b/packages/loot-core/src/shared/rules.ts
@@ -25,19 +25,6 @@ export const TYPE_INFO = {
   },
 };
 
-export type FieldTypes = {
-  imported_payee: string;
-  payee: string;
-  date: string;
-  notes: string;
-  amount: number;
-  amountInflow: number;
-  amountOutfow: number;
-  category: string;
-  account: string;
-  cleared: boolean;
-};
-
 export const FIELD_TYPES = new Map(
   Object.entries({
     imported_payee: 'string',
diff --git a/packages/loot-core/src/shared/util.ts b/packages/loot-core/src/shared/util.ts
index 008278f4a17b533103d6ff2f365841c65fcb8037..3f726ea1d9897ae47adb3ab3629e5097561a2e94 100644
--- a/packages/loot-core/src/shared/util.ts
+++ b/packages/loot-core/src/shared/util.ts
@@ -1,50 +1,7 @@
-export function cleanUUID(uuid) {
-  return uuid.replace(/-/g, '');
-}
-
 export function last(arr) {
   return arr[arr.length - 1];
 }
 
-export function mergeObjects(objects) {
-  return Object.assign.apply(null, [{}, ...objects]);
-}
-
-export function composeCellChanges(objects) {
-  const merged = {};
-  Object.keys(objects).forEach(key => {
-    if (merged[key]) {
-      merged[key] = { ...merged[key], ...objects[key] };
-    } else {
-      merged[key] = objects[key];
-    }
-  });
-}
-
-export function flattenArray(arrays) {
-  return Array.prototype.concat.apply([], arrays);
-}
-
-export function shallowEqual(a, b) {
-  if (a === b) {
-    return true;
-  }
-
-  let numKeysA = 0,
-    numKeysB = 0,
-    key;
-  for (key in b) {
-    numKeysB++;
-    if (!a.hasOwnProperty(key) || a[key] !== b[key]) {
-      return false;
-    }
-  }
-  for (key in a) {
-    numKeysA++;
-  }
-  return numKeysA === numKeysB;
-}
-
 export function getChangedValues(obj1, obj2) {
   // Keep the id field because this is mostly used to diff database
   // objects
@@ -132,19 +89,6 @@ export function groupBy(data, field, mapper?: (v: unknown) => unknown) {
   return res;
 }
 
-export function groupBySingle(data, field, mapper) {
-  let res = new Map();
-  for (let i = 0; i < data.length; i++) {
-    let item = data[i];
-    let key = item[field];
-    if (res.has(key)) {
-      throw new Error('groupBySingle found conflicting key: ' + key);
-    }
-    res.set(key, mapper ? mapper(item) : data[i]);
-  }
-  return res;
-}
-
 // This should replace the existing `groupById` function, since a
 // `Map` is better, but we can't swap it out because `Map` has a
 // different API and we need to go through and update everywhere that
@@ -192,14 +136,6 @@ export function groupById(data) {
   return res;
 }
 
-export function debugMemoFailure(prevProps, nextProps) {
-  let changed = getChangedValues(prevProps, nextProps);
-  if (changed !== null) {
-    console.log(changed);
-  }
-  return changed === null;
-}
-
 export function setIn(map, keys, item) {
   for (let i = 0; i < keys.length; i++) {
     let key = keys[i];
@@ -228,11 +164,6 @@ export function getIn(map, keys) {
   return item;
 }
 
-// Useful for throwing exception from expressions
-export function throwError(err) {
-  throw err;
-}
-
 export function fastSetMerge(set1, set2) {
   let finalSet = new Set(set1);
   let iter = set2.values();
@@ -339,10 +270,6 @@ export function toRelaxedNumber(value) {
   return integerToAmount(currencyToInteger(value) || 0);
 }
 
-export function toRelaxedInteger(value) {
-  return stringToInteger(value) || 0;
-}
-
 export function integerToCurrency(n) {
   return numberFormat.formatter.format(safeNumber(n) / 100);
 }
@@ -406,15 +333,3 @@ export function looselyParseAmount(amount) {
 
   return safeNumber(parseFloat(left + '.' + right));
 }
-
-export function semverToNumber(str) {
-  return parseInt(
-    '1' +
-      str
-        .split('.')
-        .map(x => {
-          return ('000' + x.replace(/[^0-9]/g, '')).slice(-3);
-        })
-        .join(''),
-  );
-}
diff --git a/upcoming-release-notes/1158.md b/upcoming-release-notes/1158.md
new file mode 100644
index 0000000000000000000000000000000000000000..3bd127418993fc90b8d01ffa583b3afc5d5fce8e
--- /dev/null
+++ b/upcoming-release-notes/1158.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [Shazib]
+---
+
+Remove unused/legacy code from codebase