From d7796af623ed3a1d4ce5ed13df282442699fbbf5 Mon Sep 17 00:00:00 2001 From: Van Nguyen <36019388+nguyentvan7@users.noreply.github.com> Date: Tue, 2 Jan 2024 11:47:28 -0700 Subject: [PATCH 1/6] Add debug calc --- libs/sr-formula/src/data/util/read.ts | 34 ++++++ libs/sr-formula/src/debug.ts | 148 +++++++++++++++++++++++++- libs/sr-formula/src/index.ts | 1 + 3 files changed, 178 insertions(+), 5 deletions(-) diff --git a/libs/sr-formula/src/data/util/read.ts b/libs/sr-formula/src/data/util/read.ts index b93c13284a..21d7b0ce20 100644 --- a/libs/sr-formula/src/data/util/read.ts +++ b/libs/sr-formula/src/data/util/read.ts @@ -108,6 +108,40 @@ export function tagVal(cat: keyof Tag): TagValRead { return baseTagVal(cat) } +export function tagStr(tag: Tag, ex?: any): string { + const { name, member, dst, et, src, q, qt, ...remaining } = tag + + if (Object.keys(remaining).length) console.error(remaining) + + let result = '{ ', + includedRequired = false, + includedBar = false + function required(str: string | undefined | null) { + if (!str) return + result += str + ' ' + includedRequired = true + } + function optional(str: string | undefined | null) { + if (!str) return + if (includedRequired && !includedBar) { + includedBar = true + result += '| ' + } + result += str + ' ' + } + required(name && `#${name}`) + required(member) + required(dst && `(${dst})`) + required(src) + required(et) + if (qt && q) required(`${qt}.${q}`) + else if (qt) required(`${qt}.`) + else if (q) required(`.${q}`) + + required(ex && `[${ex}]`) + return result + '}' +} + export const reader = new Read({}, undefined) export const usedNames = new Set() export const usedQ = new Set('_') diff --git a/libs/sr-formula/src/debug.ts b/libs/sr-formula/src/debug.ts index 1202b21098..9a88c8ed4c 100644 --- a/libs/sr-formula/src/debug.ts +++ b/libs/sr-formula/src/debug.ts @@ -1,11 +1,21 @@ import type { AnyNode, + CalcResult, ReRead, TagMapSubsetCache, } from '@genshin-optimizer/pando' -import { TagMapExactValues, traverse } from '@genshin-optimizer/pando' -import type { Read, Tag } from './data/util' -import type { Calculator } from './calculator' +import { + Calculator as BaseCalculator, + TagMapExactValues, + TagMapKeys, + traverse, +} from '@genshin-optimizer/pando' +import { type Calculator } from './calculator' +import { keys } from './data' +import { tagStr, type Read, type Tag, type TagMapNodeEntry } from './data/util' + +const tagKeys = new TagMapKeys(keys) +export const debugKey = Symbol('tagSource') export function dependencyString(read: Read, calc: Calculator) { const str = listDependencies(read.tag, calc).map(({ tag, read, reread }) => { @@ -30,8 +40,7 @@ export function listDependencies( const result: { tag: Tag; read: Tag[]; reread: Tag[] }[] = [], stack: Tag[] = [] /** Stack depth when first encountered the tag, or 0 if already visited */ - const tagKeys = calc.keys - const openDepth = new TagMapExactValues(tagKeys.tagLen, {}) + const openDepth = new TagMapExactValues(keys.tagLen, {}) function internal(cache: TagMapSubsetCache) { const tag = cache.tag, @@ -81,3 +90,132 @@ export function listDependencies( internal(calc.nodes.cache(calc.keys).with(tag)) return result } + +export function printEntry({ tag, value }: TagMapNodeEntry): string { + function printNode(node: AnyNode): string { + const { op, tag, br, x } = node + let { ex } = node + if (op === 'const') return JSON.stringify(ex) + if (op === 'read') return `${node}` + if (op === 'subscript') ex = undefined + const args: string[] = [] + if (ex) args.push(JSON.stringify(ex)) + if (tag) args.push(tagStr(tag)) + args.push(...br.map(printNode), ...x.map(printNode)) + return `${op}(` + args.join(', ') + ')' + } + + if (value.op === 'reread') return tagStr(tag) + ` <- ` + tagStr(value.tag) + return tagStr(tag) + ` <= ` + printNode(value) +} + +export type DebugMeta = { + valText: string + text: string + deps: DebugMeta[] +} +export class DebugCalculator extends BaseCalculator { + override computeMeta( + n: AnyNode, + val: number | string, + x: (CalcResult | undefined)[], + br: CalcResult[], + tag: Tag | undefined + ): DebugMeta { + const result: DebugMeta = { + valText: valStr(val), + text: '', + deps: [], + } + function toStr( + x: CalcResult | undefined + ): string { + if (!x) return '' + result.deps.push(...x.meta.deps) + return x.meta.text + } + + function valStr(val: number | string): string { + if (typeof val !== 'number') return `"${val}"` + if (Math.round(val) === val) return `${val}` + return val.toFixed(2) + } + + const { op, ex, tag: nTag } = n + switch (op) { + case 'const': + result.text = result.valText + break + case 'read': { + const args = x as CalcResult[] + return { + valText: result.valText, + text: tagStr(nTag!, ex), + deps: [ + { + valText: result.valText, + text: `expand ${tagStr(nTag!, ex)} (${tagStr(tag!)})`, + deps: args.map(({ meta, entryTag }) => ({ + ...meta, + text: `${entryTag?.map((tag) => tagStr(tag)).join(' <- ')} <= ${ + meta.text + }`, + })), + }, + ], + } + } + case 'match': + case 'thres': + case 'lookup': { + const chosen = x.find((x) => x)! + result.text = `${op}(${br.map(toStr).join(', ')} => ${toStr(chosen)})` + break + } + case 'subscript': { + const [index] = br + const chosen = valStr(ex[index.val as number]!) + result.text = `subscript(${toStr(index)} => ${chosen})` + break + } + case 'tag': + result.text = `tag(${tagStr(nTag!)}, ${toStr(x[0])})` + break + case 'dtag': + result.text = `dtag(${br + .map((br, i) => `${ex[i]} => ${toStr(br)}`) + .join(', ')}; ${toStr(x[0])})` + break + default: { + const specialArgs = ex ? [JSON.stringify(ex)] : [] + const brArgs = br.map(toStr) + const xArgs = x.map(toStr) + const args = [specialArgs, brArgs, xArgs].map((x) => x.join(', ')) + + result.text = `${op}(` + args.filter((x) => x.length).join('; ') + ')' + } + } + return result + } + + debug(node: AnyNode): string[] { + const { meta } = this.compute(node) + const result: string[] = [] + const found = new Set() + + function print(meta: DebugMeta, level: number) { + const indent = Array(2 * level + 1).join(' ') + const line = `${meta.valText} ${meta.text}` + + if (found.has(meta)) { + result.push(indent + line + ' (Dup)') + } else { + found.add(meta) + result.push(indent + line) + meta.deps.forEach((dep) => print(dep, level + 1)) + } + } + print(meta, 0) + return result + } +} diff --git a/libs/sr-formula/src/index.ts b/libs/sr-formula/src/index.ts index 5d8e7bd027..431eac0d3b 100644 --- a/libs/sr-formula/src/index.ts +++ b/libs/sr-formula/src/index.ts @@ -3,6 +3,7 @@ import { compileTagMapValues, constant } from '@genshin-optimizer/pando' import { Calculator } from './calculator' import { keys, values } from './data' export * from './data/util' +export * from './debug' export function srCalculatorWithValues(extras: TagMapEntries) { return srCalculatorWithEntries( From 835ff5d7463605ee6b172e6e3d7d748070e28e2c Mon Sep 17 00:00:00 2001 From: Van Nguyen <36019388+nguyentvan7@users.noreply.github.com> Date: Tue, 2 Jan 2024 12:56:38 -0700 Subject: [PATCH 2/6] Fix lint --- libs/sr-formula/src/data/util/read.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/sr-formula/src/data/util/read.ts b/libs/sr-formula/src/data/util/read.ts index 21d7b0ce20..aeb47f2cd2 100644 --- a/libs/sr-formula/src/data/util/read.ts +++ b/libs/sr-formula/src/data/util/read.ts @@ -121,7 +121,7 @@ export function tagStr(tag: Tag, ex?: any): string { result += str + ' ' includedRequired = true } - function optional(str: string | undefined | null) { + function _optional(str: string | undefined | null) { if (!str) return if (includedRequired && !includedBar) { includedBar = true From ee14275004c5df63dd76071ffef181771fc95d05 Mon Sep 17 00:00:00 2001 From: Van Nguyen <36019388+nguyentvan7@users.noreply.github.com> Date: Tue, 2 Jan 2024 13:06:05 -0700 Subject: [PATCH 3/6] Fix lint --- libs/sr-formula/src/data/util/read.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/sr-formula/src/data/util/read.ts b/libs/sr-formula/src/data/util/read.ts index aeb47f2cd2..6a111a632b 100644 --- a/libs/sr-formula/src/data/util/read.ts +++ b/libs/sr-formula/src/data/util/read.ts @@ -121,7 +121,8 @@ export function tagStr(tag: Tag, ex?: any): string { result += str + ' ' includedRequired = true } - function _optional(str: string | undefined | null) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + function optional(str: string | undefined | null) { if (!str) return if (includedRequired && !includedBar) { includedBar = true From 48d7e494a1b780f5ce956b1cbf2b0d29d03c848c Mon Sep 17 00:00:00 2001 From: Van Nguyen <36019388+nguyentvan7@users.noreply.github.com> Date: Tue, 2 Jan 2024 13:59:48 -0700 Subject: [PATCH 4/6] Fix lint --- libs/sr-formula/src/data/util/read.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/sr-formula/src/data/util/read.ts b/libs/sr-formula/src/data/util/read.ts index 6a111a632b..604c62ed23 100644 --- a/libs/sr-formula/src/data/util/read.ts +++ b/libs/sr-formula/src/data/util/read.ts @@ -109,7 +109,7 @@ export function tagVal(cat: keyof Tag): TagValRead { } export function tagStr(tag: Tag, ex?: any): string { - const { name, member, dst, et, src, q, qt, ...remaining } = tag + const { name, member, dst, et, src, q, qt, dt, move, ...remaining } = tag if (Object.keys(remaining).length) console.error(remaining) @@ -121,7 +121,6 @@ export function tagStr(tag: Tag, ex?: any): string { result += str + ' ' includedRequired = true } - // eslint-disable-next-line @typescript-eslint/no-unused-vars function optional(str: string | undefined | null) { if (!str) return if (includedRequired && !includedBar) { @@ -139,6 +138,8 @@ export function tagStr(tag: Tag, ex?: any): string { else if (qt) required(`${qt}.`) else if (q) required(`.${q}`) + optional(dt) + optional(move) required(ex && `[${ex}]`) return result + '}' } From 21fee1996d0435bf245fb8e61b946686695096b6 Mon Sep 17 00:00:00 2001 From: Van Nguyen <36019388+nguyentvan7@users.noreply.github.com> Date: Wed, 3 Jan 2024 13:37:18 -0700 Subject: [PATCH 5/6] Update debug to match gi-formula --- libs/sr-formula/src/debug.ts | 59 ++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/libs/sr-formula/src/debug.ts b/libs/sr-formula/src/debug.ts index 9a88c8ed4c..3191f44091 100644 --- a/libs/sr-formula/src/debug.ts +++ b/libs/sr-formula/src/debug.ts @@ -115,6 +115,10 @@ export type DebugMeta = { deps: DebugMeta[] } export class DebugCalculator extends BaseCalculator { + constructor(calc: Calculator) { + super(calc.keys) + this.nodes = calc.nodes + } override computeMeta( n: AnyNode, val: number | string, @@ -127,6 +131,7 @@ export class DebugCalculator extends BaseCalculator { text: '', deps: [], } + function toStr( x: CalcResult | undefined ): string { @@ -134,7 +139,6 @@ export class DebugCalculator extends BaseCalculator { result.deps.push(...x.meta.deps) return x.meta.text } - function valStr(val: number | string): string { if (typeof val !== 'number') return `"${val}"` if (Math.round(val) === val) return `${val}` @@ -148,18 +152,19 @@ export class DebugCalculator extends BaseCalculator { break case 'read': { const args = x as CalcResult[] + const text = `read ${tagStr(nTag!, ex)}` return { valText: result.valText, - text: tagStr(nTag!, ex), + text, deps: [ { valText: result.valText, - text: `expand ${tagStr(nTag!, ex)} (${tagStr(tag!)})`, + text: `match ${tagStr(tag!, ex)} for ${text}`, deps: args.map(({ meta, entryTag }) => ({ ...meta, - text: `${entryTag?.map((tag) => tagStr(tag)).join(' <- ')} <= ${ - meta.text - }`, + text: `${entryTag + ?.map((tag) => (tag ? tagStr(tag) : '(unknown tag)')) + .join(' <- ')} <= ${meta.text}`, })), }, ], @@ -198,24 +203,38 @@ export class DebugCalculator extends BaseCalculator { return result } - debug(node: AnyNode): string[] { + debugCompute(node: AnyNode): string[] { const { meta } = this.compute(node) const result: string[] = [] - const found = new Set() - - function print(meta: DebugMeta, level: number) { - const indent = Array(2 * level + 1).join(' ') - const line = `${meta.valText} ${meta.text}` + addDebugString(meta, 0, result, new Set()) + return result + } - if (found.has(meta)) { - result.push(indent + line + ' (Dup)') - } else { - found.add(meta) - result.push(indent + line) - meta.deps.forEach((dep) => print(dep, level + 1)) - } + debugGet(tag: Tag): string[] { + const list = this.get(tag) + const result: string[] = [] + const found = new Set() + for (const { meta } of list) { + addDebugString(meta, 0, result, found) } - print(meta, 0) return result } } + +function addDebugString( + meta: DebugMeta, + level: number, + result: string[], + found: Set +) { + const indent = Array(2 * level + 1).join(' ') + const line = `${meta.valText} ${meta.text}` + + if (found.has(meta)) { + result.push(indent + line + ' (Dup)') + } else { + found.add(meta) + result.push(indent + line) + meta.deps.forEach((dep) => addDebugString(dep, level + 1, result, found)) + } +} From 99658366f361594648fd00ac64a0aa91661faf2d Mon Sep 17 00:00:00 2001 From: Van Nguyen <36019388+nguyentvan7@users.noreply.github.com> Date: Wed, 3 Jan 2024 13:48:41 -0700 Subject: [PATCH 6/6] Add toDebug function --- libs/sr-formula/src/calculator.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/sr-formula/src/calculator.ts b/libs/sr-formula/src/calculator.ts index 93d6b7c982..489ecc6511 100644 --- a/libs/sr-formula/src/calculator.ts +++ b/libs/sr-formula/src/calculator.ts @@ -2,6 +2,7 @@ import type { AnyNode, CalcResult } from '@genshin-optimizer/pando' import { Calculator as Base, calculation } from '@genshin-optimizer/pando' import { assertUnreachable } from '@genshin-optimizer/util' import type { Tag } from './data/util' +import { DebugCalculator } from './debug' const { arithmetic } = calculation @@ -68,4 +69,7 @@ export class Calculator extends Base { assertUnreachable(op) } } + toDebug(): DebugCalculator { + return new DebugCalculator(this) + } }