microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
iadavis/qdk_openqasm_parser

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/npm/qsharp/ux/circuit-vis/angleExpression.ts

133lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT license.
3
4// Normalizes "pi" (any case) to "π" and trims whitespace for
5// consistent storage and display.
6export function normalizeAngleExpression(expr: string): string {
7 return expr.trim().replace(/pi/gi, "π");
8}
9
10// Evaluate a simple arithmetic expression supporting numbers, + - * /, parentheses, and π.
11// Returns `undefined` for invalid inputs.
12export function evaluateAngleExpression(expr: string): number | undefined {
13 if (!expr) return undefined;
14 const src = normalizeAngleExpression(expr);
15 if (!src) return undefined;
16
17 // Tokenizer
18 type Tok = { type: "num" | "pi" | "op" | "lpar" | "rpar"; value?: string };
19 const toks: Tok[] = [];
20 let i = 0;
21 while (i < src.length) {
22 const ch = src[i];
23 if (ch === " " || ch === "\t" || ch === "\n") {
24 i++;
25 continue;
26 }
27 if (ch === "(" || ch === ")") {
28 toks.push({ type: ch === "(" ? "lpar" : "rpar" });
29 i++;
30 continue;
31 }
32 if (ch === "+" || ch === "-" || ch === "*" || ch === "/") {
33 toks.push({ type: "op", value: ch });
34 i++;
35 continue;
36 }
37 if (ch === "π") {
38 toks.push({ type: "pi" });
39 i++;
40 continue;
41 }
42 // number: digits with optional decimal part; no leading dot
43 if (ch === "." || /\d/.test(ch)) {
44 let j = i + 1;
45 while (j < src.length && /[0-9.]/.test(src[j])) j++;
46 const numStr = src.slice(i, j);
47 const valid = /^(?:\d+(?:\.\d*)?)$/.test(numStr);
48 if (!valid) return undefined;
49 toks.push({ type: "num", value: numStr });
50 i = j;
51 continue;
52 }
53 // Unknown character
54 return undefined;
55 }
56
57 // Recursive descent parser
58 let k = 0;
59 const peek = () => toks[k];
60 const consume = () => toks[k++];
61
62 const parseExpr = (): number | undefined => {
63 let lhs = parseTerm();
64 if (lhs === undefined) return undefined;
65 while (
66 peek() &&
67 peek().type === "op" &&
68 (peek().value === "+" || peek().value === "-")
69 ) {
70 const op = consume().value!;
71 const rhs = parseTerm();
72 if (rhs === undefined) return undefined;
73 lhs = op === "+" ? lhs + rhs : lhs - rhs;
74 }
75 return lhs;
76 };
77
78 const parseTerm = (): number | undefined => {
79 let lhs = parseFactor();
80 if (lhs === undefined) return undefined;
81 while (
82 peek() &&
83 peek().type === "op" &&
84 (peek().value === "*" || peek().value === "/")
85 ) {
86 const op = consume().value!;
87 const rhs = parseFactor();
88 if (rhs === undefined) return undefined;
89 lhs = op === "*" ? lhs * rhs : lhs / rhs;
90 }
91 return lhs;
92 };
93
94 const parseFactor = (): number | undefined => {
95 let sign = 1;
96 if (
97 peek() &&
98 peek().type === "op" &&
99 (peek().value === "+" || peek().value === "-")
100 ) {
101 sign = consume().value! === "-" ? -1 : 1;
102 }
103
104 const t = peek();
105 if (!t) return undefined;
106 if (t.type === "num") {
107 consume();
108 return sign * parseFloat(t.value!);
109 }
110 if (t.type === "pi") {
111 consume();
112 return sign * Math.PI;
113 }
114 if (t.type === "lpar") {
115 consume();
116 const v = parseExpr();
117 if (peek() && peek().type === "rpar") consume();
118 else return undefined;
119 if (v === undefined) return undefined;
120 return sign * v;
121 }
122 return undefined;
123 };
124
125 const result = parseExpr();
126 if (result === undefined || k !== toks.length || !isFinite(result))
127 return undefined;
128 return result;
129}
130
131export function isValidAngleExpression(expr: string): boolean {
132 return evaluateAngleExpression(expr) !== undefined;
133}
134