JSFiddle - React, Tailwind, and code Playground

by kasheftin

HTML

<div id="container" class="container" />

CSS

.container {
  font-size: 30px;
  line-height: 30px;
  font-family: monospace;
  margin-top: 400px;
}

span {
  margin: 0 2px;
  position: relative;
}

span::after {
  font-size: 12px;
  line-height: 12px;
  content: var(--content);
  position: absolute;
  top: -10px;
  left: 10px;
  padding: 0 5px;
  transform-origin: 0 0;
  transform: rotate(-45deg);
  border: 1px solid #000;
}

.BracketStart, .BracketEnd {
  background-color: #2dd4bf;
}

.ReferenceBracketStart, .ReferenceBracketEnd {
  background-color: #f472b6;
}

.ReferenceName {
  background-color: #fb7185;
}

.Number, .String {
  background-color: #818cf8;
}

.DoubleQuoteStart, .DoubleQuoteEnd {
  background-color: #38bdf8;
}

.Operator {
  background-color: #a1c420;
}

.Whitespace {
  background-color: #cbd5e1;
}

.FunctionName {
  background-color: #34d399;
}

TypeScript

enum TokenType {
  Number = 'Number',
  String = 'String',
  Whitespace = 'Whitespace',
  Operator = 'Operator',
  BracketStart = 'BracketStart',
  BracketEnd = 'BracketEnd',
  ReferenceBracketStart = 'ReferenceBracketStart',
  ReferenceBracketEnd = 'ReferenceBracketEnd',
  ReferenceName = 'ReferenceName',
  FunctionName = 'FunctionName',
  Comma = 'Comma',
  QuoteStart = 'QuoteStart',
  QuoteEnd = 'QuoteEnd',
  EmptyStringAndQuoteEnd = 'EmptyStringAndQuoteEnd',
  DoubleQuoteStart = 'DoubleQuoteStart',
  DoubleQuoteEnd = 'DoubleQuoteEnd',
  EmptyStringAndDoubleQuoteEnd = 'EmptyStringAndDoubleQuoteEnd',
  Group = 'Group',
  Error = 'Error'
}

type Token = {
  type: TokenType
  value: string
}

interface LexerStream {
  match: (pattern: RegExp, consume: boolean) => string | undefined
  skip: () => void
  prev: TokenType | null
}

function getNextToken ({ match, skip, prev }: LexerStream): TokenType {
  if (prev !== TokenType.QuoteStart && match(/^"/, true)) {
    if (prev === TokenType.String) {
      return TokenType.DoubleQuoteEnd
    } else if (prev === TokenType.DoubleQuoteStart) {
      return TokenType.EmptyStringAndDoubleQuoteEnd
    } else {
      return TokenType.DoubleQuoteStart
    }
  }

  if (match(/^'/, true)) {
    if (prev === TokenType.String) {
      return TokenType.QuoteEnd
    } else if (prev === TokenType.QuoteStart) {
      return TokenType.EmptyStringAndQuoteEnd
    } else {
      return TokenType.QuoteStart
    }
  }

  if (prev === TokenType.DoubleQuoteStart) {
    if (match(/^([^"\\]|\\.)+(?=")/, false)) {
      match(/^([^"\\]|\\.)+(?=")/, true)
      return TokenType.String
    } else {
      match(/^([^"\\]|\\.)+/, true)
      return TokenType.String
    }
  }

  if (prev === TokenType.QuoteStart) {
    if (match(/^([^'\\]|\\.)+(?=')/, false)) {
      match(/^([^'\\]|\\.)+(?=')/, true)
      return TokenType.String
    } else {
      match(/^([^'\\]|\\.)+/, true)
      return TokenType.String
    }
  }

  const numberRegex =...