| // Copyright 2019 Google LLC | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| import { assert } from "chai"; | |
| import Lexer from "./lexer"; | |
| import { TokenType } from "./token"; | |
| describe("lexer", () => { | |
| describe("skipped content", () => { | |
| it("skips whitespace", () => { | |
| let input = " \t\r\n\t \tOpKill\t\n\t \r "; | |
| let l = new Lexer(input); | |
| let t = l.next(); | |
| assert.equal(t.type, TokenType.kOp); | |
| assert.equal(t.line, 2); | |
| assert.equal(t.data.name, "OpKill"); | |
| t = l.next(); | |
| assert.equal(t.type, TokenType.kEOF); | |
| assert.equal(t.line, 3); | |
| }); | |
| it("skips ; comments", () => { | |
| let input = `; start with comment | |
| OpKill ; end of line comment | |
| ; another comment | |
| %1`; | |
| let l = new Lexer(input); | |
| let t = l.next(); | |
| assert.equal(t.type, TokenType.kOp); | |
| assert.equal(t.data.name, "OpKill"); | |
| assert.equal(t.line, 2); | |
| t = l.next(); | |
| assert.equal(t.type, TokenType.kResultId); | |
| assert.equal(t.data.name, "1"); | |
| assert.equal(t.data.val, 1); | |
| assert.equal(t.line, 4); | |
| }); | |
| }); | |
| describe("numerics", () => { | |
| it("parses floats", () => { | |
| let input = ["0.0", "0.", ".0", "5.7", "5.", ".7", "-0.0", "-.0", | |
| "-0.", "-5.7", "-5.", "-.7"]; | |
| let results = [0.0, 0.0, 0.0, 5.7, 5.0, 0.7, 0.0, 0.0, 0.0, -5.7, -5.0, | |
| -0.7]; | |
| input.forEach((val, idx) => { | |
| let l = new Lexer(val); | |
| let t = l.next(); | |
| assert.equal(t.type, TokenType.kFloatLiteral, | |
| `expected ${val} to be a float got ${t.type}`); | |
| assert.equal(t.data, results[idx], | |
| `expected ${results[idx]} === ${t.data}`); | |
| t = l.next(); | |
| assert.equal(t.type, TokenType.kEOF); | |
| assert.equal(t.data, undefined); | |
| }); | |
| }); | |
| it("handles invalid floats", () => { | |
| let input = [".", "-."]; | |
| input.forEach((val) => { | |
| let l = new Lexer(val); | |
| let t = l.next(); | |
| assert.notEqual(t.type, TokenType.kFloatLiteral, | |
| `expect ${val} to not match type float`); | |
| }); | |
| }); | |
| it("parses integers", () => { | |
| let input = ["0", "-0", "123", "-123", "2147483647", "-2147483648", | |
| "4294967295", "0x00", "0x24"]; | |
| let results = [0, 0, 123, -123,2147483647, -2147483648, 4294967295, | |
| 0x0, 0x24]; | |
| input.forEach((val, idx) => { | |
| let l = new Lexer(val); | |
| let t = l.next(); | |
| assert.equal(t.type, TokenType.kIntegerLiteral, | |
| `expected ${val} to be an integer got ${t.type}`); | |
| assert.equal(t.data, results[idx], | |
| `expected ${results[idx]} === ${t.data}`); | |
| t = l.next(); | |
| assert.equal(t.type, TokenType.kEOF); | |
| assert.equal(t.data, undefined); | |
| }); | |
| }); | |
| }); | |
| it("matches result_ids", () => { | |
| let input = `%123 | |
| %001 | |
| %main | |
| %_a_b_c`; | |
| let result = [ | |
| {name: "123", val: 123}, | |
| {name: "001", val: 1}, | |
| {name: "main", val: undefined}, | |
| {name: "_a_b_c", val: undefined} | |
| ]; | |
| let l = new Lexer(input); | |
| for (let i = 0; i < result.length; ++i) { | |
| let t = l.next(); | |
| assert.equal(t.type, TokenType.kResultId); | |
| assert.equal(t.data.name, result[i].name); | |
| assert.equal(t.data.val, result[i].val); | |
| } | |
| }); | |
| it("matches punctuation", () => { | |
| let input = "="; | |
| let results = [TokenType.kEqual]; | |
| let l = new Lexer(input); | |
| for (let i = 0; i < results.length; ++i) { | |
| let t = l.next(); | |
| assert.equal(t.type, results[i]); | |
| assert.equal(t.line, i + 1); | |
| } | |
| let t = l.next(); | |
| assert.equal(t.type, TokenType.kEOF); | |
| }); | |
| describe("strings", () => { | |
| it("matches strings", () => { | |
| let input = "\"GLSL.std.450\""; | |
| let l = new Lexer(input); | |
| let t = l.next(); | |
| assert.equal(t.type, TokenType.kStringLiteral); | |
| assert.equal(t.data, "GLSL.std.450"); | |
| }); | |
| it("handles unfinished strings", () => { | |
| let input = "\"GLSL.std.450"; | |
| let l = new Lexer(input); | |
| let t = l.next(); | |
| assert.equal(t.type, TokenType.kError); | |
| }); | |
| it("handles escapes", () => { | |
| let input = `"embedded\\"quote" | |
| "embedded\\\\slash" | |
| "embedded\\nchar"`; | |
| let results = [`embedded\"quote`, `embedded\\slash`, `embeddednchar`]; | |
| let l = new Lexer(input); | |
| for (let i = 0; i < results.length; ++i) { | |
| let t = l.next(); | |
| assert.equal(t.type, TokenType.kStringLiteral, results[i]); | |
| assert.equal(t.data, results[i]); | |
| } | |
| }); | |
| }); | |
| it("matches keywords", () => { | |
| let input = "GLSL Function"; | |
| let results = ["GLSL", "Function"]; | |
| let l = new Lexer(input); | |
| for (let i = 0; i < results.length; ++i) { | |
| let t = l.next(); | |
| assert.equal(t.type, TokenType.kIdentifier, results[i]); | |
| assert.equal(t.data, results[i]); | |
| } | |
| }); | |
| }); |