From eb196e90dcb30dc83f75f7f63d893a80843eeebf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=ED=95=9C=20Rycont?= <35295182+rycont@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:21:51 +0000 Subject: [PATCH 1/4] fix: sort dynamic rules by pattern length(desc) to proper parsing --- src/prepare/parse/dynamicRule/local/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/prepare/parse/dynamicRule/local/index.ts b/src/prepare/parse/dynamicRule/local/index.ts index 17c4848..9bdcd95 100644 --- a/src/prepare/parse/dynamicRule/local/index.ts +++ b/src/prepare/parse/dynamicRule/local/index.ts @@ -23,5 +23,7 @@ export function createLocalDynamicRules( }), ) - return [...functionRules, ...ffiRules].map((e) => [e]) + return [...functionRules, ...ffiRules] + .toSorted((a, b) => b.pattern.length - a.pattern.length) + .map((e) => [e]) } From 81d9a12fa294506e8b0fbf3a1efaf9ef5c1047d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=ED=95=9C=20Rycont?= <35295182+rycont@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:27:19 +0000 Subject: [PATCH 2/4] test: add cases for reproduction --- ...all-function-with-variable-as-argument.yak | 32 +++++++++++++++++++ ...function-with-variable-as-argument.yak.out | 12 +++++++ test/codes/function-variants.yak | 14 ++++++++ test/codes/function-variants.yak.out | 4 +++ 4 files changed, 62 insertions(+) create mode 100644 test/codes/call-function-with-variable-as-argument.yak create mode 100644 test/codes/call-function-with-variable-as-argument.yak.out diff --git a/test/codes/call-function-with-variable-as-argument.yak b/test/codes/call-function-with-variable-as-argument.yak new file mode 100644 index 0000000..089c741 --- /dev/null +++ b/test/codes/call-function-with-variable-as-argument.yak @@ -0,0 +1,32 @@ +약속, 회전설정 (회전) + 결과: "rotate:" + 회전 + +약속, 시간설정 (시간) + 결과: "time:" + 시간 + +약속, (A) 합 (B) + 결과: A + "" + B + +약속, (각도)도 회전하기 + 회전설정 각도 보여주기 + +약속, (시간)초 동안 (각도)도 회전하기 + (시간설정 시간) 합 (회전설정 각도) 보여주기 + +각도: 45 +시간: 30 + +(3)초 동안 (90)도 회전하기 +3 초 동안 90 도 회전하기 +(3)초 동안 90 도 회전하기 +3 초 동안 (90)도 회전하기 + +시간 초 동안 각도 도 회전하기 +(시간)초 동안 (각도)도 회전하기 +(시간)초 동안 각도 도 회전하기 +시간 초 동안 (각도)도 회전하기 + +(90)도 회전하기 +90 도 회전하기 +각도 도 회전하기 +(각도)도 회전하기 \ No newline at end of file diff --git a/test/codes/call-function-with-variable-as-argument.yak.out b/test/codes/call-function-with-variable-as-argument.yak.out new file mode 100644 index 0000000..d11a469 --- /dev/null +++ b/test/codes/call-function-with-variable-as-argument.yak.out @@ -0,0 +1,12 @@ +time:3rotate:90 +time:3rotate:90 +time:3rotate:90 +time:3rotate:90 +time:30rotate:45 +time:30rotate:45 +time:30rotate:45 +time:30rotate:45 +rotate:90 +rotate:90 +rotate:45 +rotate:45 diff --git a/test/codes/function-variants.yak b/test/codes/function-variants.yak index e2b37f4..83baad3 100644 --- a/test/codes/function-variants.yak +++ b/test/codes/function-variants.yak @@ -5,3 +5,17 @@ "햄버거"를 "영희"와 먹기 "치킨"을 "형님"과 먹기 "초밥"을 "동생"과 먹기 + +먹을_음식: "유부초밥" +먹일_사람: "현수" + +먹을_음식 을 먹일_사람 과 먹기 +먹을_음식 을 (먹일_사람)과 먹기 + +먹을_음식: "대나무" + +(먹을_음식)을 먹일_사람 과 먹기 + +먹일_사람: "지우" + +(먹을_음식)을 (먹일_사람)과 먹기 diff --git a/test/codes/function-variants.yak.out b/test/codes/function-variants.yak.out index e40fd2e..516b5cf 100644 --- a/test/codes/function-variants.yak.out +++ b/test/codes/function-variants.yak.out @@ -2,3 +2,7 @@ 맛있는 햄버거, 영희의 입으로 모두 들어갑니다. 맛있는 치킨, 형님의 입으로 모두 들어갑니다. 맛있는 초밥, 동생의 입으로 모두 들어갑니다. +맛있는 유부초밥, 현수의 입으로 모두 들어갑니다. +맛있는 유부초밥, 현수의 입으로 모두 들어갑니다. +맛있는 대나무, 현수의 입으로 모두 들어갑니다. +맛있는 대나무, 지우의 입으로 모두 들어갑니다. From 948d4cbd0cc8c92fb3b4b7493782e17b84d69f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=ED=95=9C=20Rycont?= <35295182+rycont@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:28:17 +0000 Subject: [PATCH 3/4] fix: figure function parameters with explicit retriever --- runtest.ts | 65 ++++++++-------- .../parse/dynamicRule/local/invokeRule.ts | 74 +++++++++++++++---- 2 files changed, 92 insertions(+), 47 deletions(-) diff --git a/runtest.ts b/runtest.ts index 8bc0447..25e6a7c 100644 --- a/runtest.ts +++ b/runtest.ts @@ -1,41 +1,38 @@ -import { FunctionParams } from './src/mod.ts' -import { NumberValue } from './src/mod.ts' import { yaksok } from './src/mod.ts' yaksok( ` -번역(JavaScript), (A)와 (B) 사이 랜덤 수 -*** - return Math.floor(Math.random() * (B - A) + A) -*** +약속, 회전설정 (회전) + 결과: "rotate:" + 회전 -(1)와 (10) 사이 랜덤 수 보여주기 -`, - { - runFFI: (runtime, code, params) => { - if (runtime !== 'JavaScript') { - throw new Error('지원하지 않는 런타임입니다') - } - - const runnableCode = buildCodeFromCodeAndParams(code, params) - const resultInJS = eval(runnableCode) - - if (typeof resultInJS !== 'number') { - throw new Error('결과값은 숫자여야 합니다') - } - - return new NumberValue(resultInJS) - }, - }, -) +약속, 시간설정 (시간) + 결과: "time:" + 시간 + +약속, (A) 합 (B) + 결과: A + "" + B + +약속, (각도)도 회전하기 + 회전설정 각도 보여주기 + +약속, (시간)초 동안 (각도)도 회전하기 + (시간설정 시간) 합 (회전설정 각도) 보여주기 -function buildCodeFromCodeAndParams(code: string, params: FunctionParams) { - const paramNames = Object.keys(params) - const paramsInJS = Object.fromEntries( - Object.entries(params).map(([key, value]) => [key, value.value]), - ) +각도: 45 +시간: 30 - return `((${paramNames.join(', ')}) => {${code}})(${Object.values( - paramsInJS, - ).join(', ')})` -} +(3)초 동안 (90)도 회전하기 +3 초 동안 90 도 회전하기 +(3)초 동안 90 도 회전하기 +3 초 동안 (90)도 회전하기 + +시간 초 동안 각도 도 회전하기 +(시간)초 동안 (각도)도 회전하기 +(시간)초 동안 각도 도 회전하기 +시간 초 동안 (각도)도 회전하기 + +(90)도 회전하기 +90 도 회전하기 +각도 도 회전하기 +(각도)도 회전하기 +`, +) diff --git a/src/prepare/parse/dynamicRule/local/invokeRule.ts b/src/prepare/parse/dynamicRule/local/invokeRule.ts index 7802270..aafe5e9 100644 --- a/src/prepare/parse/dynamicRule/local/invokeRule.ts +++ b/src/prepare/parse/dynamicRule/local/invokeRule.ts @@ -1,3 +1,4 @@ +import { FunctionParams } from '../../../../constant/type.ts' import { type Node, Expression, @@ -19,10 +20,13 @@ export function createFunctionInvokeRule( .map(functionHeaderToInvokeMap) .filter(Boolean) as PatternUnit[] + const paramNameIndexMap = getParamNameIndexMapFromHeader(subtokens) + return { pattern: invokeTemplate, factory(nodes: Node[]) { - const params = getParamsFromMatchedNodes(subtokens, nodes) + const params = getParamsFromMatchedNodes(paramNameIndexMap, nodes) + return new FunctionInvoke({ name, params, @@ -35,20 +39,64 @@ export function createFunctionInvokeRule( } function getParamsFromMatchedNodes( - template: FunctionHeaderNode[], - matchedNodes: Node[], + paramNameIndexMap: Map, + nodes: Node[], +) { + const params = {} as FunctionParams + + for (const [paramName, index] of paramNameIndexMap) { + const node = nodes[index] + + if (!(node instanceof Evaluable)) { + throw new Error('Node is not Evaluable') + } + + params[paramName] = node + } + + return params +} + +function getParamNameIndexMapFromHeader( + functionHeaderNodes: FunctionHeaderNode[], ) { - return Object.fromEntries( - template - .filter((token) => !(token instanceof Expression)) - .map((token, i) => [token, matchedNodes[i]]) - .filter( - (set): set is [Identifier, Evaluable] => - set[1] instanceof Evaluable && - !(set[1] instanceof Identifier), - ) - .map(([token, node]) => [token.value, node]), + let parenthesesCount = 0 + + const paramNameIndexMap = new Map( + ( + functionHeaderNodes + .map((token, index) => { + if (token instanceof Expression) { + parenthesesCount += 1 + return null + } + + const prevToken = functionHeaderNodes[index - 1] + const nextToken = functionHeaderNodes[index + 1] + + if (!prevToken || !nextToken) { + return null + } + + const isPreviousTokenOpeningParentheses = + isParentheses(prevToken) === BRACKET_TYPE.OPENING + const isNextTokenClosingParentheses = + isParentheses(nextToken) === BRACKET_TYPE.CLOSING + + if ( + !isPreviousTokenOpeningParentheses || + !isNextTokenClosingParentheses + ) { + return null + } + + return [token, index - parenthesesCount] + }) + .filter(Boolean) as [FunctionHeaderNode, number][] + ).map(([token, index]) => [token.value, index]), ) + + return paramNameIndexMap } function functionHeaderToInvokeMap( From 2c486048e1abd59f4367844f46041839d6892b7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=ED=95=9C=20Rycont?= <35295182+rycont@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:35:35 +0000 Subject: [PATCH 4/4] chore: bump package versions to 0.1.17 --- deno.json | 2 +- quickjs/deno.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deno.json b/deno.json index f2bb760..f5b14da 100644 --- a/deno.json +++ b/deno.json @@ -33,7 +33,7 @@ } }, "name": "@yaksok-ts/core", - "version": "0.1.16", + "version": "0.1.17", "exports": "./src/mod.ts", "nodeModulesDir": "auto", "workspace": [ diff --git a/quickjs/deno.json b/quickjs/deno.json index 6fa95b8..9101e45 100644 --- a/quickjs/deno.json +++ b/quickjs/deno.json @@ -5,5 +5,5 @@ "quickjs-emscripten": "npm:quickjs-emscripten@^0.31.0", "quickjs-emscripten-core": "npm:quickjs-emscripten-core@^0.31.0" }, - "version": "0.1.16" + "version": "0.1.17" } \ No newline at end of file