CSFlexer - einfacher schneller C# Scanner

    • C#

      CSFlexer - einfacher schneller C# Scanner

      Hi,

      Da ich mich in letzer Zeit intensiv mit dem Thema Compilerbau auseinadergesetzt habe, wollte ich euch meinen C# Version eines Scanners nicht vorenthalten. Der Scanner unterteilt einen Text in einzelne sogenante Tokens, die quasi ein Bauteil einer Sprachdefinition entsprechen.

      Beispielsweise die Sprache die mathematische Ausdrücke beschreibt:
      2*(2+4)

      Gibt man diese Zeichenkette in den entsprechenden Scanner werden diese Sprachbestandteile unterschieden/ausgegeben (Jede Zeile repräsentiert ein Token):
      Spoiler anzeigen

      Quellcode

      1. 2
      2. *
      3. (
      4. 2
      5. +
      6. 4
      7. )


      Ich habe mich bei der Umsetzung des Scanners sehr an JFlex orientiert, in welchem eine Scannerregel folgendermaßen aufgebaut ist:

      Quellcode

      1. <Zustand> {
      2. RegExAusdruck1{Anweisung1;}
      3. RegExAusdruck2{Anweisung2;}
      4. }


      Die Zustände funktionieren genauso wie bei einem endlichen Automaten, der Startzustand ist immer INITIAL.

      Wobei der Scanner Methoden für den Pushback sowie den Zustandwechsel bereitstellt.

      Hier nun meine Implementierung davon:

      Die Token-Klasse:
      Spoiler anzeigen

      C#-Quellcode

      1. using System;
      2. namespace CSFlex
      3. {
      4. public enum TokenType{
      5. EOF,
      6. ID,
      7. COMMA,
      8. LSBR,
      9. RSBR,
      10. WS,
      11. NUMBER,
      12. NULL
      13. }
      14. public class Token
      15. {
      16. public String Content{ get; set;}
      17. public TokenType Type{ get; set;}
      18. public int startLine{ get; set;}
      19. public int startColumn{ get; set;}
      20. public Token (string content, TokenType type )
      21. {
      22. this.Content = content;
      23. this.Type = type;
      24. }
      25. public Token (string content, TokenType type, int startLine, int startColumn)
      26. {
      27. this.Content = content;
      28. this.Type = type;
      29. this.startLine = startLine;
      30. this.startColumn = startColumn;
      31. }
      32. }
      33. }



      Die Klasse LexingRule die eine LexerRegel darstellt:

      Spoiler anzeigen

      C#-Quellcode

      1. using System;
      2. using System.Text.RegularExpressions;
      3. namespace CSFlex
      4. {
      5. public enum LexingState{
      6. INITIAL,
      7. NUM,
      8. ERROR
      9. }
      10. public class LexingRule
      11. {
      12. public LexingState[] LexingState{ get; set;}
      13. public Regex Pattern{ get; set;}
      14. public Func<string, Match, Token> Action{ get; set;}
      15. public LexingRule (LexingState[] ls, String pattern, Func<string, Match, Token> action)
      16. {
      17. this.LexingState = ls;
      18. this.Pattern = new Regex ("^" + pattern, RegexOptions.Compiled);
      19. this.Action = action;
      20. }
      21. public Boolean ruleMatches(LexingState ls, String input){
      22. return Array.Exists(LexingState, l => l == ls) && Pattern.IsMatch(input);
      23. }
      24. public Match getMatch(String input){
      25. Match m = Pattern.Match (input);
      26. if (m.Success) {
      27. return m;
      28. }
      29. return null;
      30. }
      31. }
      32. }



      Spoiler anzeigen

      C#-Quellcode

      1. using System;
      2. using System.Collections.Generic;
      3. using System.Text;
      4. using System.Text.RegularExpressions;
      5. namespace CSFlex
      6. {
      7. public class CSFlexer
      8. {
      9. private int startLine =1, startColumn=1;
      10. private string toParse;
      11. private string remainingText;
      12. private List<LexingRule> LRules = new List<LexingRule>();
      13. private LexingState CurrentState = LexingState.INITIAL;
      14. private StringBuilder stringBuffer = new StringBuilder();
      15. private double numBuffer;
      16. public CSFlexer(string text){
      17. startLine = 0;
      18. startColumn = 0;
      19. toParse = text;
      20. remainingText = text;
      21. numBuffer = 0;
      22. loadRules ();
      23. }
      24. public void pushBack(int length){
      25. remainingText = toParse.Substring (toParse.Length - (remainingText.Length + length));
      26. }
      27. public void changeState(LexingState ls){
      28. CurrentState = ls;
      29. }
      30. public void loadRules(){
      31. }
      32. public LexingRule getMatchingRule(ref Match m){
      33. LexingRule result = null;
      34. Match mt = null;
      35. int currentLength = -1;
      36. foreach (LexingRule ls in LRules) {
      37. if (ls.ruleMatches (CurrentState, remainingText)) {
      38. mt = ls.getMatch (remainingText);
      39. if (mt.Length > currentLength) {
      40. result = ls;
      41. currentLength = mt.Length;
      42. m = mt;
      43. }
      44. }
      45. }
      46. return result;
      47. }
      48. public Token nextToken(){
      49. Token resultToken = null;
      50. while(resultToken==null){
      51. Match m = null;
      52. LexingRule lr = getMatchingRule (ref m);
      53. string yytext = "";
      54. if (remainingText != "")
      55. yytext = m.Value;
      56. remainingText = remainingText.Substring (m.Length);
      57. resultToken = lr.Action.Invoke (yytext, m);
      58. }
      59. return resultToken;
      60. }
      61. public List<Token> Tokenize(){
      62. List<Token> result = new List<Token> ();
      63. Token nT = null;
      64. do {
      65. nT = nextToken();
      66. if(nT.Type!=TokenType.WS && nT.Type != TokenType.EOF)
      67. result.Add(nT);
      68. } while ( nT.Type != TokenType.EOF );
      69. return result;
      70. }
      71. }
      72. }


      Die Regeln werden folgendermaßen in der LoadRules Methode hinzugefügt:

      C#-Quellcode

      1. LRules.Add (new LexingRule(new LexingState[]{LexingState.INITIAL}, "[a-zA-Z][a-zA-Z0-9]*",
      2. delegate(string content, Match m)
      3. {
      4. return new Token(content, TokenType.ID);
      5. }
      6. ));



      Das war es auch schon. Hoffe jemand kann damit etwas anfangen.

      8-) faxe1008 8-)