Lexer.lex   F
last analyzed

Complexity

Conditions 18

Size

Total Lines 84
Code Lines 65

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 55
CRAP Score 18

Importance

Changes 0
Metric Value
cc 18
eloc 65
dl 0
loc 84
ccs 55
cts 55
cp 1
crap 18
rs 1.2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like Lexer.lex often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1 3
import Token, { TokenType } from './Token'
2
3 3
const OPERATORS = {
4
  '+': 'add',
5
  '-': 'sub',
6
  '*': 'mul',
7
  '/': 'div',
8
  '**': 'pow',
9
  '^': 'pow',
10
  '=': 'set',
11
  '==': 'equals',
12
  '<': 'isLessThan',
13
  '>': 'isGreaterThan',
14
}
15
16
/**
17
 * @class Lexer
18
 * @name Lexer
19
 */
20 3
export class Lexer {
21
  s: string
22
23
  /**
24
   * Initialize a lexer.
25
   */
26
  constructor(input: string) {
27 16
    this.s = input
28
  }
29
30
  /**
31
   * Yields tokens as they are lexed.
32
   *
33
   */
34
  *lex(): Generator<Token> {
35 14
    let onSpace = false
36 14
    let onInteger = false
37 14
    let onWord = false
38 14
    let onOperator = false
39 14
    let buffer = ''
40
41
    // for each character
42 14
    for (let i = 0; i < this.s.length; i++) {
43 59
      const char = this.s[i] ?? '?'
44
45
      // if a space, combine with subsequent spaces into a separator
46 59
      if (/^\s$/.exec(char)) {
47 12
        if (onInteger) {
48 4
          yield new Token(TokenType.literal, buffer)
49 4
          onInteger = false
50 4
          buffer = ''
51 8
        } else if (onWord) {
52 2
          yield new Token(TokenType.identifier, buffer)
53 2
          onWord = false
54 2
          buffer = ''
55
        }
56 12
        onSpace = true
57 12
        buffer += char
58 12
        continue
59 47
      } else if (onSpace) {
60 7
        yield new Token(TokenType.separator, '')
61 7
        onSpace = false
62 7
        buffer = ''
63
      }
64
65
      // if a digit, combine with subsequent digits into a literal
66 47
      if (/^\d$/.exec(char)) {
67 14
        if (!onWord) {
68 11
          onInteger = true
69
        }
70 14
        buffer += char
71 14
        continue
72 33
      } else if (onInteger) {
73 1
        yield new Token(TokenType.literal, buffer)
74 1
        onInteger = false
75 1
        buffer = ''
76
      }
77
78
      // if a letter, combine with subsequent letters/numbers into an identifier
79 33
      if (/^[a-z]$/.exec(char) || (onWord && /^\d$/.exec(char))) {
80 26
        onWord = true
81 26
        buffer += char
82 26
        continue
83 7
      } else if (onWord) {
84 1
        yield new Token(TokenType.identifier, buffer)
85 1
        onWord = false
86 1
        buffer = ''
87
      }
88
89
      // if another char, combine with subsequent char and match an operator
90 7
      buffer += char
91 7
      if (buffer in OPERATORS) {
92 3
        yield new Token(TokenType.operator, buffer)
93 3
        onOperator = false
94 3
        buffer = ''
95 3
        continue
96
      }
97 4
      if (onOperator) {
98 2
        throw `Invalid operator "${buffer}"`
99
      }
100 2
      onOperator = true
101
    }
102
103 12
    if (onSpace) {
104 3
      yield new Token(TokenType.separator, '')
105
    }
106
107 12
    if (onInteger) {
108 3
      yield new Token(TokenType.literal, buffer)
109
    }
110
111 12
    if (onWord) {
112 6
      yield new Token(TokenType.identifier, buffer)
113
    }
114
  }
115
}
116
117
export default Lexer
118