feat: synchronize input/output variables in the panel with generated code by the code generator (#10150)
parent
fafa5938da
commit
f674de4f5d
@ -0,0 +1,326 @@
|
||||
import { VarType } from '../../types'
|
||||
import { extractFunctionParams, extractReturnType } from './code-parser'
|
||||
import { CodeLanguage } from './types'
|
||||
|
||||
const SAMPLE_CODES = {
|
||||
python3: {
|
||||
noParams: 'def main():',
|
||||
singleParam: 'def main(param1):',
|
||||
multipleParams: `def main(param1, param2, param3):
|
||||
return {"result": param1}`,
|
||||
withTypes: `def main(param1: str, param2: int, param3: List[str]):
|
||||
result = process_data(param1, param2)
|
||||
return {"output": result}`,
|
||||
withDefaults: `def main(param1: str = "default", param2: int = 0):
|
||||
return {"data": param1}`,
|
||||
},
|
||||
javascript: {
|
||||
noParams: 'function main() {',
|
||||
singleParam: 'function main(param1) {',
|
||||
multipleParams: `function main(param1, param2, param3) {
|
||||
return { result: param1 }
|
||||
}`,
|
||||
withComments: `// Main function
|
||||
function main(param1, param2) {
|
||||
// Process data
|
||||
return { output: process(param1, param2) }
|
||||
}`,
|
||||
withSpaces: 'function main( param1 , param2 ) {',
|
||||
},
|
||||
}
|
||||
|
||||
describe('extractFunctionParams', () => {
|
||||
describe('Python3', () => {
|
||||
test('handles no parameters', () => {
|
||||
const result = extractFunctionParams(SAMPLE_CODES.python3.noParams, CodeLanguage.python3)
|
||||
expect(result).toEqual([])
|
||||
})
|
||||
|
||||
test('extracts single parameter', () => {
|
||||
const result = extractFunctionParams(SAMPLE_CODES.python3.singleParam, CodeLanguage.python3)
|
||||
expect(result).toEqual(['param1'])
|
||||
})
|
||||
|
||||
test('extracts multiple parameters', () => {
|
||||
const result = extractFunctionParams(SAMPLE_CODES.python3.multipleParams, CodeLanguage.python3)
|
||||
expect(result).toEqual(['param1', 'param2', 'param3'])
|
||||
})
|
||||
|
||||
test('handles type hints', () => {
|
||||
const result = extractFunctionParams(SAMPLE_CODES.python3.withTypes, CodeLanguage.python3)
|
||||
expect(result).toEqual(['param1', 'param2', 'param3'])
|
||||
})
|
||||
|
||||
test('handles default values', () => {
|
||||
const result = extractFunctionParams(SAMPLE_CODES.python3.withDefaults, CodeLanguage.python3)
|
||||
expect(result).toEqual(['param1', 'param2'])
|
||||
})
|
||||
})
|
||||
|
||||
// JavaScriptのテストケース
|
||||
describe('JavaScript', () => {
|
||||
test('handles no parameters', () => {
|
||||
const result = extractFunctionParams(SAMPLE_CODES.javascript.noParams, CodeLanguage.javascript)
|
||||
expect(result).toEqual([])
|
||||
})
|
||||
|
||||
test('extracts single parameter', () => {
|
||||
const result = extractFunctionParams(SAMPLE_CODES.javascript.singleParam, CodeLanguage.javascript)
|
||||
expect(result).toEqual(['param1'])
|
||||
})
|
||||
|
||||
test('extracts multiple parameters', () => {
|
||||
const result = extractFunctionParams(SAMPLE_CODES.javascript.multipleParams, CodeLanguage.javascript)
|
||||
expect(result).toEqual(['param1', 'param2', 'param3'])
|
||||
})
|
||||
|
||||
test('handles comments in code', () => {
|
||||
const result = extractFunctionParams(SAMPLE_CODES.javascript.withComments, CodeLanguage.javascript)
|
||||
expect(result).toEqual(['param1', 'param2'])
|
||||
})
|
||||
|
||||
test('handles whitespace', () => {
|
||||
const result = extractFunctionParams(SAMPLE_CODES.javascript.withSpaces, CodeLanguage.javascript)
|
||||
expect(result).toEqual(['param1', 'param2'])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
const RETURN_TYPE_SAMPLES = {
|
||||
python3: {
|
||||
singleReturn: `
|
||||
def main(param1):
|
||||
return {"result": "value"}`,
|
||||
|
||||
multipleReturns: `
|
||||
def main(param1, param2):
|
||||
return {"result": "value", "status": "success"}`,
|
||||
|
||||
noReturn: `
|
||||
def main():
|
||||
print("Hello")`,
|
||||
|
||||
complexReturn: `
|
||||
def main():
|
||||
data = process()
|
||||
return {"result": data, "count": 42, "messages": ["hello"]}`,
|
||||
nestedObject: `
|
||||
def main(name, age, city):
|
||||
return {
|
||||
'personal_info': {
|
||||
'name': name,
|
||||
'age': age,
|
||||
'city': city
|
||||
},
|
||||
'timestamp': int(time.time()),
|
||||
'status': 'active'
|
||||
}`,
|
||||
},
|
||||
|
||||
javascript: {
|
||||
singleReturn: `
|
||||
function main(param1) {
|
||||
return { result: "value" }
|
||||
}`,
|
||||
|
||||
multipleReturns: `
|
||||
function main(param1) {
|
||||
return { result: "value", status: "success" }
|
||||
}`,
|
||||
|
||||
withParentheses: `
|
||||
function main() {
|
||||
return ({ result: "value", status: "success" })
|
||||
}`,
|
||||
|
||||
noReturn: `
|
||||
function main() {
|
||||
console.log("Hello")
|
||||
}`,
|
||||
|
||||
withQuotes: `
|
||||
function main() {
|
||||
return { "result": 'value', 'status': "success" }
|
||||
}`,
|
||||
nestedObject: `
|
||||
function main(name, age, city) {
|
||||
return {
|
||||
personal_info: {
|
||||
name: name,
|
||||
age: age,
|
||||
city: city
|
||||
},
|
||||
timestamp: Date.now(),
|
||||
status: 'active'
|
||||
}
|
||||
}`,
|
||||
withJSDoc: `
|
||||
/**
|
||||
* Creates a user profile with personal information and metadata
|
||||
* @param {string} name - The user's name
|
||||
* @param {number} age - The user's age
|
||||
* @param {string} city - The user's city of residence
|
||||
* @returns {Object} An object containing the user profile
|
||||
*/
|
||||
function main(name, age, city) {
|
||||
return {
|
||||
result: {
|
||||
personal_info: {
|
||||
name: name,
|
||||
age: age,
|
||||
city: city
|
||||
},
|
||||
timestamp: Date.now(),
|
||||
status: 'active'
|
||||
}
|
||||
};
|
||||
}`,
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
describe('extractReturnType', () => {
|
||||
// Python3のテスト
|
||||
describe('Python3', () => {
|
||||
test('extracts single return value', () => {
|
||||
const result = extractReturnType(RETURN_TYPE_SAMPLES.python3.singleReturn, CodeLanguage.python3)
|
||||
expect(result).toEqual({
|
||||
result: {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('extracts multiple return values', () => {
|
||||
const result = extractReturnType(RETURN_TYPE_SAMPLES.python3.multipleReturns, CodeLanguage.python3)
|
||||
expect(result).toEqual({
|
||||
result: {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
},
|
||||
status: {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('returns empty object when no return statement', () => {
|
||||
const result = extractReturnType(RETURN_TYPE_SAMPLES.python3.noReturn, CodeLanguage.python3)
|
||||
expect(result).toEqual({})
|
||||
})
|
||||
|
||||
test('handles complex return statement', () => {
|
||||
const result = extractReturnType(RETURN_TYPE_SAMPLES.python3.complexReturn, CodeLanguage.python3)
|
||||
expect(result).toEqual({
|
||||
result: {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
},
|
||||
count: {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
},
|
||||
messages: {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
},
|
||||
})
|
||||
})
|
||||
test('handles nested object structure', () => {
|
||||
const result = extractReturnType(RETURN_TYPE_SAMPLES.python3.nestedObject, CodeLanguage.python3)
|
||||
expect(result).toEqual({
|
||||
personal_info: {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
},
|
||||
timestamp: {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
},
|
||||
status: {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// JavaScriptのテスト
|
||||
describe('JavaScript', () => {
|
||||
test('extracts single return value', () => {
|
||||
const result = extractReturnType(RETURN_TYPE_SAMPLES.javascript.singleReturn, CodeLanguage.javascript)
|
||||
expect(result).toEqual({
|
||||
result: {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('extracts multiple return values', () => {
|
||||
const result = extractReturnType(RETURN_TYPE_SAMPLES.javascript.multipleReturns, CodeLanguage.javascript)
|
||||
expect(result).toEqual({
|
||||
result: {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
},
|
||||
status: {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('handles return with parentheses', () => {
|
||||
const result = extractReturnType(RETURN_TYPE_SAMPLES.javascript.withParentheses, CodeLanguage.javascript)
|
||||
expect(result).toEqual({
|
||||
result: {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
},
|
||||
status: {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('returns empty object when no return statement', () => {
|
||||
const result = extractReturnType(RETURN_TYPE_SAMPLES.javascript.noReturn, CodeLanguage.javascript)
|
||||
expect(result).toEqual({})
|
||||
})
|
||||
|
||||
test('handles quoted keys', () => {
|
||||
const result = extractReturnType(RETURN_TYPE_SAMPLES.javascript.withQuotes, CodeLanguage.javascript)
|
||||
expect(result).toEqual({
|
||||
result: {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
},
|
||||
status: {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
},
|
||||
})
|
||||
})
|
||||
test('handles nested object structure', () => {
|
||||
const result = extractReturnType(RETURN_TYPE_SAMPLES.javascript.nestedObject, CodeLanguage.javascript)
|
||||
expect(result).toEqual({
|
||||
personal_info: {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
},
|
||||
timestamp: {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
},
|
||||
status: {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -0,0 +1,86 @@
|
||||
import { VarType } from '../../types'
|
||||
import type { OutputVar } from './types'
|
||||
import { CodeLanguage } from './types'
|
||||
|
||||
export const extractFunctionParams = (code: string, language: CodeLanguage) => {
|
||||
if (language === CodeLanguage.json)
|
||||
return []
|
||||
|
||||
const patterns: Record<Exclude<CodeLanguage, CodeLanguage.json>, RegExp> = {
|
||||
[CodeLanguage.python3]: /def\s+main\s*\((.*?)\)/,
|
||||
[CodeLanguage.javascript]: /function\s+main\s*\((.*?)\)/,
|
||||
}
|
||||
const match = code.match(patterns[language])
|
||||
const params: string[] = []
|
||||
|
||||
if (match?.[1]) {
|
||||
params.push(...match[1].split(',')
|
||||
.map(p => p.trim())
|
||||
.filter(Boolean)
|
||||
.map(p => p.split(':')[0].trim()),
|
||||
)
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
export const extractReturnType = (code: string, language: CodeLanguage): OutputVar => {
|
||||
const codeWithoutComments = code.replace(/\/\*\*[\s\S]*?\*\//, '')
|
||||
console.log(codeWithoutComments)
|
||||
|
||||
const returnIndex = codeWithoutComments.indexOf('return')
|
||||
if (returnIndex === -1)
|
||||
return {}
|
||||
|
||||
// returnから始まる部分文字列を取得
|
||||
const codeAfterReturn = codeWithoutComments.slice(returnIndex)
|
||||
|
||||
let bracketCount = 0
|
||||
let startIndex = codeAfterReturn.indexOf('{')
|
||||
|
||||
if (language === CodeLanguage.javascript && startIndex === -1) {
|
||||
const parenStart = codeAfterReturn.indexOf('(')
|
||||
if (parenStart !== -1)
|
||||
startIndex = codeAfterReturn.indexOf('{', parenStart)
|
||||
}
|
||||
|
||||
if (startIndex === -1)
|
||||
return {}
|
||||
|
||||
let endIndex = -1
|
||||
|
||||
for (let i = startIndex; i < codeAfterReturn.length; i++) {
|
||||
if (codeAfterReturn[i] === '{')
|
||||
bracketCount++
|
||||
if (codeAfterReturn[i] === '}') {
|
||||
bracketCount--
|
||||
if (bracketCount === 0) {
|
||||
endIndex = i + 1
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (endIndex === -1)
|
||||
return {}
|
||||
|
||||
const returnContent = codeAfterReturn.slice(startIndex + 1, endIndex - 1)
|
||||
console.log(returnContent)
|
||||
|
||||
const result: OutputVar = {}
|
||||
|
||||
const keyRegex = /['"]?(\w+)['"]?\s*:(?![^{]*})/g
|
||||
const matches = returnContent.matchAll(keyRegex)
|
||||
|
||||
for (const match of matches) {
|
||||
console.log(`Found key: "${match[1]}" from match: "${match[0]}"`)
|
||||
const key = match[1]
|
||||
result[key] = {
|
||||
type: VarType.string,
|
||||
children: null,
|
||||
}
|
||||
}
|
||||
|
||||
console.log(result)
|
||||
|
||||
return result
|
||||
}
|
||||
Loading…
Reference in New Issue