HTML 형식을위한 '토큰 화'C# 코드를위한보다 우아한 방법을 제안 할 수 있습니까?

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

  •  04-07-2019
  •  | 
  •  

문제

(이 질문 Refactoring F# Code에 대해 한 번의 투표와 흥미롭고 유용한 답변도 얻었습니다. 그리고 62 F# 32,000+ 중 질문에 대한 질문은 불쌍한 것 같습니다. 그래서 나는 더 많은 비 승인의 위험을 감수 할 것입니다!)

어제 블로거 블로그에 약간의 코드를 게시하려고했는데 이 지역, 과거에 유용한 것을 발견했습니다. 그러나 블로거 편집자는 모든 스타일 선언을 먹었으므로 막 다른 골목으로 판명되었습니다.

그래서 (다른 해커처럼), 나는 "얼마나 힘들 수 있습니까?"라고 생각했습니다. F#의 <100 라인에서 내 자신을 굴 렸습니다.

다음은 입력 문자열을 '토큰'목록으로 바꾸는 코드의 '고기'입니다. 이 토큰은 Lexing/Parsing 스타일 토큰과 혼동되어서는 안됩니다. 나는 그것들을 간단히 보았고, 아무것도 이해하지 못했지만 그들이 나에게 줄 것이라는 것을 이해했습니다. 토큰, 나는 원래 문자열을 유지하고 싶습니다.

문제는 : 더 우아한 방법이 있습니까? 입력 문자열에서 각 토큰 문자열을 제거하는 데 필요한 S의 n 재정의가 마음에 들지 않지만 댓글, 문자열 및 #region Directive와 같은 것들 때문에 문자열을 미리 전위 토큰으로 분할하기가 어렵습니다. 비 단어 문자를 포함합니다).

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

(누군가가 정말로 관심이 있다면 나머지 코드를 게시하게되어 기쁩니다)

편집하다사용 훌륭한 제안활성 패턴 KVB에 의해, 중앙 비트는 이와 같이 보입니다!

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
도움이 되었습니까?

해결책

활성 패턴을 사용하여 regex.ismatch 및 regex.match 쌍을 캡슐화합니다.

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

그런 다음 NextToken 기능은 다음과 같습니다.

let nexttoken (st:String) =         
  match st with        
  | Matches "^s+" s -> Whitespace(s)        
  | Matches "^//.*?\r?\n" s -> Comment(s)
  ...
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top