0

I want to use regex like a state machine, feeding only one character at a time.

Example:

// this matches only 13 or 123
const createStateMachine = () => {
  const machine = {
    "": {
      1: "1",
    },
    1: {
      2: "2",
      3: "3",
    },
    2: {
      3: "3",
    },
  };

  let state = "";

  return (value) => {
    const nextState = machine[state][value];
    if (nextState) {
      state = nextState;
      return true;
    }
    return false;
  };
};

const exec = createStateMachine();

document.querySelector("input").addEventListener("keydown", (event) => {
  if (!exec(event.key)) {
    event.preventDefault();
  }
});
This input allows only 13 and 123
<input>

And here is the regex version but it obviously doesn't work:

const regex = /^12?3$/

document.querySelector("input").addEventListener("keydown", (event) => {
  if (!regex.exec(event.key)) {
    event.preventDefault();
  }
});
<input>

It is possible to use the built-in regex object for that?

Konrad
  • 21,590
  • 4
  • 28
  • 64
  • You could use the positive lookbehind for this, but I don't think it's a good idea. Just save your states in an array without over-engineering – Christian Vincenzo Traina May 29 '23 at 13:44
  • On `keydown`, you only get the last char as input, so your pattern naturally does not match since it requires at least two chars. – Wiktor Stribiżew May 29 '23 at 13:44
  • 2
    It sounds like you're looking for some kind of a _so-far-so-good_ indication from your regular expression. Like specifically you're looking for a deal-breaker in the text that may not be a full-match yet (as the user is still typing), but contains something that precludes it from ever being a match if characters are only appended. I'm afraid you may have to write a second expression that matches a partial match and look for a match against that, making judicious use of `^`. If I misunderstand, please clarify. – Wyck May 29 '23 at 13:45
  • 2
    How about setting the event listener for `blur` instead of `keydown`, and making the `` `:invalid` while displaying a message using [`.setCustomValidity()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setCustomValidity) instead of `.preventDefault()`? – InSync May 29 '23 at 13:52
  • 1
    Your state machine is missing a state `"3": {}`. Also it does not handle backspace, deletion, cut/copy-and-paste, etc. Is this really what you want? – Bergi May 29 '23 at 13:52
  • Be aware that your last sentence, which is roughly: _is it possible to use Y to accomplish X_, is literally the blueprint for an [XY problem](https://xyproblem.info/). – Wyck May 29 '23 at 14:04
  • @Bergi I'm aware of that, I didn't want to overcomplicate the example :) – Konrad May 29 '23 at 14:06
  • @Wyck you understood it correctly :) I made the question too focused on the input part of it, but it was just an example. *so-far-so-good indication from regular expression* is a better description of what I meant – Konrad May 29 '23 at 14:16
  • 2
    @Konrad thanks for clarifying about _so-far-so-good_. You may be interested in [Partial matching a string against a regex](https://stackoverflow.com/q/42461651/1563833) and [Regex - check if input still has chances to become matching](https://stackoverflow.com/q/22483214/1563833) – Wyck May 29 '23 at 14:25

1 Answers1

2

It is possible to use the built-in regex object for that?

No.

You can either parse the regular expression and build your own state machine from that, or you write (or generate) a regular expression that matches any prefix of the regular language, but there exist no builtin methods for either of this.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375