<commit_before>﻿module Parcel
    open FParsec

    (*
     * TYPES
     *)
    type ParseException(formula: string, reason: string) =
        inherit System.Exception(formula)
        new(formula: string) = ParseException(formula, "")

    (*
     * PRIVATE IMPLEMENTATIONS
     *)

    // wrapper for success/failure
    let private test p str =
        match runParserOnString p (AST.Env("", "", "")) "" str with
        | Success(result, _, _)     -> printfn "Success: %A" result
        | Failure(errorMsg, _, _)   -> printfn "Failure: %s" errorMsg

    let private getAddress(formula: string)(path: string)(wbname: string)(wsname: string): AST.Address =
        match runParserOnString (Grammar.AddrR1C1 .>> eof) (AST.Env(path, wbname, wsname)) "" formula with
        | Success(addr, _, _)       -> addr
        | Failure(errorMsg, _, _)   -> failwith errorMsg

    let private getRange(formula: string)(path: string)(wbname: string)(wsname: string): AST.Range option =
        match runParserOnString (Grammar.RangeR1C1 .>> eof) (AST.Env(path, wbname, wsname)) "" formula with
        | Success(range, _, _)      -> Some(range)
        | Failure(errorMsg, _, _)   -> None

    let private getReference(formula: string)(path: string)(wbname: string)(wsname: string): AST.Reference option =
        match runParserOnString (Grammar.Reference .>> eof) (AST.Env(path, wbname, wsname)) "" formula with
        | Success(reference, _, _)  -> Some(reference)
        | Failure(errorMsg, _, _)   -> None

    let private isNumeric(str: string): bool =
        match run (pfloat .>> eof) str with
        | Success(number, _, _)     -> true
        | Failure(errorMsg, _, _)   -> false

    (*
     * PUBLIC API
     *)
    let parseFormula(formula: string)(path: string)(wbname: string)(wsname: string): AST.Expression option =
        match runParserOnString Grammar.Formula (AST.Env(path, wbname, wsname)) "" formula with
        | Success(formula, _, _) -> Some(formula)
        | Failure(errorMsg, _, _) -> None

    // The parser REPL calls this; note that the
    // Formula parser looks for EOF
    let consoleParser(s: string) = test Grammar.Formula s

    // Call this for simple address parsing
    let simpleReferenceParser(s: string, e: AST.Env) : AST.Reference =
        match runParserOnString Grammar.Reference e "" s with
        | Success(result, _, _) -> result
        | Failure(errorMsg, _, _) -> failwith ("String \"" + s + "\" does not appear to be a Reference:\n" + errorMsg)

    let rangeReferencesFromFormula(formula: string, path: string, workbook: string, worksheet: string, ignore_parse_errors: bool) : seq<AST.Range> =
        try
            match (parseFormula formula path workbook worksheet),ignore_parse_errors with
            | Some(tree),_ -> RangeVisitor.rangesFromExpr(tree) |> Seq.ofList
            | None,false -> raise (ParseException(formula))
            | None,true -> Seq.empty    // just ignore parse exceptions for now
        with
        // right now, we recognize indirect addresses but do not correctly dereference them
        | :? AST.IndirectAddressingNotSupportedException -> seq[]

    let addrReferencesFromFormula(formula: string, path: string, wb: string, ws: string, ignore_parse_errors: bool) : seq<AST.Address> =
        match (parseFormula formula path wb ws),ignore_parse_errors with
        | Some(ast),_ -> CellVisitor.addrsFromExpr(ast) |> Seq.ofList
        | None,false -> raise (ParseException formula)
        | None,true -> Seq.empty    // just ignore parse exceptions for now

    let rec formulaNamesFromExpr(ast: AST.Expression): string list =
        match ast with<commit_msg>Change return type of addr/range reference methods to array for easier debugging than seq.<commit_after>﻿module Parcel
    open FParsec

    (*
     * TYPES
     *)
    type ParseException(formula: string, reason: string) =
        inherit System.Exception(formula)
        new(formula: string) = ParseException(formula, "")

    (*
     * PRIVATE IMPLEMENTATIONS
     *)

    // wrapper for success/failure
    let private test p str =
        match runParserOnString p (AST.Env("", "", "")) "" str with
        | Success(result, _, _)     -> printfn "Success: %A" result
        | Failure(errorMsg, _, _)   -> printfn "Failure: %s" errorMsg

    let private getAddress(formula: string)(path: string)(wbname: string)(wsname: string): AST.Address =
        match runParserOnString (Grammar.AddrR1C1 .>> eof) (AST.Env(path, wbname, wsname)) "" formula with
        | Success(addr, _, _)       -> addr
        | Failure(errorMsg, _, _)   -> failwith errorMsg

    let private getRange(formula: string)(path: string)(wbname: string)(wsname: string): AST.Range option =
        match runParserOnString (Grammar.RangeR1C1 .>> eof) (AST.Env(path, wbname, wsname)) "" formula with
        | Success(range, _, _)      -> Some(range)
        | Failure(errorMsg, _, _)   -> None

    let private getReference(formula: string)(path: string)(wbname: string)(wsname: string): AST.Reference option =
        match runParserOnString (Grammar.Reference .>> eof) (AST.Env(path, wbname, wsname)) "" formula with
        | Success(reference, _, _)  -> Some(reference)
        | Failure(errorMsg, _, _)   -> None

    let private isNumeric(str: string): bool =
        match run (pfloat .>> eof) str with
        | Success(number, _, _)     -> true
        | Failure(errorMsg, _, _)   -> false

    (*
     * PUBLIC API
     *)
    let parseFormula(formula: string)(path: string)(wbname: string)(wsname: string): AST.Expression option =
        match runParserOnString Grammar.Formula (AST.Env(path, wbname, wsname)) "" formula with
        | Success(formula, _, _) -> Some(formula)
        | Failure(errorMsg, _, _) -> None

    // The parser REPL calls this; note that the
    // Formula parser looks for EOF
    let consoleParser(s: string) = test Grammar.Formula s

    // Call this for simple address parsing
    let simpleReferenceParser(s: string, e: AST.Env) : AST.Reference =
        match runParserOnString Grammar.Reference e "" s with
        | Success(result, _, _) -> result
        | Failure(errorMsg, _, _) -> failwith ("String \"" + s + "\" does not appear to be a Reference:\n" + errorMsg)

    let rangeReferencesFromFormula(formula: string, path: string, workbook: string, worksheet: string, ignore_parse_errors: bool) : AST.Range[] =
        try
            match (parseFormula formula path workbook worksheet),ignore_parse_errors with
            | Some(tree),_ -> RangeVisitor.rangesFromExpr(tree) |> Seq.distinct |> Seq.toArray
            | None,false -> raise (ParseException(formula))
            | None,true -> [||]    // just ignore parse exceptions for now
        with
        // right now, we recognize indirect addresses but do not correctly dereference them,
        // which requires a program interpreter and input spreadsheet :(
        | :? AST.IndirectAddressingNotSupportedException -> [||]

    let addrReferencesFromFormula(formula: string, path: string, wb: string, ws: string, ignore_parse_errors: bool) : AST.Address[] =
        match (parseFormula formula path wb ws),ignore_parse_errors with
        | Some(ast),_ -> CellVisitor.addrsFromExpr(ast) |> Seq.distinct |> Seq.toArray
        | None,false -> raise (ParseException formula)
        | None,true -> [||]    // just ignore parse exceptions for now

    let rec formulaNamesFromExpr(ast: AST.Expression): string list =
        match ast with
{'fraction_non_alphanumeric': 0.12018716577540108, 'fraction_numerical': 0.0010695187165775401, 'mean_word_length': 2.8842159916926273, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 69, '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': '3659805', 'n_tokens_mistral': 2244, 'n_tokens_neox': 2133, 'n_words': 593}