Passed
Push — main ( 8104af...1d7420 )
by Dylan
04:05
created

Lexer.lex   F

Complexity

Conditions 16

Size

Total Lines 89
Code Lines 65

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 55
CRAP Score 16

Importance

Changes 0
Metric Value
eloc 65
dl 0
loc 89
ccs 55
cts 55
cp 1
rs 2.4
c 0
b 0
f 0
cc 16
crap 16

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
        }
52 8
        else if (onWord) {
53 2
          yield new Token(TokenType.identifier, buffer)
54 2
          onWord = false
55 2
          buffer = ''
56
        }
57 12
        onSpace = true
58 12
        buffer += char
59 12
        continue
60
      }
61 47
      else if (onSpace) {
62 7
        yield new Token(TokenType.separator, '')
63 7
        onSpace = false
64 7
        buffer = ''
65
      }
66
67
      // if a digit, combine with subsequent digits into a literal
68 47
      if (/^\d$/.exec(char)) {
69 14
        if (!onWord) {
70 11
          onInteger = true
71
        }
72 14
        buffer += char
73 14
        continue
74
      }
75 33
      else if (onInteger) {
76 1
        yield new Token(TokenType.literal, buffer)
77 1
        onInteger = false
78 1
        buffer = ''
79
      }
80
81
      // if a letter, combine with subsequent letters/numbers into an identifier
82 33
      if (/^[a-z]$/.exec(char) || (onWord && /^\d$/.exec(char))) {
83 26
        onWord = true
84 26
        buffer += char
85 26
        continue
86
      }
87 7
      else if (onWord) {
88 1
        yield new Token(TokenType.identifier, buffer)
89 1
        onWord = false
90 1
        buffer = ''
91
      }
92
93
      // if another char, combine with subsequent char and match an operator
94 7
      buffer += char
95 7
      if (buffer in OPERATORS) {
96 3
        yield new Token(TokenType.operator, buffer)
97 3
        onOperator = false
98 3
        buffer = ''
99 3
        continue
100
      }
101 4
      if (onOperator) {
102 2
        throw `Invalid operator "${buffer}"`
103
      }
104 2
      onOperator = true
105
106
    }
107
108 12
    if (onSpace) {
109 3
      yield new Token(TokenType.separator, '')
110
    }
111
112 12
    if (onInteger) {
113 3
      yield new Token(TokenType.literal, buffer)
114
    }
115
116 12
    if (onWord) {
117 6
      yield new Token(TokenType.identifier, buffer)
118
    }
119
120
  }
121
122
}
123
124
export default Lexer
125