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,6 +36,10 @@ class OsloCLI {
36 36
       process.exit()
37 37
     }
38 38
 
39
+    if (output.error) {
40
+      this.error(output.error)
41
+    }
42
+
39 43
     if (opts.o) {
40 44
       const absoluteOutputPath = this.absolutePath(opts.o)
41 45
       if (opts.f || opts.e) {
@@ -126,6 +130,11 @@ class OsloCLI {
126 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 140
 new OsloCLI(args)

+ 7
- 0
src/error.js View File

@@ -0,0 +1,7 @@
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,6 +7,11 @@ module.exports = function oslo(source, context) {
7 7
   const tokens = lexer.scan(source)
8 8
   const parser = new Parser(tokens)
9 9
   const tree = parser.parse()
10
+
11
+  if (tree.error) {
12
+    return tree
13
+  }
14
+
10 15
   const compiler = new Compiler(tree, context)
11 16
   return compiler.compile()
12 17
 }

+ 16
- 0
src/parser.js View File

@@ -1,3 +1,4 @@
1
+const Error = require('./Error')
1 2
 const Node = require('./node')
2 3
 const tokenTypes = require('./tokenTypes')
3 4
 
@@ -10,6 +11,11 @@ module.exports = class Parser {
10 11
     let tree = []
11 12
     while (this.tokenStream.peek().type !== tokenTypes.EOF) {
12 13
       tree.push(this.expr())
14
+      if (this.tokenStream.error) {
15
+        return {
16
+          error: this.tokenStream.error,
17
+        }
18
+      }
13 19
     }
14 20
     return tree
15 21
   }
@@ -23,6 +29,8 @@ module.exports = class Parser {
23 29
       this.tokenStream.peek().type !== tokenTypes.CPAREN &&
24 30
       this.tokenStream.peek().type !== tokenTypes.EOF
25 31
     ) {
32
+      if (this.tokenStream.error) return
33
+
26 34
       if (this.tokenStream.peek().type === tokenTypes.LITERAL) {
27 35
         node = this.element()
28 36
       } else if (this.tokenStream.peek().type === tokenTypes.KEYWORD) {
@@ -48,6 +56,8 @@ module.exports = class Parser {
48 56
         this.tokenStream.peek().type,
49 57
       )
50 58
     ) {
59
+      if (this.tokenStream.error) return
60
+
51 61
       if (this.tokenStream.peek().type === tokenTypes.ATTRIBUTE) {
52 62
         elementNode.args.push(this.attribute())
53 63
       } else if (this.tokenStream.peek().type === tokenTypes.QUOTE) {
@@ -71,6 +81,12 @@ module.exports = class Parser {
71 81
       attributeNode.attributeValue = this.quotedString()
72 82
     } else if (this.tokenStream.peek().type === tokenTypes.LITERAL) {
73 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 92
     return attributeNode

+ 13
- 3
src/tokenStream.js View File

@@ -1,3 +1,5 @@
1
+const Error = require('./error')
2
+
1 3
 module.exports = class TokenStream {
2 4
   constructor() {
3 5
     this.tokens = []
@@ -9,11 +11,19 @@ module.exports = class TokenStream {
9 11
   }
10 12
 
11 13
   eat(tokenType) {
12
-    if (this.peek() && this.peek().type === tokenType) {
14
+    let token = this.peek()
15
+
16
+    if (token && token.type === tokenType) {
13 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 29
   peek(step = 0) {

+ 1
- 1
src/tokenTypes.js View File

@@ -6,5 +6,5 @@ module.exports = {
6 6
   LITERAL: 'literal',
7 7
   ATTRIBUTE: 'attribute',
8 8
   SYMBOL: 'symbol',
9
-  EOF: 'eof',
9
+  EOF: 'EOF',
10 10
 }

+ 32
- 0
test/parser.js View File

@@ -41,3 +41,35 @@ test('parses token stream into a tree', t => {
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