Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I match a regex in a switch statement?

I'm trying to match tokens against a switch statement in a function, and one of those tokens needs to be able to identify any string or any numbers defined by the regex in the code below.

Basically, is it possible to define a regex against a case like case "[a-z]+":

Clearly the way I have it now, my pattern is not reachable unless I pass STRING or NUMBER as an argument.

public Token analyzeToken(String token) {
      Token tokenType = null;     

      switch (token) {

         case "STRING":
            Pattern p = Pattern.compile("[a-z]+");
            Matcher m = p.matcher(token);
            if(m.matches()) {
               tokenType = Token.STRING;
               break;
            }
         case "NUMBER":
            Pattern p = Pattern.compile("[0-9]+");
            Matcher m = p.matcher(token);
            if(m.matches()) {
               tokenType = Token.NUMBER;
               break;

         case "(":
            tokenType = Token.LEFT_PAREN;
            break;
         case ")":
            tokenType = Token.RIGHT_PAREN;
            break;
         case ".":
            tokenType = Token.PERIOD;
            break;
         case ":":
            tokenType = Token.COLON;
            break;
         case ";":
            tokenType = Token.SEMICOLON;

         default:
            tokenType = TOKEN.UNKNOWN;
            break;

      }
   }
like image 463
hax0r_n_code Avatar asked Feb 07 '16 04:02

hax0r_n_code


People also ask

How do I add a switch statement to a regex?

“Smart alec. How do I add a switch statement?” she asked. “You need to type the word switch and use the regex switched parameter. Then you use the Get-Content command that you used yesterday to read the content from all your files. Next, you open and close a set of curly brackets.”

What does the-regex parameter do in a switch statement?

The -Regex parameter allows switch statements to perform regular expression matching against conditions. switch -Regex ('Condition') { 'Con\D+ion' {'One or more non-digits'} 'Conditio*$' {'Zero or more "o"'} 'C.ndition' {'Any single char.'} '^C\w+ition$' {'Anchors and one or more word chars.'} 'Test' {'No match'} }

Can I use a regex as a caseexp?

I think the switch statement evaluates (switchexp == caseexp), so using a regex as a caseexp does not work as intended - it tests if the switchexp IS the regex instead of if the regex matches the switchexp. A switch actually performs === tests not == tests.

How do you use a switch statement in Python?

One of them is with a switch. The switch statement allows you to provide a variable and a list of possible values. If the value matches the variable, then its scriptblock is executed. For this example, the value of $day matches one of the numeric values, then the correct name is assigned to $result.


3 Answers

Don't do it in a switch statement, do it in a conditional or better yet, in a loop:

private interface TokenMatcher {
    Token match(String s);
}
static List<TokenMatcher> matchers = new ArrayList<>();
static {
    final Pattern strPattern = Pattern.compile("[a-z]+");
    final Pattern numPattern = Pattern.compile("[0-9]+");
    matchers.add( new TokenMatcher {
        public Token match(String s) {
            Matcher m = strPattern.matcher(s);
            return m.matches() ? Token.STRING : null;
        }
    });
    matchers.add( new TokenMatcher {
        public Token match(String s) {
            Matcher m = numPattern.matcher(s);
            return m.matches() ? Token.NUMBER : null;
        }
    });
}

Now you can do this:

static Token match(String s) {
    for (TokenMatcher m : matchers) {
        Token t = m.match(s);
        if (t != null) {
            return t;
        }
    }
    return TOKEN.UNKNOWN;
}

The for loop has taken place of the switch statement, while the entries in the matchers list have taken place of the individual cases in the switch. Adding new token types is as simple as adding new patterns and their associated implementations to the matchers list.

Edit: You could make the solution shorter by replacing the interface above with a class, like this:

private static final class TokenMatcher {
    private final Pattern p;
    private final Token t;
    public TokenMatcher(String pString, Token t) {
        p = Pattern.compile(pString);
        this.t = t;
    }
    public Token match(String s) {
        Matcher m = p.matcher(s);
        return m.matches() ? t: null;
    }
}

Now your matchers initialization could be done like this:

matchers.add(new TokenMatcher("[a-z]+", Token.STRING));
matchers.add(new TokenMatcher("[0-9]+", Token.NUMBER));
like image 155
Sergey Kalinichenko Avatar answered Sep 21 '22 00:09

Sergey Kalinichenko


This solution inspired by solution of @dasblinkenlight. Just attempt to improve it.

public class TokenMatcher{

    private HashMap<String, Token> tokenMap = new HashMap<String, Token>();
    {
        tokenMap.put("[a-z]+", Token.STRING);
        tokenMap.put("[0-9]+", Token.NUMBER);
        tokenMap.put("\\(", Token.RIGHT_PARENT);
        tokenMap.put("\\)", Token.LEFT_PARENT);
        ...
    }


    public Token match(String s){
        for(String key : tokenMap.keySet()){
            Pattern pattern = Pattern.compile(key);
            Matcher matcher = pattern.matcher(s);
            if(matcher.matches()) return tokenMap.get(key);
        }
        return Token.UNKNOWN;
    }
}

Improvements: easier to add new token, less repeated code, don't need extra interfaces.

like image 32
Ken Bekov Avatar answered Sep 22 '22 00:09

Ken Bekov


You'll need 2 parameters:

public Token analyzeToken(String token, String string) {
      Token tokenType = null;     
      switch (token) {
         case "STRING":
            Pattern p = Pattern.compile("[a-z]+");
            Matcher m = p.matcher(string); // match the string, not the token!
            if(m.matches()) {
               tokenType = Token.STRING;
               break;
            }

update:

public Token analyzeToken(String regex, String string) {
            Pattern p = Pattern.compile(regex);
            Matcher m = p.matcher(string); // match the string, not the token!
            if(m.matches()) {
               // ...
            }
like image 28
Gavriel Avatar answered Sep 22 '22 00:09

Gavriel