Files
  • index.js
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
const lexer = input => {
  let tokens = []
  let current = 0
  
  while (current < input.length) {
    let char = input[current]
    
    if (char === " ") {
      current++
      continue
    }

    if (char === "+") {
      tokens.push({ type: "Plus" })
      current++
      continue
    }

    if (char === "-") {
      tokens.push({ type: "Minus" })
      current++
      continue
    }

    if (char === "*") {
      tokens.push({ type: "Star" })
      current++
      continue
    }

    if (char === "/") {
      tokens.push({ type: "Slash" })
      current++
      continue
    }
    
    if (char === "(") {
      tokens.push({ type: "LeftParen" })
      current++
      continue
    }

    if (char === ")") {
      tokens.push({ type: "RightParen" })
      current++
      continue
    }

    if (/[0-9]/.test(char)) {
      let value = ''

      while (/[0-9]/.test(char)) {
        value += char
        current++
        char = input[current]
      }
      
      tokens.push({ type: "Number", value: parseInt(value, 10) })
      
      continue
    }

    throw new Error(`Unexpected char ${char}`)
  }

  tokens.push({ type: "End" })

  return tokens
}  

const parser = tokens => {
  let current = 0

  const peek = () => tokens[current]
  const prev = () => tokens[current - 1]
  const match = type => {
    if (peek().type === type) {
      current++
      return true
    }
    return false
  }

  const expr = () => {
    return add()
  }

  const add = () => {
    let result = mul()

    while (match('Plus')) {
      result = result + mul()
    }

    return result
  }

  const mul = () => {
    let result = num()

    while (match('Star')) {
      result = result * num()
    }    

    return result
  }

  const num = () => {
    if (match('Number')) {
      return prev().value
    }

    throw new Error(`Unexpected ${peek().type} token`)
  }

  return expr()
}

console.log('4 + 3 * 2 =', parser(lexer('4 + 3 * 2')))
console.log('4 * 3 + 2 =', parser(lexer('4 * 3 + 2')))
node v10.15.2 linux/amd64