Können Sie sich einen eleganteren Weg zu ‚tokenize‘ c # Code für die HTML-Formatierung vorschlagen?

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

  •  04-07-2019
  •  | 
  •  

Frage

( Diese Frage über F # Code Refactoring hat mich eine nach unten Stimme, aber auch einige interessante und nützliche Antworten. und 62 F # Fragen aus dem 32.000 auf sO scheint erbärmlich, also werde ich das Risiko von mehr Ablehnung nehmen!)

Ich habe versucht, ein Stück Code auf einem Blogger-Blog gestern zu schreiben, und wandte sich href="http://manoli.net/csharpformat/" rel="nofollow noreferrer"> dieser Seite

So (wie jeder Hacker), dachte ich, „wie schwer es sein kann?“ und rollte mit meinem eigenen in <100 Zeilen F #.

Hier ist das ‚Fleisch‘ des Codes, der eine Eingabezeichenfolge in eine Liste von ‚Token‘ dreht. Beachten Sie, dass diese Token sind nicht mit dem lexing / Parsing-Stil Token verwechselt werden. Ich habe bei diesen kurzen Blick, und obwohl ich kaum etwas verstanden, ich verstand, dass sie mir geben würde, nur Token, während ich möchte, dass meine ursprüngliche Zeichenfolge halten.

Die Frage ist: Gibt es eine elegantere Möglichkeit, dies zu tun? Ich weiß nicht, wie die n Wieder Definitionen s erforderlich, um jeden Token-String aus der Eingabezeichenfolge zu entfernen, aber es ist schwierig, die Zeichenfolge in potentielle Token im Voraus zu spalten, weil Dinge wie Kommentare, Strings und die #region Richtlinie (die enthält ein nicht-Wort-Zeichen).

//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

(Wenn jemand wirklich interessiert ist, ich bin glücklich, den Rest des Codes zu schreiben)

Bearbeiten Mit Hilfe der ausgezeichneter Vorschlag von aktive Muster von KVB, das zentrale Bit wie folgt aussieht, viel besser!

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
War es hilfreich?

Lösung

Ich würde ein aktives Muster verwenden, um die Regex.IsMatch und Regex.Match Paare zu verkapseln, etwa so:

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

Dann ist Ihre Funktion kann nexttoken wie folgt aussehen:

let nexttoken (st:String) =         
  match st with        
  | Matches "^s+" s -> Whitespace(s)        
  | Matches "^//.*?\r?\n" s -> Comment(s)
  ...
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top