anchor.diff:  
   (**
   
  +Since this is SQL we're dealing with, there are a bunch of special kinds of names.
  +
  +Table names can have an optional schema qualifier part, like `main.Users`.
  +To avoid needless backtracking, we treat the second part as optional instead of the first for parsing purposes.
  +
  +*)
  +
  +let tableName =
  +    (%% +.name
  +    -- ws
  +    -- +.(zeroOrOne * (%% '.' -- ws -? +.name -%> id))
  +    -%> fun name name2 ->
  +        match name2 with
  +        | None -> { SchemaName = None; TableName = name }
  +        | Some name2 -> { SchemaName = Some name; TableName = name2 })
  +    <?> "table-name"
  +
  +(**
  +
  +Column names are similar -- they can be qualified with a table name, including its own optional schema qualifier.
  +We could parse this as an optional table name followed by the column name. But this would require extra effort
  +to ensure we parse "foo.bar" as (no schema, table foo, column bar), not (schema foo, table bar, no column).
  +
  +So instead, we just parse between 1 and 3 names separated by dots, then put together the whole structure
  +based on how many we got.
  +
  +*)
  +
  +let columnName =
  +    (qty.[1..3] / tws '.' * tws name
  +    |>> fun names ->
  +        match names.Count with
  +        | 1 -> { Table = None; ColumnName = names.[0] }
  +        | 2 -> { Table = Some { SchemaName = None; TableName = names.[0] }; ColumnName = names.[1] }
  +        | 3 -> { Table = Some { SchemaName = Some names.[0]; TableName = names.[1] }; ColumnName = names.[2] }
  +        | _ -> failwith "Unreachable")
  +    <?> "column-name"
  +
  +(**
  +
  +Bind parameters in prepared statements can take [several forms](https://www.sqlite.org/lang_expr.html#varparam).
  +
  +We'll support all of them except the special Tcl syntax for `$` parameters.
  +
  +Named parameters are prefixed by `@`, `:`, or `$`.
  +
  +*)
  +
  +let namedBindParameter =
  +    %% +.['@'; ':'; '$']
  +    -- +.name
  +    -%> fun prefix name -> NamedParameter (prefix, name)
  +
  +(**
  +
  +Positional parameters start with `?`, and may or may not contain an explicit integer parameter index.
  +
  +*)
  +
  +let positionalBindParameter =
  +    %% '?'
  +    -- +.(p<uint32> * zeroOrOne)
  +    -%> PositionalParameter
  +
  +(*
  +
  +Either parameter type is allowed anywhere bind parameters can be used.
  +
  +*)
  +
  +let bindParameter =
  +    %[ namedBindParameter; positionalBindParameter ]
  +    <?> "bind-parameter"
  +
  +(**
  +
  +Next up, a fundamental building block of expressions: literal values like `1` and `'str'`.
  +
  +Some literals in SQL, such as `NULL`, are language keywords.
  +
   When we look for a keyword, we need to ensure that we don't actually read part of another name.
   For example, if we're looking for CAST, we shouldn't match after reading the first four characters of
   CASTLE. Therefore, we require that keywords are not followed by other legal identifier characters.
  
positive.code: 
  (**
  
  Next are string literals, which are very similar to quoted identifiers.
  
  *)
  
  
negative.code:     || c >= '0' && c <= '9'
      || c = '$'
  
  /// A plain, unquoted name.
  let unquotedName =
      let identifier =
          many1Satisfy2 isInitialIdentifierCharacter isFollowingIdentifierCharacter
  