q-sharp-ts
Version:
A parser for Q# language features, implemented in TypeScript.
1,194 lines (1,193 loc) • 64.3 kB
JavaScript
"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;