Pouvez-vous proposer un moyen plus élégant de «tokenize» le code c # pour le formatage HTML?

StackOverflow https://stackoverflow.com/questions/228605

  •  04-07-2019
  •  | 
  •  

Question

( cette question à propos de la refactorisation du code F # m'a valu un vote négatif, mais aussi des réponses intéressantes et utiles. Et 62 questions F # sur les 32 000+ de SO semblent pitoyables, je vais donc prendre le risque de plus de désapprobation!)

J'essayais hier de poster un peu de code sur un blog de blogueur et je me suis tourné vers ce site . , que j'avais trouvé utile dans le passé. Cependant, l'éditeur de blogueur a mangé toutes les déclarations de style, ce qui s'est avéré être une impasse.

Alors (comme tout pirate informatique), je me suis dit & "Que peut-il être si difficile? &"; et roulé mon propre dans < 100 lignes de F #.

Voici la "viande" du code, qui transforme une chaîne d'entrée en une liste de "jetons". Notez que ces jetons ne doivent pas être confondus avec les jetons de style lexing / parsing. Je les ai regardées brièvement, et même si je ne comprenais presque rien, j’avais bien compris qu’elles ne me donneraient que des jetons, alors que je souhaitais conserver la chaîne originale.

La question est la suivante: existe-t-il un moyen plus élégant de procéder? Je n'aime pas les n-définitions de s requises pour supprimer chaque chaîne de jeton de la chaîne d'entrée, mais il est difficile de scinder la chaîne en tokens potentiels à l'avance, en raison d'éléments comme les commentaires, les chaînes et la directive #region (qui contient un caractère autre qu'un mot).

//Types of tokens we are going to detect
type Token = 
    | Whitespace of string
    | Comment of string
    | Strng of string
    | Keyword of string
    | Text of string
    | EOF

//turn a string into a list of recognised tokens
let tokenize (s:String) = 
    //this is the 'parser' - should we look at compiling the regexs in advance?
    let nexttoken (st:String) = 
        match st with
        | st when Regex.IsMatch(st, "^\s+") -> Whitespace(Regex.Match(st, "^\s+").Value)
        | st when Regex.IsMatch(st, "^//.*?\r?\n") -> Comment(Regex.Match(st, "^//.*?\r?\n").Value) //this is double slash-style comments
        | st when Regex.IsMatch(st, "^/\*(.|[\r?\n])*?\*/") -> Comment(Regex.Match(st, "^/\*(.|[\r?\n])*?\*/").Value) // /* */ style comments http://ostermiller.org/findcomment.html
        | st when Regex.IsMatch(st, @"^""([^""\\]|\\.|"""")*""") -> Strng(Regex.Match(st, @"^""([^""\\]|\\.|"""")*""").Value) // unescaped = "([^"\\]|\\.|"")*" http://wordaligned.org/articles/string-literals-and-regular-expressions
        | st when Regex.IsMatch(st, "^#(end)?region") -> Keyword(Regex.Match(st, "^#(end)?region").Value)
        | st when st <> "" -> 
                match Regex.Match(st, @"^[^""\s]*").Value with //all text until next whitespace or quote (this may be wrong)
                | x when iskeyword x -> Keyword(x)  //iskeyword uses Microsoft.CSharp.CSharpCodeProvider.IsValidIdentifier - a bit fragile...
                | x -> Text(x)
        | _ -> EOF

    //tail-recursive use of next token to transform string into token list
    let tokeneater s = 
        let rec loop s acc = 
            let t = nexttoken s
            match t with
            | EOF -> List.rev acc //return accumulator (have to reverse it because built backwards with tail recursion)
            | Whitespace(x) | Comment(x) 
            | Keyword(x) | Text(x) | Strng(x) -> 
                loop (s.Remove(0, x.Length)) (t::acc)  //tail recursive
        loop s []

    tokeneater s

(Si quelqu'un est vraiment intéressé, je suis heureux de poster le reste du code.)

MODIFIER Utilisation du excellente suggestion de modèles actifs de kvb, le bit central ressemble à ceci, bien meilleur!

let nexttoken (st:String) = 
    match st with
    | Matches "^\s+" s -> Whitespace(s)
    | Matches "^//.*?\r?(\n|$)" s -> Comment(s) //this is double slash-style comments
    | Matches "^/\*(.|[\r?\n])*?\*/" s -> Comment(s)  // /* */ style comments http://ostermiller.org/findcomment.html
    | Matches @"^@?""([^""\\]|\\.|"""")*""" s -> Strng(s) // unescaped regexp = ^@?"([^"\\]|\\.|"")*" http://wordaligned.org/articles/string-literals-and-regular-expressions
    | Matches "^#(end)?region" s -> Keyword(s) 
    | Matches @"^[^""\s]+" s ->   //all text until next whitespace or quote (this may be wrong)
            match s with
            | IsKeyword x -> Keyword(s)
            | _ -> Text(s)
    | _ -> EOF
Était-ce utile?

La solution

J'utiliserais un modèle actif pour encapsuler les paires Regex.IsMatch et Regex.Match, comme suit:

let (|Matches|_|) re s =
  let m = Regex(re).Match(s)
  if m.Success then
    Some(Matches (m.Value))
  else
    None

Ensuite, votre fonction nexttoken peut ressembler à:

let nexttoken (st:String) =         
  match st with        
  | Matches "^s+" s -> Whitespace(s)        
  | Matches "^//.*?\r?\n" s -> Comment(s)
  ...
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top