chore: can generator middle struct
This commit is contained in:
parent
07aa2ca9cf
commit
e0ed17a2e6
@ -0,0 +1,93 @@
|
|||||||
|
import { parseDSL } from './graph-to-log-struct-2'
|
||||||
|
|
||||||
|
describe('parseDSL', () => {
|
||||||
|
test('parse plain flow', () => {
|
||||||
|
const dsl = 'a -> b -> c'
|
||||||
|
const result = parseDSL(dsl)
|
||||||
|
expect(result).toEqual([
|
||||||
|
{ nodeType: 'plain', nodeId: 'a' },
|
||||||
|
{ nodeType: 'plain', nodeId: 'b' },
|
||||||
|
{ nodeType: 'plain', nodeId: 'c' },
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parse iteration node with flow', () => {
|
||||||
|
const dsl = '(iteration, a, b -> c)'
|
||||||
|
const result = parseDSL(dsl)
|
||||||
|
expect(result).toEqual([
|
||||||
|
{
|
||||||
|
nodeType: 'iteration',
|
||||||
|
nodeId: 'a',
|
||||||
|
params: [
|
||||||
|
[
|
||||||
|
{ nodeType: 'plain', nodeId: 'b' },
|
||||||
|
{ nodeType: 'plain', nodeId: 'c' },
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parse parallel node with flow', () => {
|
||||||
|
const dsl = 'a -> (parallel, b, c -> d, e)'
|
||||||
|
const result = parseDSL(dsl)
|
||||||
|
expect(result).toEqual([
|
||||||
|
{
|
||||||
|
nodeType: 'plain',
|
||||||
|
nodeId: 'a',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
nodeType: 'parallel',
|
||||||
|
nodeId: 'b',
|
||||||
|
params: [
|
||||||
|
[
|
||||||
|
{ nodeType: 'plain', nodeId: 'c' },
|
||||||
|
{ nodeType: 'plain', nodeId: 'd' },
|
||||||
|
],
|
||||||
|
// single node don't need to be wrapped in an array
|
||||||
|
{ nodeType: 'plain', nodeId: 'e' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parse retry', () => {
|
||||||
|
const dsl = '(retry, a, 3)'
|
||||||
|
const result = parseDSL(dsl)
|
||||||
|
expect(result).toEqual([
|
||||||
|
{
|
||||||
|
nodeType: 'retry',
|
||||||
|
nodeId: 'a',
|
||||||
|
params: [3],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parse nested complex nodes', () => {
|
||||||
|
const dsl = '(iteration, a, b -> (parallel, e, f -> g, h))'
|
||||||
|
const result = parseDSL(dsl)
|
||||||
|
expect(result).toEqual([
|
||||||
|
{
|
||||||
|
nodeType: 'iteration',
|
||||||
|
nodeId: 'a',
|
||||||
|
params: [
|
||||||
|
[
|
||||||
|
{ nodeType: 'plain', nodeId: 'b' },
|
||||||
|
{
|
||||||
|
nodeType: 'parallel',
|
||||||
|
nodeId: 'e',
|
||||||
|
params: [
|
||||||
|
[
|
||||||
|
{ nodeType: 'plain', nodeId: 'f' },
|
||||||
|
{ nodeType: 'plain', nodeId: 'g' },
|
||||||
|
],
|
||||||
|
// single node don't need to be wrapped in an array
|
||||||
|
{ nodeType: 'plain', nodeId: 'h' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
@ -0,0 +1,115 @@
|
|||||||
|
type NodePlain = { nodeType: 'plain'; nodeId: string }
|
||||||
|
type NodeComplex = { nodeType: string; nodeId: string; params: (NodePlain | NodeComplex | Node[])[] }
|
||||||
|
type Node = NodePlain | NodeComplex
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a DSL string into an array of node objects.
|
||||||
|
* @param dsl - The input DSL string.
|
||||||
|
* @returns An array of parsed nodes.
|
||||||
|
*/
|
||||||
|
function parseDSL(dsl: string): Node[] {
|
||||||
|
return parseTopLevelFlow(dsl).map(parseNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits a top-level flow string by "->", respecting nested structures.
|
||||||
|
* @param dsl - The DSL string to split.
|
||||||
|
* @returns An array of top-level segments.
|
||||||
|
*/
|
||||||
|
function parseTopLevelFlow(dsl: string): string[] {
|
||||||
|
const segments: string[] = []
|
||||||
|
let buffer = ''
|
||||||
|
let nested = 0
|
||||||
|
|
||||||
|
for (let i = 0; i < dsl.length; i++) {
|
||||||
|
const char = dsl[i]
|
||||||
|
if (char === '(') nested++
|
||||||
|
if (char === ')') nested--
|
||||||
|
if (char === '-' && dsl[i + 1] === '>' && nested === 0) {
|
||||||
|
segments.push(buffer.trim())
|
||||||
|
buffer = ''
|
||||||
|
i++ // Skip the ">" character
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buffer += char
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (buffer.trim())
|
||||||
|
segments.push(buffer.trim())
|
||||||
|
|
||||||
|
return segments
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a single node string.
|
||||||
|
* If the node is complex (e.g., has parentheses), it extracts the node type, node ID, and parameters.
|
||||||
|
* @param nodeStr - The node string to parse.
|
||||||
|
* @returns A parsed node object.
|
||||||
|
*/
|
||||||
|
function parseNode(nodeStr: string): Node {
|
||||||
|
// Check if the node is a complex node
|
||||||
|
if (nodeStr.startsWith('(') && nodeStr.endsWith(')')) {
|
||||||
|
const innerContent = nodeStr.slice(1, -1).trim() // Remove outer parentheses
|
||||||
|
let nested = 0
|
||||||
|
let buffer = ''
|
||||||
|
const parts: string[] = []
|
||||||
|
|
||||||
|
// Split the inner content by commas, respecting nested parentheses
|
||||||
|
for (let i = 0; i < innerContent.length; i++) {
|
||||||
|
const char = innerContent[i]
|
||||||
|
if (char === '(') nested++
|
||||||
|
if (char === ')') nested--
|
||||||
|
|
||||||
|
if (char === ',' && nested === 0) {
|
||||||
|
parts.push(buffer.trim())
|
||||||
|
buffer = ''
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buffer += char
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parts.push(buffer.trim())
|
||||||
|
|
||||||
|
// Extract nodeType, nodeId, and params
|
||||||
|
const [nodeType, nodeId, ...paramsRaw] = parts
|
||||||
|
const params = parseParams(paramsRaw)
|
||||||
|
|
||||||
|
return {
|
||||||
|
nodeType: nodeType.trim(),
|
||||||
|
nodeId: nodeId.trim(),
|
||||||
|
params,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's not a complex node, treat it as a plain node
|
||||||
|
return { nodeType: 'plain', nodeId: nodeStr.trim() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses parameters of a complex node.
|
||||||
|
* Supports nested flows and complex sub-nodes.
|
||||||
|
* @param paramParts - The parameters string split by commas.
|
||||||
|
* @returns An array of parsed parameters (plain nodes, nested nodes, or flows).
|
||||||
|
*/
|
||||||
|
function parseParams(paramParts: string[]): (Node | Node[])[] {
|
||||||
|
return paramParts.map((part) => {
|
||||||
|
if (part.includes('->')) {
|
||||||
|
// Parse as a flow and return an array of nodes
|
||||||
|
return parseTopLevelFlow(part).map(parseNode)
|
||||||
|
}
|
||||||
|
else if (part.startsWith('(')) {
|
||||||
|
// Parse as a nested complex node
|
||||||
|
return parseNode(part)
|
||||||
|
}
|
||||||
|
else if (!isNaN(Number(part.trim()))) {
|
||||||
|
// Parse as a numeric parameter
|
||||||
|
return Number(part.trim())
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Parse as a plain node
|
||||||
|
return parseNode(part)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export { parseDSL }
|
@ -2,6 +2,7 @@ const STEP_SPLIT = '->'
|
|||||||
|
|
||||||
const toNodeData = (step: string, info: Record<string, any> = {}): any => {
|
const toNodeData = (step: string, info: Record<string, any> = {}): any => {
|
||||||
const [nodeId, title] = step.split('@')
|
const [nodeId, title] = step.split('@')
|
||||||
|
|
||||||
const data: Record<string, any> = {
|
const data: Record<string, any> = {
|
||||||
id: nodeId,
|
id: nodeId,
|
||||||
node_id: nodeId,
|
node_id: nodeId,
|
||||||
@ -48,8 +49,10 @@ const toIterationNodeData = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const res = [toNodeData(nodeId, { isIteration: true })]
|
const res = [toNodeData(nodeId, { isIteration: true })]
|
||||||
// TODO: handle inner node structure
|
// TODO: handle inner node structure
|
||||||
for (let i = 0; i < children.length; i++)
|
for (let i = 0; i < children.length; i++) {
|
||||||
res.push(toNodeData(`${children[i]}`, { inIterationInfo: { iterationId: nodeId, iterationIndex: i } }))
|
const step = `${children[i]}`
|
||||||
|
res.push(toNodeData(step, { inIterationInfo: { iterationId: nodeId, iterationIndex: i } }))
|
||||||
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
@ -104,12 +107,21 @@ export function parseNodeString(input: string): NodeStructure {
|
|||||||
for (let i = 0; i < parts.length; i++) {
|
for (let i = 0; i < parts.length; i++) {
|
||||||
const part = parts[i]
|
const part = parts[i]
|
||||||
|
|
||||||
if (typeof part === 'string' && part.startsWith('('))
|
if (typeof part === 'string') {
|
||||||
result.params.push(parseNodeString(part))
|
if (part.startsWith('('))
|
||||||
else if (i === 0)
|
result.params.push(parseNodeString(part))
|
||||||
result.node = part as string
|
|
||||||
else
|
if (part.startsWith('[')) {
|
||||||
result.params.push(part as string)
|
const content = part.slice(1, -1)
|
||||||
|
result.params.push(parseNodeString(content))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (i === 0) {
|
||||||
|
result.node = part as unknown as string
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result.params.push(part as unknown as string)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@ -130,6 +142,8 @@ const toNodes = (input: string): any[] => {
|
|||||||
const { node, params } = parseNodeString(step)
|
const { node, params } = parseNodeString(step)
|
||||||
switch (node) {
|
switch (node) {
|
||||||
case 'iteration':
|
case 'iteration':
|
||||||
|
console.log(params)
|
||||||
|
break
|
||||||
res.push(...toIterationNodeData({
|
res.push(...toIterationNodeData({
|
||||||
nodeId: params[0] as string,
|
nodeId: params[0] as string,
|
||||||
children: JSON.parse(params[1] as string) as number[],
|
children: JSON.parse(params[1] as string) as number[],
|
||||||
|
Loading…
Reference in New Issue
Block a user