Browse Source

Implement some very basic error handling

It's a little hacky and not very clean but it's
working for now.
master
Dylan Baker 5 years ago
parent
commit
64e2ab8eb0
7 changed files with 83 additions and 4 deletions
  1. 9
    0
      bin/oslo.js
  2. 7
    0
      src/error.js
  3. 5
    0
      src/index.js
  4. 16
    0
      src/parser.js
  5. 13
    3
      src/tokenStream.js
  6. 1
    1
      src/tokenTypes.js
  7. 32
    0
      test/parser.js

+ 9
- 0
bin/oslo.js View File

36
       process.exit()
36
       process.exit()
37
     }
37
     }
38
 
38
 
39
+    if (output.error) {
40
+      this.error(output.error)
41
+    }
42
+
39
     if (opts.o) {
43
     if (opts.o) {
40
       const absoluteOutputPath = this.absolutePath(opts.o)
44
       const absoluteOutputPath = this.absolutePath(opts.o)
41
       if (opts.f || opts.e) {
45
       if (opts.f || opts.e) {
126
       oslo -d templates -o html`,
130
       oslo -d templates -o html`,
127
     )
131
     )
128
   }
132
   }
133
+
134
+  error(err) {
135
+    console.log(err.message)
136
+    process.exit()
137
+  }
129
 }
138
 }
130
 
139
 
131
 new OsloCLI(args)
140
 new OsloCLI(args)

+ 7
- 0
src/error.js View File

1
+module.exports = class Error {
2
+  constructor(opts) {
3
+    this.file = opts.file
4
+    this.line = opts.line
5
+    this.message = opts.message
6
+  }
7
+}

+ 5
- 0
src/index.js View File

7
   const tokens = lexer.scan(source)
7
   const tokens = lexer.scan(source)
8
   const parser = new Parser(tokens)
8
   const parser = new Parser(tokens)
9
   const tree = parser.parse()
9
   const tree = parser.parse()
10
+
11
+  if (tree.error) {
12
+    return tree
13
+  }
14
+
10
   const compiler = new Compiler(tree, context)
15
   const compiler = new Compiler(tree, context)
11
   return compiler.compile()
16
   return compiler.compile()
12
 }
17
 }

+ 16
- 0
src/parser.js View File

1
+const Error = require('./Error')
1
 const Node = require('./node')
2
 const Node = require('./node')
2
 const tokenTypes = require('./tokenTypes')
3
 const tokenTypes = require('./tokenTypes')
3
 
4
 
10
     let tree = []
11
     let tree = []
11
     while (this.tokenStream.peek().type !== tokenTypes.EOF) {
12
     while (this.tokenStream.peek().type !== tokenTypes.EOF) {
12
       tree.push(this.expr())
13
       tree.push(this.expr())
14
+      if (this.tokenStream.error) {
15
+        return {
16
+          error: this.tokenStream.error,
17
+        }
18
+      }
13
     }
19
     }
14
     return tree
20
     return tree
15
   }
21
   }
23
       this.tokenStream.peek().type !== tokenTypes.CPAREN &&
29
       this.tokenStream.peek().type !== tokenTypes.CPAREN &&
24
       this.tokenStream.peek().type !== tokenTypes.EOF
30
       this.tokenStream.peek().type !== tokenTypes.EOF
25
     ) {
31
     ) {
32
+      if (this.tokenStream.error) return
33
+
26
       if (this.tokenStream.peek().type === tokenTypes.LITERAL) {
34
       if (this.tokenStream.peek().type === tokenTypes.LITERAL) {
27
         node = this.element()
35
         node = this.element()
28
       } else if (this.tokenStream.peek().type === tokenTypes.KEYWORD) {
36
       } else if (this.tokenStream.peek().type === tokenTypes.KEYWORD) {
48
         this.tokenStream.peek().type,
56
         this.tokenStream.peek().type,
49
       )
57
       )
50
     ) {
58
     ) {
59
+      if (this.tokenStream.error) return
60
+
51
       if (this.tokenStream.peek().type === tokenTypes.ATTRIBUTE) {
61
       if (this.tokenStream.peek().type === tokenTypes.ATTRIBUTE) {
52
         elementNode.args.push(this.attribute())
62
         elementNode.args.push(this.attribute())
53
       } else if (this.tokenStream.peek().type === tokenTypes.QUOTE) {
63
       } else if (this.tokenStream.peek().type === tokenTypes.QUOTE) {
71
       attributeNode.attributeValue = this.quotedString()
81
       attributeNode.attributeValue = this.quotedString()
72
     } else if (this.tokenStream.peek().type === tokenTypes.LITERAL) {
82
     } else if (this.tokenStream.peek().type === tokenTypes.LITERAL) {
73
       attributeNode.attributeValue = this.identifier()
83
       attributeNode.attributeValue = this.identifier()
84
+    } else {
85
+      let token = this.tokenStream.peek()
86
+      this.tokenStream.error = new Error({
87
+        line: token.line,
88
+        message: `Encountered an unexpected ${token.type} while looking for a string or identifier.`
89
+      })
74
     }
90
     }
75
 
91
 
76
     return attributeNode
92
     return attributeNode

+ 13
- 3
src/tokenStream.js View File

1
+const Error = require('./error')
2
+
1
 module.exports = class TokenStream {
3
 module.exports = class TokenStream {
2
   constructor() {
4
   constructor() {
3
     this.tokens = []
5
     this.tokens = []
9
   }
11
   }
10
 
12
 
11
   eat(tokenType) {
13
   eat(tokenType) {
12
-    if (this.peek() && this.peek().type === tokenType) {
14
+    let token = this.peek()
15
+
16
+    if (token && token.type === tokenType) {
13
       this.advance()
17
       this.advance()
14
-      return this.peek(-1)
18
+      return token
15
     }
19
     }
16
-    return false
20
+
21
+    this.error = new Error({
22
+      line: token.line,
23
+      message: `Encountered an unexpected ${
24
+        token.type
25
+      } while looking for a ${tokenType}.`,
26
+    })
17
   }
27
   }
18
 
28
 
19
   peek(step = 0) {
29
   peek(step = 0) {

+ 1
- 1
src/tokenTypes.js View File

6
   LITERAL: 'literal',
6
   LITERAL: 'literal',
7
   ATTRIBUTE: 'attribute',
7
   ATTRIBUTE: 'attribute',
8
   SYMBOL: 'symbol',
8
   SYMBOL: 'symbol',
9
-  EOF: 'eof',
9
+  EOF: 'EOF',
10
 }
10
 }

+ 32
- 0
test/parser.js View File

41
     }),
41
     }),
42
   ])
42
   ])
43
 })
43
 })
44
+
45
+test('missing close paren returns an error', function(t) {
46
+  t.plan(2)
47
+  const lexer = new Lexer()
48
+  let tokenStream = lexer.scan(`
49
+    (div :class "foobar"
50
+  `)
51
+  let parser = new Parser(tokenStream)
52
+  let tree = parser.parse()
53
+  t.equal(tree.error.constructor.name, 'Error')
54
+  t.deepEqual(tree.error, {
55
+    file: undefined,
56
+    line: 3,
57
+    message: 'Encountered an unexpected EOF while looking for a ).',
58
+  })
59
+})
60
+
61
+test('unexpected attribute return an error', function(t) {
62
+  t.plan(2)
63
+  const lexer = new Lexer()
64
+  let tokenStream = lexer.scan(`
65
+    (div :class :id)
66
+  `)
67
+  let parser = new Parser(tokenStream)
68
+  let tree = parser.parse()
69
+  t.equal(tree.error.constructor.name, 'Error')
70
+  t.deepEqual(tree.error, {
71
+    file: undefined,
72
+    line: 2,
73
+    message: 'Encountered an unexpected attribute while looking for a string or identifier.',
74
+  })
75
+})

Loading…
Cancel
Save