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):
Ich habe mich bei der Umsetzung des Scanners sehr an JFlex orientiert, in welchem eine Scannerregel folgendermaßen aufgebaut ist:
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
Die Klasse LexingRule die eine LexerRegel darstellt:
Spoiler anzeigen
Spoiler anzeigen
Die Regeln werden folgendermaßen in der LoadRules Methode hinzugefügt:
Das war es auch schon. Hoffe jemand kann damit etwas anfangen.
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):
Ich habe mich bei der Umsetzung des Scanners sehr an JFlex orientiert, in welchem eine Scannerregel folgendermaßen aufgebaut ist:
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:
C#-Quellcode
- using System;
- namespace CSFlex
- {
- public enum TokenType{
- EOF,
- ID,
- COMMA,
- LSBR,
- RSBR,
- WS,
- NUMBER,
- NULL
- }
- public class Token
- {
- public String Content{ get; set;}
- public TokenType Type{ get; set;}
- public int startLine{ get; set;}
- public int startColumn{ get; set;}
- public Token (string content, TokenType type )
- {
- this.Content = content;
- this.Type = type;
- }
- public Token (string content, TokenType type, int startLine, int startColumn)
- {
- this.Content = content;
- this.Type = type;
- this.startLine = startLine;
- this.startColumn = startColumn;
- }
- }
- }
Die Klasse LexingRule die eine LexerRegel darstellt:
C#-Quellcode
- using System;
- using System.Text.RegularExpressions;
- namespace CSFlex
- {
- public enum LexingState{
- INITIAL,
- NUM,
- ERROR
- }
- public class LexingRule
- {
- public LexingState[] LexingState{ get; set;}
- public Regex Pattern{ get; set;}
- public Func<string, Match, Token> Action{ get; set;}
- public LexingRule (LexingState[] ls, String pattern, Func<string, Match, Token> action)
- {
- this.LexingState = ls;
- this.Pattern = new Regex ("^" + pattern, RegexOptions.Compiled);
- this.Action = action;
- }
- public Boolean ruleMatches(LexingState ls, String input){
- return Array.Exists(LexingState, l => l == ls) && Pattern.IsMatch(input);
- }
- public Match getMatch(String input){
- Match m = Pattern.Match (input);
- if (m.Success) {
- return m;
- }
- return null;
- }
- }
- }
C#-Quellcode
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Text.RegularExpressions;
- namespace CSFlex
- {
- public class CSFlexer
- {
- private int startLine =1, startColumn=1;
- private string toParse;
- private string remainingText;
- private List<LexingRule> LRules = new List<LexingRule>();
- private LexingState CurrentState = LexingState.INITIAL;
- private StringBuilder stringBuffer = new StringBuilder();
- private double numBuffer;
- public CSFlexer(string text){
- startLine = 0;
- startColumn = 0;
- toParse = text;
- remainingText = text;
- numBuffer = 0;
- loadRules ();
- }
- public void pushBack(int length){
- remainingText = toParse.Substring (toParse.Length - (remainingText.Length + length));
- }
- public void changeState(LexingState ls){
- CurrentState = ls;
- }
- public void loadRules(){
- }
- public LexingRule getMatchingRule(ref Match m){
- LexingRule result = null;
- Match mt = null;
- int currentLength = -1;
- foreach (LexingRule ls in LRules) {
- if (ls.ruleMatches (CurrentState, remainingText)) {
- mt = ls.getMatch (remainingText);
- if (mt.Length > currentLength) {
- result = ls;
- currentLength = mt.Length;
- m = mt;
- }
- }
- }
- return result;
- }
- public Token nextToken(){
- Token resultToken = null;
- while(resultToken==null){
- Match m = null;
- LexingRule lr = getMatchingRule (ref m);
- string yytext = "";
- if (remainingText != "")
- yytext = m.Value;
- remainingText = remainingText.Substring (m.Length);
- resultToken = lr.Action.Invoke (yytext, m);
- }
- return resultToken;
- }
- public List<Token> Tokenize(){
- List<Token> result = new List<Token> ();
- Token nT = null;
- do {
- nT = nextToken();
- if(nT.Type!=TokenType.WS && nT.Type != TokenType.EOF)
- result.Add(nT);
- } while ( nT.Type != TokenType.EOF );
- return result;
- }
- }
- }
Die Regeln werden folgendermaßen in der LoadRules Methode hinzugefügt:
Das war es auch schon. Hoffe jemand kann damit etwas anfangen.
faxe1008