UNPKG

q-sharp-ts

Version:

A parser for Q# language features, implemented in TypeScript.

1,194 lines (1,193 loc) 64.3 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); var fs = require("fs"); var lexer_js_1 = require("./lexer.js"); var token_js_1 = require("./token.js"); var ast_js_1 = require("./ast.js"); var errors_js_1 = require("./errors.js"); var complex_js_1 = require("./complex.js"); /** Class representing a token parser. */ var Parser = /** @class */ (function () { /** * Creates a parser. * @param tokens - Tokens to parse. */ function Parser(tokens, isSubContext, filePath) { if (isSubContext === void 0) { isSubContext = false; } if (filePath === void 0) { filePath = ''; } this.tokens = tokens; this.libraries = []; this.symbols = []; this.qubits = []; this.variables = []; this.variableTypes = []; this.parameters = []; this.instances = {}; this.clauseParsers = []; this.funcParsers = {}; this.operationParsers = {}; this.structParsers = []; this.isSubContext = isSubContext; this.filePath = filePath; } /** * Updates the cursor after aprsing one clause. * @param i - The old cursor position. * @returns The new cursor position. */ Parser.prototype.traverseClause = function (i) { var openBrackets = -1; while ((!(this.tokens[i] == undefined) && !(this.matchNext(this.tokens.slice(i), [token_js_1.Token.EndOfFile])) && !(this.matchNext(this.tokens.slice(i), [token_js_1.Token.Rcurlbrac]))) || (openBrackets > 0)) { if (this.matchNext(this.tokens.slice(i), [token_js_1.Token.Lcurlbrac])) { openBrackets += 1; } else if (this.matchNext(this.tokens.slice(i), [token_js_1.Token.Rcurlbrac]) && openBrackets > 0) { openBrackets -= 1; } i++; } return i; }; /** * Figures out the new cursor position based on what has been parsed. * @param i - The current cursor position. * @returns The new cursor position. */ Parser.prototype.updateCursor = function (i) { if (this.tokens[i][0] == token_js_1.Token.Function || this.tokens[i][0] == token_js_1.Token.Operation) { i = this.traverseClause(i); } else if (this.tokens[i][0] == token_js_1.Token.Within) { i = this.traverseClause(i); i++; if (this.matchNext(this.tokens.slice(i), [token_js_1.Token.Apply])) { i++; i = this.traverseClause(i); } } else if (this.tokens[i][0] == token_js_1.Token.If) { i = this.traverseClause(i); i++; if (this.matchNext(this.tokens.slice(i), [token_js_1.Token.Else])) { i++; i = this.traverseClause(i); } // TODO: Repeat } else if ((this.tokens[i][0] == token_js_1.Token.For) || (this.tokens[i][0] == token_js_1.Token.While)) { i = this.traverseClause(i); } else if (this.tokens[i][0] == token_js_1.Token.Adjoint) { // NOP } else { while (!(this.tokens[i] == undefined) && !(this.matchNext(this.tokens.slice(i), [token_js_1.Token.EndOfFile])) && !(this.matchNext(this.tokens.slice(i), [token_js_1.Token.Newline]))) { i++; } } i++; return i; }; /** * Calling this method parses the code represented by the provided tokens. * @return The abstract syntax tree. */ Parser.prototype.parse = function () { var ast = []; var i = 0; while (i < (this.tokens.length - 1)) { var nodes = this.parseNode(this.tokens.slice(i)); ast = ast.concat(nodes ? nodes : []); i = this.updateCursor(i); } return ast; }; /** * Delegates the parsing of the next set of tokens to the appropriate method. * @param tokens - Remaining tokens to parse. * @param allowVariables - Whether encountered identifiers should be consider variable initializations or references. * @return A set of AST nodes. */ Parser.prototype.parseNode = function (tokens, allowVariables) { if (allowVariables === void 0) { allowVariables = true; } var token = tokens[0]; switch (token[0]) { // TODO: cases for AstTypes case token_js_1.Token.UnitType: return [new ast_js_1.UnitType()]; case token_js_1.Token.IntType: return [new ast_js_1.IntType()]; case token_js_1.Token.BigIntType: return [new ast_js_1.BigIntType()]; case token_js_1.Token.DoubleType: return [new ast_js_1.DoubleType()]; case token_js_1.Token.BoolType: return [new ast_js_1.BoolType()]; case token_js_1.Token.StringType: return [new ast_js_1.StringType()]; case token_js_1.Token.QubitType: return [new ast_js_1.QubitType()]; case token_js_1.Token.ResultType: return [new ast_js_1.ResultType()]; case token_js_1.Token.PauliType: return [new ast_js_1.PauliType()]; case token_js_1.Token.RangeType: return [new ast_js_1.RangeType()]; case token_js_1.Token.ArrayType: return [new ast_js_1.ArrayType()]; case token_js_1.Token.TupleType: return [new ast_js_1.TupleType()]; case token_js_1.Token.StructType: return [new ast_js_1.StringType()]; case token_js_1.Token.OperationType: return [new ast_js_1.OperationType()]; case token_js_1.Token.FunctionType: return [new ast_js_1.FunctionType()]; case token_js_1.Token.AND: return [new ast_js_1.And()]; case token_js_1.Token.CCNOT: return this.twoQubitGate(tokens.slice(1), ast_js_1.CCNOT); case token_js_1.Token.CNOT: return this.twoQubitGate(tokens.slice(1), ast_js_1.CNOT); case token_js_1.Token.Ex: return [new ast_js_1.Ex()]; case token_js_1.Token.H: return this.singleQubitGate(tokens.slice(1), ast_js_1.H); case token_js_1.Token.I: return this.singleQubitGate(tokens.slice(1), ast_js_1.I); case token_js_1.Token.M: return this.singleQubitGate(tokens.slice(1), ast_js_1.M); case token_js_1.Token.Measure: return this.singleQubitGate(tokens.slice(1), ast_js_1.M); // check this! case token_js_1.Token.R: return this.singleRotationGate(tokens.slice(1), ast_js_1.H); case token_js_1.Token.R1: return this.singleRotationGate(tokens.slice(1), ast_js_1.R1); case token_js_1.Token.R1Frac: return this.singleRotationGate(tokens.slice(1), ast_js_1.R1Frac); case token_js_1.Token.Reset: return this.singleQubitGate(tokens.slice(1), ast_js_1.Reset); case token_js_1.Token.ResetAll: return this.singleQubitGate(tokens.slice(1), ast_js_1.ResetAll); case token_js_1.Token.RFrac: return this.singleRotationGate(tokens.slice(1), ast_js_1.RFrac); case token_js_1.Token.Rx: return this.singleRotationGate(tokens.slice(1), ast_js_1.Rx); case token_js_1.Token.Rxx: return this.isingRotationGate(tokens.slice(1), ast_js_1.Rxx); case token_js_1.Token.Ry: return this.singleRotationGate(tokens.slice(1), ast_js_1.Ry); case token_js_1.Token.Ryy: return this.isingRotationGate(tokens.slice(1), ast_js_1.Ryy); case token_js_1.Token.Rz: return this.singleRotationGate(tokens.slice(1), ast_js_1.Rz); case token_js_1.Token.Rzz: return this.isingRotationGate(tokens.slice(1), ast_js_1.Rzz); case token_js_1.Token.S: return this.singleQubitGate(tokens.slice(1), ast_js_1.S); case token_js_1.Token.SWAP: return this.twoQubitGate(tokens.slice(1), ast_js_1.SWAP); case token_js_1.Token.T: return this.singleQubitGate(tokens.slice(1), ast_js_1.T); case token_js_1.Token.X: return this.singleQubitGate(tokens.slice(1), ast_js_1.X); case token_js_1.Token.Y: return this.singleQubitGate(tokens.slice(1), ast_js_1.Y); case token_js_1.Token.Z: return this.singleQubitGate(tokens.slice(1), ast_js_1.Z); case token_js_1.Token.ApplyUnitary: return this.unitary(tokens.slice(1)); case token_js_1.Token.True: return [new ast_js_1.Bool(true)]; case token_js_1.Token.False: return [new ast_js_1.Bool(false)]; case token_js_1.Token.Zero: return [new ast_js_1.Result(false)]; case token_js_1.Token.One: return [new ast_js_1.Result(true)]; case token_js_1.Token.PauliX: return [new ast_js_1.Pauli(ast_js_1.Paulis.PauliX)]; case token_js_1.Token.PauliY: return [new ast_js_1.Pauli(ast_js_1.Paulis.PauliY)]; case token_js_1.Token.PauliZ: return [new ast_js_1.Pauli(ast_js_1.Paulis.PauliZ)]; case token_js_1.Token.Let: return this.let(tokens); case token_js_1.Token.Mutable: return this.mutable(tokens); case token_js_1.Token.If: return this.if(tokens); case token_js_1.Token.For: return this.for(tokens.slice(1)); case token_js_1.Token.While: return this.while(tokens.slice(1)); case token_js_1.Token.Repeat: return this.repeat(tokens.slice(1)); case token_js_1.Token.Return: return this.return(tokens); case token_js_1.Token.Fail: return this.fail(tokens.slice(1)); case token_js_1.Token.Identifier: return this.identifier(tokens, allowVariables); case token_js_1.Token.Function: return this.function(tokens.slice(1)); case token_js_1.Token.Use: return this.use(tokens.slice(1)); case token_js_1.Token.Operation: return this.operation(tokens.slice(1)); case token_js_1.Token.Lsqbrac: return this.array(tokens.slice(1)); case token_js_1.Token.StructType: return this.struct(tokens.slice(2), tokens[1][1]); case token_js_1.Token.Import: return this.import(tokens.slice(1)); case token_js_1.Token.Int: return [new ast_js_1.Int(Number(tokens[0][1]))]; case token_js_1.Token.BigInt: return [new ast_js_1.BigInt(Number(tokens[0][1]))]; case token_js_1.Token.Double: return [new ast_js_1.Double(Number(tokens[0][1]))]; case token_js_1.Token.Or: return [new ast_js_1.Or()]; case token_js_1.Token.And: return [new ast_js_1.And()]; case token_js_1.Token.And: return [new ast_js_1.Not()]; case token_js_1.Token.Less: return [new ast_js_1.Less()]; case token_js_1.Token.More: return [new ast_js_1.More()]; case token_js_1.Token.Left: return [new ast_js_1.Left()]; case token_js_1.Token.Right: return [new ast_js_1.Right()]; case token_js_1.Token.Unwrap: return [new ast_js_1.Unwrap()]; case token_js_1.Token.BitwiseAnd: return [new ast_js_1.BitwiseAnd()]; case token_js_1.Token.BitwiseOr: return [new ast_js_1.BitwiseOr()]; case token_js_1.Token.BitwiseNot: return [new ast_js_1.BitwiseNot()]; case token_js_1.Token.BitwiseXor: return [new ast_js_1.BitwiseXor()]; case token_js_1.Token.Mod: return [new ast_js_1.Mod()]; case token_js_1.Token.Dummy: return [new ast_js_1.Dummy()]; case token_js_1.Token.Exp: return [new ast_js_1.Exp()]; case token_js_1.Token.Eq: return [new ast_js_1.Eq()]; case token_js_1.Token.Neq: return [new ast_js_1.Neq()]; case token_js_1.Token.Divide: return [new ast_js_1.Divide()]; case token_js_1.Token.Eq: return [new ast_js_1.Eq()]; case token_js_1.Token.Leq: return [new ast_js_1.Leq()]; case token_js_1.Token.Geq: return [new ast_js_1.Geq()]; case token_js_1.Token.Neq: return [new ast_js_1.Neq()]; case token_js_1.Token.Peq: return [new ast_js_1.Peq()]; case token_js_1.Token.Meq: return [new ast_js_1.Meq()]; case token_js_1.Token.Times: return [new ast_js_1.Times()]; case token_js_1.Token.Plus: return [new ast_js_1.Plus()]; case token_js_1.Token.Minus: return [new ast_js_1.Minus()]; case token_js_1.Token.String: return [new ast_js_1.Str(token[1])]; case token_js_1.Token.Within: return this.conjugation(tokens.slice(0)); case token_js_1.Token.Comment: return [new ast_js_1.Comment(token[1].toString())]; case token_js_1.Token.Controlled: if (tokens[1][0] == token_js_1.Token.X) { return this.twoQubitGate(tokens.slice(2), ast_js_1.CNOT); // TODO: support other controlled gates } else if (tokens[1][0] == token_js_1.Token.Z) { return this.twoQubitGate(tokens.slice(2), ast_js_1.CZ); } else { return this.modifiers(tokens); } case token_js_1.Token.Adjoint: return this.modifiers(tokens); } }; /** * Parses a single qubit gate application. * @param tokens - Tokens to parse. * @return A parsed gate application. */ Parser.prototype.singleQubitGate = function (tokens, gateClass) { var _a; var qubit; var consumed; if (this.matchNext(tokens, [token_js_1.Token.Lbrac, token_js_1.Token.Identifier])) { tokens = tokens.slice(1); _a = this.parseSymbol(tokens), qubit = _a[0], consumed = _a[1]; } else { throw errors_js_1.BadApplicationError; } return [new gateClass(qubit)]; }; /** * Parses a two qubit gate application. * @param tokens - Tokens to parse. * @return A parsed gate application. */ Parser.prototype.twoQubitGate = function (tokens, gateClass) { var _a, _b; var qubit; var secondQubit; var consumed; if (this.matchNext(tokens, [token_js_1.Token.Lbrac, token_js_1.Token.Identifier])) { tokens = tokens.slice(1); _a = this.parseSymbol(tokens), qubit = _a[0], consumed = _a[1]; tokens = tokens.slice(consumed); if (this.matchNext(tokens, [token_js_1.Token.Comma, token_js_1.Token.Identifier])) { tokens = tokens.slice(1); _b = this.parseSymbol(tokens), secondQubit = _b[0], consumed = _b[1]; } else { throw errors_js_1.BadApplicationError; } } return [new gateClass(qubit, secondQubit)]; }; /** * Parses a single qubit rotation. * @param tokens - Tokens to parse. * @return A parsed gate application. */ Parser.prototype.singleRotationGate = function (tokens, gateClass) { var _a; var qubit; var rads; var consumed; if (this.matchNext(tokens, [token_js_1.Token.Lbrac, token_js_1.Token.Double])) { tokens = tokens.slice(1); rads = new ast_js_1.Double(tokens[0][1]); tokens = tokens.slice(1); if (this.matchNext(tokens, [token_js_1.Token.Comma, token_js_1.Token.Identifier])) { tokens = tokens.slice(1); _a = this.parseSymbol(tokens), qubit = _a[0], consumed = _a[1]; } else { throw errors_js_1.BadApplicationError; } } return [new gateClass(rads, qubit)]; }; /** * Parses an arbitrary unitary. * @param tokens - Tokens to parse. * @return A parsed unitary application. */ Parser.prototype.unitary = function (tokens) { var row = []; while (!(this.matchNext(tokens, [token_js_1.Token.Rsqbrac]))) { if (!(this.matchNext(tokens, [token_js_1.Token.Lsqbrac]))) { var complex = (0, complex_js_1.parseComplex)(tokens[1].toString()); row.push(complex); while (!(this.matchNext(tokens, [token_js_1.Token.Comma])) && !(this.matchNext(tokens, [token_js_1.Token.Rsqbrac]))) { tokens = tokens.slice(1); } } else { row.push(this.unitary(tokens.slice(1))); } } return [new complex_js_1.ComplexMatrix(row)]; }; /** * Parses an two qubit rotation. * @param tokens - Tokens to parse. * @return A parsed gate application. */ Parser.prototype.isingRotationGate = function (tokens, gateClass) { var _a, _b; var qubit; var rads; var secondQubit; var consumed; if (this.matchNext(tokens, [token_js_1.Token.Lbrac, token_js_1.Token.Double])) { tokens = tokens.slice(1); rads = new ast_js_1.Double(tokens[0][1]); tokens = tokens.slice(1); if (this.matchNext(tokens, [token_js_1.Token.Comma, token_js_1.Token.Identifier])) { tokens = tokens.slice(1); _a = this.parseSymbol(tokens), qubit = _a[0], consumed = _a[1]; tokens = tokens.slice(consumed); if (this.matchNext(tokens, [token_js_1.Token.Comma, token_js_1.Token.Identifier])) { tokens = tokens.slice(1); _b = this.parseSymbol(tokens), secondQubit = _b[0], consumed = _b[1]; return [new gateClass(rads, qubit, secondQubit)]; } else { throw errors_js_1.BadApplicationError; } } else { throw errors_js_1.BadApplicationError; } } }; /** * Parses a logical and mathematical expression. * @param tokens - Expression tokens to parse. * @return A parsed expression. */ Parser.prototype.parseExpression = function (tokens) { var elements = []; var nodes; var nonParam = false; var consumed = 0; while (tokens.length > 0 && !nonParam) { nodes = this.parseNode(tokens, true); if (nodes !== undefined) { for (var i in nodes) { if (nodes[i] instanceof ast_js_1.Parameter) { elements.push(nodes[i]); } else { nonParam = true; break; } } } else { nonParam = true; } for (var n in nodes) { if (nodes[n] instanceof ast_js_1.ApplyOperator) { var i = 0; var openBrackets = -1; while ((tokens[i] != undefined && !this.matchNext(tokens.slice(i), [token_js_1.Token.Rbrac]) && !this.matchNext(tokens.slice(i), [token_js_1.Token.EndOfFile])) || (openBrackets > 0)) { if (this.matchNext(tokens.slice(i), [token_js_1.Token.Lbrac])) { openBrackets += 1; } else if (this.matchNext(tokens.slice(i), [token_js_1.Token.Rbrac]) && openBrackets > 0) { openBrackets -= 1; } i++; } tokens = tokens.slice(i + 1); consumed += i + 1; } else { tokens = tokens.slice(1); consumed += 1; } } } return [new ast_js_1.Expression(elements), consumed]; }; /** * Parses a conjugation. * @param tokens - Tokens to parse. * @return A parsed conjugation. */ Parser.prototype.conjugation = function (tokens) { var withinTokens = []; var applyTokens = []; if (this.matchNext(tokens, [token_js_1.Token.Within, token_js_1.Token.Lcurlbrac])) { tokens = tokens.slice(1); var i = 0; var openBrackets = -1; while ((tokens[i] != undefined && !this.matchNext(tokens.slice(i), [token_js_1.Token.Rcurlbrac])) || (openBrackets > 0)) { if (this.matchNext(tokens.slice(i), [token_js_1.Token.Lcurlbrac])) { openBrackets += 1; } else if (this.matchNext(tokens.slice(i), [token_js_1.Token.Rcurlbrac]) && openBrackets > 0) { openBrackets -= 1; } withinTokens.push(tokens[i]); i++; } i++; tokens = tokens.slice(i); var withinParser = this.childParser(withinTokens); var withinCode = withinParser.parse(); if (this.matchNext(tokens, [token_js_1.Token.Apply, token_js_1.Token.Lcurlbrac])) { tokens = tokens.slice(1); var j = 0; openBrackets = -1; while ((tokens[j] != undefined && !this.matchNext(tokens.slice(j), [token_js_1.Token.Rcurlbrac])) || (openBrackets > 0)) { if (this.matchNext(tokens.slice(j), [token_js_1.Token.Lcurlbrac])) { openBrackets += 1; } else if (this.matchNext(tokens.slice(j), [token_js_1.Token.Rcurlbrac]) && openBrackets > 0) { openBrackets -= 1; } applyTokens.push(tokens[j]); j++; } var applyParser = this.childParser(applyTokens); var applyCode = applyParser.parse(); return [new ast_js_1.Conjugation(withinCode, applyCode)]; } else { throw errors_js_1.BadConjugationError; } } }; /** * Whether a variable is known. * @param name - The string name of the variable. * @return Whether it corresponds to a variable in this.variables. */ Parser.prototype.hasVariable = function (name) { var seen = false; for (var _i = 0, _a = this.variables; _i < _a.length; _i++) { var variable = _a[_i]; if (variable.name == name) { seen = true; break; } } return seen; }; /** * Whether a qubit is known. * @param name - The string name of the qubit. * @return Whether it corresponds to a qubit in this.qubits. */ Parser.prototype.hasQubit = function (name) { var seen = false; for (var _i = 0, _a = this.qubits; _i < _a.length; _i++) { var q = _a[_i]; if (q.name == name) { seen = true; break; } } return seen; }; /** * Parses a return. * @param tokens - Tokens to parse. * @return A parsed return statement. */ Parser.prototype.return = function (tokens) { return [new ast_js_1.Return(this.parseExpression(tokens.slice(1))[0])]; }; /** * Parses an fail statement. * @param tokens - Tokens to parse. * @return A parsed fail statement. */ Parser.prototype.fail = function (tokens) { return [new ast_js_1.Fail(new ast_js_1.Str(tokens[0]))]; }; /** * Parses an operation. * @param tokens - Tokens to parse. * @return A parsed operation. */ Parser.prototype.operation = function (tokens) { var name; var operationTokens = []; var operationParams = []; var modifierTokens = []; if (this.matchNext(tokens, [token_js_1.Token.Identifier, token_js_1.Token.Lbrac])) { name = tokens[0][1].toString(); tokens = tokens.slice(2); var i = 0; operationParams = this.matchParamList(tokens); while (tokens[i] != undefined && !(this.matchNext(tokens.slice(i), [token_js_1.Token.Rbrac]))) { i++; } if (this.matchNext(tokens.slice(i + 1), [token_js_1.Token.Unit, token_js_1.Token.Is])) { modifierTokens = this.modifiers(tokens.slice(i + 2)).map(function (mod) { if (mod instanceof ast_js_1.Adjoint) { return ast_js_1.Modifier.Adjoint; } else if (mod instanceof ast_js_1.Controlled) { return ast_js_1.Modifier.Controlled; } }); } var j = i + 2 + modifierTokens.length; var ty = new ast_js_1.UnitType(); var k = 0; if ((0, token_js_1.inverseTypeLookup)(tokens.slice(j)[0][0])) { ty = this.parseNode(tokens.slice(j))[0]; while (tokens[j + k] != undefined && !(this.matchNext(tokens.slice(j + k), [token_js_1.Token.Lcurlbrac]))) { k++; } } j += k + 1; var openBrackets = 0; while ((tokens[j] != undefined && !this.matchNext(tokens.slice(j), [token_js_1.Token.Rcurlbrac])) || (openBrackets > 0)) { if (this.matchNext(tokens.slice(j), [token_js_1.Token.Lcurlbrac])) { openBrackets += 1; } else if (this.matchNext(tokens.slice(j), [token_js_1.Token.Rcurlbrac]) && openBrackets > 0) { openBrackets -= 1; } operationTokens.push(tokens[j]); j++; } var operationParser = this.childParser(operationTokens); var operationCode = operationParser.parse(); this.libraries.push(name); this.operationParsers[name] = operationParser; return [new ast_js_1.Operation(name, operationCode, operationParams, modifierTokens, ty)]; } else { throw errors_js_1.BadOperationNameError; } }; /** * Parses a list of modifiers. * @param tokens - Modifiers to parse. * @return Parsed modifiers. */ Parser.prototype.modifiers = function (tokens) { var modifiers = []; var i = 0; while (tokens[i] != undefined && (this.matchNext(tokens.slice(i), [token_js_1.Token.Plus]) || this.matchNext(tokens.slice(i), [token_js_1.Token.Adjoint]) || this.matchNext(tokens.slice(i), [token_js_1.Token.Controlled]))) { if (this.matchNext(tokens.slice(i), [token_js_1.Token.Adjoint])) { modifiers.push(new ast_js_1.Adjoint()); } else if (this.matchNext(tokens.slice(i), [token_js_1.Token.Controlled])) { modifiers.push(new ast_js_1.Controlled()); } i++; } return modifiers; }; /** * Parses a function. * @param tokens - Function tokens to parse. * @return A parsed function. */ Parser.prototype.function = function (tokens) { var name; var functionTokens = []; var functionParams = []; if (this.matchNext(tokens, [token_js_1.Token.Identifier, token_js_1.Token.Lbrac])) { name = tokens[0][1].toString(); tokens = tokens.slice(2); var i = 0; functionParams = this.matchParamList(tokens); while (tokens[i] != undefined && !(this.matchNext(tokens.slice(i), [token_js_1.Token.Rbrac]))) { i++; } var ty = new ast_js_1.UnitType(); var k = 0; if ((0, token_js_1.inverseTypeLookup)(tokens.slice(2)[0][0])) { ty = this.parseNode(tokens.slice(2))[0]; while (tokens[2 + k] != undefined && !(this.matchNext(tokens.slice(2 + k), [token_js_1.Token.Lcurlbrac]))) { k++; } } var j = 2 + k; var openBrackets = -1; while ((tokens[j] != undefined && !this.matchNext(tokens.slice(j), [token_js_1.Token.Rcurlbrac])) || (openBrackets > 0)) { if (this.matchNext(tokens.slice(j), [token_js_1.Token.Lcurlbrac])) { openBrackets += 1; } else if (this.matchNext(tokens.slice(j), [token_js_1.Token.Rcurlbrac]) && openBrackets > 0) { openBrackets -= 1; } functionTokens.push(tokens[j]); j++; } var functionParser = this.childParser(functionTokens); var functionCode = functionParser.parse(); this.libraries.push(name); this.funcParsers[name] = functionParser; return [new ast_js_1.Function(name, functionCode, functionParams, ty)]; } else { throw errors_js_1.BadFunctionNameError; } }; /** * Parses an Identifier or Ancilliary symbol. * @param tokens - Symbol tokens to parse. * @return A parsed symbol. */ Parser.prototype.parseSymbol = function (tokens) { var name; var consumed = 0; if (this.matchNext(tokens, [token_js_1.Token.Identifier])) { name = tokens[0][1].toString(); } tokens = tokens.slice(1); consumed += 1; if (this.matchNext(tokens, [token_js_1.Token.Lsqbrac])) { tokens = tokens.slice(1); consumed += 1; if (this.matchNext(tokens, [token_js_1.Token.Identifier, token_js_1.Token.Rsqbrac])) { var index = new ast_js_1.Variable(tokens[0][1].toString()); if (this.hasVariable(tokens[0][1].toString())) { return [new ast_js_1.GetParam(name, new ast_js_1.Expression([index])), consumed]; } else { throw errors_js_1.BadIndexError; } } else if (this.matchNext(tokens, [token_js_1.Token.Int, token_js_1.Token.Rsqbrac])) { var index = new ast_js_1.Int(tokens[0][1]); tokens = tokens.slice(1); return [new ast_js_1.GetParam(name, new ast_js_1.Expression([index])), 4]; } else { var _a = this.parseExpression(tokens), expr = _a[0], exprConsumed = _a[1]; if (this.matchNext(tokens.slice(exprConsumed), [token_js_1.Token.Rsqbrac])) { return [new ast_js_1.GetParam(name, expr), consumed]; } else { var _b = this.range(tokens), range = _b[0], rngConsumed = _b[1]; return [new ast_js_1.GetParam(name, range), consumed + rngConsumed]; } } } else if (this.matchNext(tokens, [token_js_1.Token.Period, token_js_1.Token.Identifier])) { var inst = name; return [new ast_js_1.GetParam(inst, new ast_js_1.Expression([this.parseSymbol(tokens.slice(1))[0]])), 3]; } else if (this.hasQubit(name)) { return [new ast_js_1.Qubit(name), 1]; } else if (this.hasVariable(name)) { return [new ast_js_1.Variable(name), 1]; } else { return [new ast_js_1.Id(name), 1]; } }; /** * Parses and identifier. * @param tokens - Tokens to parse. * @return A parsed identifier. */ Parser.prototype.identifier = function (tokens, allowVariables) { if (!allowVariables) { this.symbols.push(tokens[0][1].toString()); return [this.parseSymbol(tokens)[0]]; } else if (allowVariables) { if ((tokens.length > 1) && (tokens[1][0] == token_js_1.Token.Lbrac) && (this.libraries.length > 0)) { // TODO: build a robust import system return [this.application(tokens)]; } if (this.symbols.includes(tokens[0][1].toString()) || this.libraries.length > 0) { // TODO: build a robust import system return [this.parseSymbol(tokens)[0]]; } } throw errors_js_1.BadIdentifierError; }; /** * Parses the application of a non-intrinsic operator or function. * @param tokens - Tokens to parse. * @return A parsed operator or function application. */ /** * Parses an application of one of hte allowed operators. * @param tokens - Remaining tokens to parse. * @return An AST node representing the operator application. */ Parser.prototype.application = function (tokens) { var name; var registers; var params; if (this.matchNext(tokens, [token_js_1.Token.Identifier, token_js_1.Token.Lbrac])) { name = tokens[0][1].toString(); tokens = tokens.slice(2); params = this.matchParamList(tokens); } else { throw errors_js_1.BadApplicationError; } var j = 0; var openBrackets = -1; while ((tokens[j] != undefined && !this.matchNext(tokens.slice(j), [token_js_1.Token.Rbrac])) || (openBrackets > 0)) { if (this.matchNext(tokens.slice(j), [token_js_1.Token.Lbrac])) { openBrackets += 1; } else if (this.matchNext(tokens.slice(j), [token_js_1.Token.Rbrac]) && openBrackets > 0) { openBrackets -= 1; } j++; } tokens = tokens.slice(j); if (this.matchNext(tokens, [token_js_1.Token.Lbrac])) { tokens = tokens.slice(1); registers = this.matchIndexList(tokens); } return new ast_js_1.ApplyOperator(name, params, registers); }; /** * Parses a list of registers. * @param tokens - Tokens to parse. * @return An array of AST nodes representing the registers. */ Parser.prototype.matchIndexList = function (tokens) { var args = []; var next; var id; var j = 0; while (j < tokens.length && !this.matchNext(tokens.slice(j), [token_js_1.Token.Newline]) && !this.matchNext(tokens.slice(j), [token_js_1.Token.Rbrac])) { id = tokens[j][1].toString(); var index = this.matchIndex(tokens.slice(j + 1)); next = new ast_js_1.Qubit(id, new ast_js_1.Int(1)); args.push(next); if (index != undefined) { j += 4; } else { j++; } if (this.matchNext(tokens.slice(j), [token_js_1.Token.Comma])) { j++; } } return args; }; /** * Parses a register index. * @param tokens - Tokens to parse. * @return The index’s value. */ Parser.prototype.matchIndex = function (tokens) { var index; if (this.matchNext(tokens, [token_js_1.Token.Lsqbrac])) { tokens = tokens.slice(1); if (this.matchNext(tokens, [token_js_1.Token.Int])) { index = Number(tokens[0][1]); tokens = tokens.slice(1); } else { throw errors_js_1.BadArgumentError; } if (this.matchNext(tokens, [token_js_1.Token.Rsqbrac])) { return index; } else { throw errors_js_1.BadArgumentError; } } }; /** * Parses a list of parameter values. * @param tokens - Tokens to parse. * @return An array of AST nodes representing the parameter values. */ Parser.prototype.matchParamList = function (tokens) { var args = []; var i = 0; var j = 0; var openBrackets = 0; args[0] = []; while (tokens[j] != undefined && (openBrackets == 0 && !this.matchNext(tokens.slice(j), [token_js_1.Token.Rbrac]))) { while (!this.matchNext(tokens.slice(j), [token_js_1.Token.Comma]) && tokens[j] != undefined && !this.matchNext(tokens.slice(j), [token_js_1.Token.Rbrac])) { if (this.matchNext(tokens.slice(j), [token_js_1.Token.Lbrac])) { openBrackets += 1; j++; } if ((0, token_js_1.notParam)(tokens[j][0])) { throw errors_js_1.BadParameterError; } var next = this.parseExpression(tokens.slice(j))[0]; if (next != undefined) { args[i].push(next); } while (!(this.matchNext(tokens.slice(j), [token_js_1.Token.Rbrac])) && !this.matchNext(tokens.slice(j), [token_js_1.Token.Comma])) { j++; } } if (this.matchNext(tokens.slice(j), [token_js_1.Token.Rbrac])) { if (openBrackets != 0) { openBrackets -= 1; } else { break; } } i++; j++; args[i] = []; } return args.filter(function (elem) { return (elem.length > 0); }); }; /** * Parses a parameter value. * @param tokens - Tokens to parse. * @return An AST node representing the parameter value. */ Parser.prototype.matchParam = function (tokens) { var param; var paramTokens = []; if (!((0, token_js_1.notParam)(tokens[0][0]))) { var i = 0; while (tokens[i] != undefined && !this.matchNext(tokens.slice(i), [token_js_1.Token.Newline]) && !this.matchNext(tokens.slice(i), [token_js_1.Token.Rbrac]) && !this.matchNext(tokens.slice(i), [token_js_1.Token.Comma])) { paramTokens.push(tokens[i]); i++; } param = this.parseNode(paramTokens, true)[0]; } else { throw errors_js_1.BadParameterError; } return param; }; /** * Parses a struct. * @param tokens - Tokens to parse. * @return A parsed struct. */ Parser.prototype.struct = function (tokens, name) { var _a, _b; var names = []; var vals = []; var _c = ['', 0], id = _c[0], consumed = _c[1]; while (!(this.matchNext(tokens, [token_js_1.Token.Rcurlbrac]))) { if (this.matchNext(tokens, [token_js_1.Token.Identifier, token_js_1.Token.Colon])) { if (Object.keys(this.structParsers).includes(name)) { _a = this.structParsers[name].parseSymbol(tokens), id = _a[0], consumed = _a[1]; tokens = tokens.slice(consumed); } else { this.structParsers[name] = new StructParser(tokens); _b = this.structParsers[name].parseSymbol(tokens), id = _b[0], consumed = _b[1]; tokens = tokens.slice(consumed); } var val = this.parseNode(tokens.slice(1)); names.push(new ast_js_1.Id(id)); vals.push(val); tokens = tokens.slice(1); if (this.matchNext(tokens, [token_js_1.Token.Comma])) { tokens = tokens.slice(1); } } else { throw errors_js_1.BadStructError; } } return [new ast_js_1.Struct(vals, names)]; }; /** * Parses an array. * @param tokens - Tokens to parse. * @return A parsed array. */ Parser.prototype.array = function (tokens) { var vals = []; while (!(this.matchNext(tokens, [token_js_1.Token.Rsqbrac]))) { // TODO: support arbitrary expressions var _a = this.parseExpression(tokens), param = _a[0], consumed = _a[1]; vals.push(param); while (!(this.matchNext(tokens, [token_js_1.Token.Comma])) && !(this.matchNext(tokens, [token_js_1.Token.Rsqbrac]))) { tokens = tokens.slice(consumed); } if (this.matchNext(tokens, [token_js_1.Token.Comma])) { tokens = tokens.slice(1); } } return [new ast_js_1.Arr(vals, vals.length)]; }; /** * Parses qubit usage. * @param tokens - Qubit usage tokens to parse. * @return A parsed qubit usage. */ Parser.prototype.use = function (tokens) { // use qubit = Qubit(); style if (this.matchNext(tokens, [token_js_1.Token.Identifier, token_js_1.Token.Eq])) { var id = this.identifier(tokens, false)[0]; if (id instanceof ast_js_1.Id) { var qubit = new ast_js_1.Qubit(id.id); tokens = tokens.slice(2); if (this.matchNext(tokens, [token_js_1.Token.QubitType, token_js_1.Token.Lbrac, token_js_1.Token.Rbrac])) { this.qubits.push(qubit); return [new ast_js_1.Use(new ast_js_1.Str(id.id), qubit)]; } else if (this.matchNext(tokens, [token_js_1.Token.QubitType, token_js_1.Token.Lsqbrac])) { tokens = tokens.slice(2); var size = this.matchParam([tokens[0]]); qubit.length = size; this.qubits.push(qubit); return [new ast_js_1.Use(new ast_js_1.Str(id.id), qubit)]; } else { throw errors_js_1.BadUseError; } } else { throw errors_js_1.BadUseError; } // use (aux, register) = (Qubit(), Qubit[5]); style } else if (this.matchNext(tokens, [token_js_1.Token.Lbrac])) { var names = []; while (!(this.matchNext(tokens, [token_js_1.Token.Rbrac]))) { var id = this.identifier(tokens, true); if (id instanceof ast_js_1.Id) { names.push(id); } else { throw errors_js_1.BadUseError; } while (!(this.matchNext(tokens, [token_js_1.Token.Comma])) && !(this.matchNext(tokens, [token_js_1.Token.Rbrac]))) { tokens = tokens.slice(1); } if (this.matchNext(tokens, [token_js_1.Token.Comma])) { tokens = tokens.slice(1); } } if (this.matchNext(tokens, [token_js_1.Token.Eq])) { tokens = tokens.slice(1); } else { throw errors_js_1.BadUseError; } if (this.matchNext(tokens, [token_js_1.Token.Lbrac])) { var vals = []; while (!(this.matchNext(tokens, [token_js_1.Token.Rbrac]))) { // Qubit() if (this.matchNext(tokens, [token_js_1.Token.QubitType, token_js_1.Token.Lbrac, token_js_1.Token.Rbrac])) { vals.push(new ast_js_1.Qubit(names[vals.length].id)); // Qubit[5] } else if (this.matchNext(tokens, [token_js_1.Token.QubitType, token_js_1.Token.Lsqbrac])) { var len = new ast_js_1.Int(Number(tokens.slice(2)[1])); var q = new ast_js_1.Qubit(names[vals.length].id, len); vals.push(q); this.qubits.push(q); } else { throw errors_js_1.BadUseError; } while (!(this.matchNext(tokens, [token_js_1.Token.Comma])) && !(this.matchNext(tokens, [token_js_1.Token.Rbrac]))) { tokens = tokens.slice(1); } if (this.matchNext(tokens, [token_js_1.Token.Comma])) { tokens = tokens.slice(1); } } var res = []; for (var i = 0; i < names.length; i++) { res.push(new ast_js_1.Use(new ast_js_1.Str(names[i].id), vals[i])); } return res; } else { throw errors_js_1.BadUseError; } } }; /** * Parses qubit borrow. * @param tokens - Qubit borrow tokens to parse. * @return A parsed qubit borrow. */ Parser.prototype.borrow = function (tokens) { var uses = this.use(tokens); return uses.map(function (use) { return new ast_js_1.Borrow(use.name, use.qubits); }); }; /** * Parses an import. * @param tokens - Import tokens to parse. * @return A parsed import. */ Parser.prototype.import = function (tokens) { var token = tokens[0]; var name = this.parseSymbol(tokens); if (token[0] == token_js_1.Token.Identifier) { this.libraries.push(name[0].repr); if (token[1] != 'Std' && token[1] != 'Microsoft') { var q_sharp = fs.readFileSync(this.filePath + name.slice(1, name.length - 1) + '.qs', 'utf8'); var lexer = new lexer_js_1.default(q_sharp, 0); var tokens_1 = lexer.lex(); var parser = new Parser(tokens_1, true, this.filePath + name[0].repr.slice(1, name[0].repr.length - 1).split('/').slice(0, name[0].repr.slice(1, name[0].repr.length - 1).split('/').length - 1).join('/')); parser.symbols = this.symbols; parser.instances = this.instances; parser.libraries = this.libraries; parser.funcParsers = this.funcParsers; this.funcParsers[name[0].repr] = parser; return [new ast_js_1.Import(name[0])]; } else { return [new ast_js_1.Import(name[0])]; } // TODO: parse until semicolon } else { throw errors_js_1.BadImportError; } }; /** * Creates a new parser and copies the current parser's context to it. * @param tokens - Symbol tokens for the child parser to parse. * @return A new parser. */ Parser.prototype.childParser = function (tokens) { var newParser = new Parser(tokens, true, this.filePath); newParser.symbols = this.symbols; newParser.instances = this.instances; newParser.libraries = this.libraries; newParser.funcParsers = this.funcParsers; return newParser; }; /** * Parses an assignment. * @param tokens - Tokens to parse. * @return An array of AST nodes representing the assignment. */ Parser.prototype.let = function (tokens) { var name; var exprTokens = []; if (this.matchNext(tokens, [token_js_1.Token.Let, token_js_1.Token.Identifier, token_js_1.Token.Eq])) { tokens = tokens.slice(1); name = tokens[0][1].toString(); tokens = tokens.slice(2); var i = 0; while (tokens[i] != undefined && !this.matchNext(tokens.slice(i), [token_js_1.Token.Newline])) { exprTokens.push(tokens[i]); i++; } var exp = this.parseExpression(exprTokens)[0]; this.variables.push(new ast_js_1.Variable(name)); return [new ast_js_1.Let(exp, new ast_js_1.Variable(name))]; } else { throw errors_js_1.BadBindingError; } }; /** * Parses a mutable assignment. * @param tokens - Tokens to parse. * @return An array of AST nodes representing the assignment. */ Parser.prototype.mutable = function (tokens) { var name;