| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- // 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 { TokenType } from "./token.js";
- import * as AST from "./ast.js";
- export default class Parser {
- /**
- * @param {Hash} The SPIR-V grammar
- * @param {Lexer} The lexer
- * @return {AST} Attempts to build an AST from the tokens returned by the
- * given lexer
- */
- constructor(grammar, lexer) {
- this.grammar_ = grammar;
- this.lexer_ = lexer;
- this.peek_ = [];
- this.error_ = "";
- }
- get error() { return this.error_; }
- next() {
- return this.peek_.shift() || this.lexer_.next();
- }
- peek(idx) {
- while (this.peek_.length <= idx) {
- this.peek_.push(this.lexer_.next());
- }
- return this.peek_[idx];
- }
- /**
- * Executes the parser.
- *
- * @return {AST|undefined} returns a parsed AST on success or undefined
- * on error. The error message can be retrieved by
- * calling error().
- */
- parse() {
- let ast = new AST.Module();
- for(;;) {
- let token = this.next();
- if (token === TokenType.kError) {
- this.error_ = token.line() + ": " + token.data();
- return undefined;
- }
- if (token.type === TokenType.kEOF)
- break;
- let result_id = undefined;
- if (token.type === TokenType.kResultId) {
- result_id = token;
- token = this.next();
- if (token.type !== TokenType.kEqual) {
- this.error_ = token.line + ": expected = after result id";
- return undefined;
- }
- token = this.next();
- }
- if (token.type !== TokenType.kOp) {
- this.error_ = token.line + ": expected Op got " + token.type;
- return undefined;
- }
- let name = token.data.name;
- let data = this.getInstructionData(name);
- let operands = [];
- let result_type = undefined;
- for (let operand of data.operands) {
- if (operand.kind === "IdResult") {
- if (result_id === undefined) {
- this.error_ = token.line + ": expected result id";
- return undefined;
- }
- let o = new AST.Operand(ast, result_id.data.name, "result_id",
- result_id.data.val, []);
- if (o === undefined) {
- return undefined;
- }
- operands.push(o);
- } else {
- if (operand.quantifier === "?") {
- if (this.nextIsNewInstr()) {
- break;
- }
- } else if (operand.quantifier === "*") {
- while (!this.nextIsNewInstr()) {
- let o = this.extractOperand(ast, result_type, operand);
- if (o === undefined) {
- return undefined;
- }
- operands.push(o);
- }
- break;
- }
- let o = this.extractOperand(ast, result_type, operand);
- if (o === undefined) {
- return undefined;
- }
- // Store the result type away so we can use it for context dependent
- // numbers if needed.
- if (operand.kind === "IdResultType") {
- result_type = ast.getType(o.name());
- }
- operands.push(o);
- }
- }
- // Verify only GLSL extended instructions are used
- if (name === "OpExtInstImport" && operands[1].value() !== "GLSL.std.450") {
- this.error_ = token.line + ": Only GLSL.std.450 external instructions supported";
- return undefined;
- }
- let inst = new AST.Instruction(name, data.opcode, operands);
- ast.addInstruction(inst);
- }
- return ast;
- }
- getInstructionData(name) {
- return this.grammar_["instructions"][name];
- }
- nextIsNewInstr() {
- let n0 = this.peek(0);
- if (n0.type === TokenType.kOp || n0.type === TokenType.kEOF) {
- return true;
- }
- let n1 = this.peek(1);
- if (n1.type === TokenType.kEOF) {
- return false;
- }
- if (n0.type === TokenType.kResultId && n1.type === TokenType.kEqual)
- return true;
- return false;
- }
- extractOperand(ast, result_type, data) {
- let t = this.next();
- let name = undefined;
- let kind = undefined;
- let value = undefined;
- let params = [];
- // TODO(dsinclair): There are a bunch of missing types here. See
- // https://github.com/KhronosGroup/SPIRV-Tools/blob/master/source/text.cpp#L210
- //
- // LiteralSpecConstantOpInteger
- // PairLiteralIntegerIdRef
- // PairIdRefLiteralInteger
- // PairIdRefIdRef
- if (data.kind === "IdResult" || data.kind === "IdRef"
- || data.kind === "IdResultType" || data.kind === "IdScope"
- || data.kind === "IdMemorySemantics") {
- if (t.type !== TokenType.kResultId) {
- this.error_ = t.line + ": expected result id";
- return undefined;
- }
- name = t.data.name;
- kind = "result_id";
- value = t.data.val;
- } else if (data.kind === "LiteralString") {
- if (t.type !== TokenType.kStringLiteral) {
- this.error_ = t.line + ": expected string not found";
- return undefined;
- }
- name = t.data;
- kind = "string";
- value = t.data;
- } else if (data.kind === "LiteralInteger") {
- if (t.type !== TokenType.kIntegerLiteral) {
- this.error_ = t.line + ": expected integer not found";
- return undefined;
- }
- name = "" + t.data;
- kind = t.type;
- value = t.data;
- } else if (data.kind === "LiteralContextDependentNumber") {
- if (result_type === undefined) {
- this.error_ = t.line +
- ": missing result type for context dependent number";
- return undefined;
- }
- if (t.type !== TokenType.kIntegerLiteral
- && t.type !== TokenType.kFloatLiteral) {
- this.error_ = t.line + ": expected number not found";
- return undefined;
- }
- name = "" + t.data;
- kind = result_type.type;
- value = t.data;
- } else if (data.kind === "LiteralExtInstInteger") {
- if (t.type !== TokenType.kIdentifier) {
- this.error_ = t.line + ": expected instruction identifier";
- return undefined;
- }
- if (this.grammar_.ext[t.data] === undefined) {
- this.error_ = t.line + `: unable to find extended instruction (${t.data})`;
- return undefined;
- }
- name = t.data;
- kind = "integer";
- value = this.grammar_.ext[t.data];
- } else {
- let d = this.grammar_.operand_kinds[data.kind];
- if (d === undefined) {
- this.error_ = t.line + ": expected " + data.kind + " not found";
- return undefined;
- }
- let val = d.values[t.data]["value"];
- let names = [t.data];
- if (d.type === "BitEnum") {
- for(;;) {
- let tmp = this.peek(0);
- if (tmp.type !== TokenType.kPipe) {
- break;
- }
- this.next(); // skip pipe
- tmp = this.next();
- if (tmp.type !== TokenType.kIdentifier) {
- this.error_ = tmp.line() + ": expected identifier";
- return undefined;
- }
- val |= d.values[tmp.data]["value"];
- names.push(tmp.data);
- }
- }
- name = names.join("|");
- kind = d.type;
- value = val;
- for (const op_name of names) {
- if (d.values[op_name]['params'] === undefined) {
- continue;
- }
- for (const param of d.values[op_name]["params"]) {
- params.push(this.extractOperand(ast, result_type, { kind: param }));
- }
- }
- }
- return new AST.Operand(ast, name, kind, value, params);
- }
- }
|