Skip to content

Commit

Permalink
Merge pull request #56 from rycont/support-async-ffi-0.2.x
Browse files Browse the repository at this point in the history
Support async ffi 0.2.x
  • Loading branch information
rycont authored Dec 8, 2024
2 parents 544d72e + c3a71af commit cb7b1dd
Show file tree
Hide file tree
Showing 56 changed files with 332 additions and 269 deletions.
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
}
},
"name": "@yaksok-ts/core",
"version": "0.2.0-alpha.4+20241126.nightly",
"version": "0.2.0-alpha.5+20241206.nightly",
"exports": "./src/mod.ts",
"nodeModulesDir": "auto",
"workspace": [
Expand Down
4 changes: 2 additions & 2 deletions docs-component/code-runner.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ function viewAnswer() {
editorInstance.setValue(props.challenge.answerCode)
}
function runCode() {
async function runCode() {
stdout.value = []
try {
yaksok(code.value, {
await yaksok(code.value, {
stdout: (output) => {
stdout.value = [...stdout.value, output]
},
Expand Down
2 changes: 1 addition & 1 deletion docs/library/1. 시작하기.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ bunx jsr add @yaksok-ts/core

```ts
import { yaksok } from '@yaksok-ts/core'
yaksok(`"안녕, 세상!" 보여주기`) // "안녕, 세상!"이 콘솔에 출력됩니다.
await yaksok(`"안녕, 세상!" 보여주기`) // "안녕, 세상!"이 콘솔에 출력됩니다.
```
2 changes: 1 addition & 1 deletion docs/library/2. 런타임에서 변수 가져오기.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const code = `
내_이름: "영희"
`

const result = yaksok(code)
const result = await yaksok(code)
console.log(result.scope.getVariable('내_이름').value)
```

Expand Down
4 changes: 2 additions & 2 deletions docs/library/3. 표준출력(보여주기) 받아오기.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const config: RuntimeConfig = { // [!code highlight]
}, // [!code highlight]
} // [!code highlight]

yaksok(code, config)
await yaksok(code, config)
```
<!-- prettier-ignore-end -->

Expand All @@ -40,7 +40,7 @@ const config: RuntimeConfig = { // [!code highlight]
}, // [!code highlight]
} // [!code highlight]

yaksok(code, config)
await yaksok(code, config)
```
<!-- prettier-ignore-end -->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@
```typescript
import { yaksok } from '@yaksok-ts/core'

yaksok(code, {
runFFI: (runtime, code, params) => {
await yaksok(code, {
runFFI: async (runtime, code, params) => {
// Do something: 번역할 코드를 실행합니다
},
})
Expand Down Expand Up @@ -85,6 +85,10 @@ yaksok(code, {

`runFFI` 함수의 반환값은 다시 약속 런타임으로 돌아갑니다. **반환값은 약속 런타임이 이해할 수 있는 값([`ValueType`](/api/classes/ValueType))이여야 합니다.** 반환값이 약속 런타임의 값이 아니거나 `undefined`일 경우 오류가 발생합니다.

::: tip Promise를 반환할 수 있습니다
비동기로 실행되는 작업을 위해 `runFFI` 함수는 `Promise`를 반환할 수 있습니다. 이 경우 반환값의 타입은 `Promise<ValueType>`이 됩니다.
:::

## 예제

### 약속이 아닌 언어의 코드를 호출해 값을 가져오기
Expand All @@ -94,7 +98,7 @@ yaksok(code, {
```typescript
import { yaksok, FunctionParams, NumberValue } from '@yaksok-ts/core'

yaksok(
await yaksok(
`
번역(JavaScript), (A)와 (B) 사이 랜덤 수
***
Expand Down Expand Up @@ -159,7 +163,7 @@ const code = `
"현재 실행중인 환경은 " + (브라우저의 UserAgent 가져오기) 보여주기
`

yaksok(code, { runFFI })
await yaksok(code, { runFFI })
```

::: tip 쉬운 이해를 위해 일부 코드가 생략되었습니다
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ function runFFI(
return result
}

yaksok(code, { runFFI })
await yaksok(code, { runFFI })
```

약속 런타임과 QuickJS 데이터타입, 자바스크립트 데이터타입 사이의 형변환은 라이브러리 내에서 자동으로 이루어집니다.
Expand Down
2 changes: 1 addition & 1 deletion quickjs/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"quickjs-emscripten": "npm:quickjs-emscripten@^0.31.0",
"quickjs-emscripten-core": "npm:quickjs-emscripten-core@^0.31.0"
},
"version": "0.2.0-alpha.4+20241126.nightly"
"version": "0.2.0-alpha.5+20241206.nightly"
}
8 changes: 4 additions & 4 deletions quickjs/quickjs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Deno.test('Error in QuickJS', async () => {
await quickJS.init()

try {
yaksok(
await yaksok(
`
번역(QuickJS), 에러 발생
***
Expand Down Expand Up @@ -36,7 +36,7 @@ Deno.test('QuickJS passed number', async () => {
const quickJS = new QuickJS()
await quickJS.init()

const result = yaksok(
const result = await yaksok(
`
번역(QuickJS), 랜덤 수
***
Expand Down Expand Up @@ -65,7 +65,7 @@ Deno.test('QuickJS passed Array<number>', async () => {
const quickJS = new QuickJS()
await quickJS.init()

const result = yaksok(
const result = await yaksok(
`
번역(QuickJS), 랜덤 수
***
Expand Down Expand Up @@ -102,7 +102,7 @@ Deno.test('JavaScript bridge function passed object', async () => {
})
await quickJS.init()

const result = yaksok(
const result = await yaksok(
`
번역(QuickJS), 학생 정보
***
Expand Down
44 changes: 14 additions & 30 deletions runtest.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,18 @@
import { yaksok } from '@yaksok-ts/core'
import { QuickJS } from '@yaksok-ts/quickjs'
import { yaksok, type ValueType } from '@yaksok-ts/core'

const quickJS = new QuickJS()
await quickJS.init()

const code = `
번역(QuickJS), (최소)와 (최대) 사이의 랜덤한 수
***
return Math.floor(Math.random() * (최대 - 최소 + 1)) + 최소
***
(1)와 (10) 사이의 랜덤한 수 보여주기
`

function runFFI(
runtime: string,
code: string,
params: Record<string, ValueType>,
) {
if (runtime !== 'QuickJS') {
throw new Error(`Unknown runtime: ${runtime}`)
}
const quickJS = new QuickJS({
prompt: () => {
return '10'
},
})

const result = quickJS.run(code, params)

if (!result) {
throw new Error('Result is null')
}

return result
}
await quickJS.init()

yaksok(code, { runFFI })
await yaksok({
아두이노: `
약속, 이름
결과: "아두이노" / 2
`,
main: '(@아두이노 이름) 보여주기',
})
10 changes: 5 additions & 5 deletions src/error/printError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ export function printError(error: YaksokError) {

output += '> ' + error.message + '\n\n'

if (!code || !error.position) return output

output += '┌─────\n'
output += getHintCode(error.position, code)
output += '└─────\n'
if (code && error.position) {
output += '┌─────\n'
output += getHintCode(error.position, code)
output += '└─────\n'
}

if (error.child) {
output += '\n'
Expand Down
6 changes: 3 additions & 3 deletions src/executer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import { BreakSignal, ReturnSignal } from './signals.ts'
import type { CodeFile } from '../type/code-file.ts'
import type { Executable } from '../node/base.ts'

export function executer<NodeType extends Executable>(
export async function executer<NodeType extends Executable>(
node: NodeType,
codeFile?: CodeFile,
): ExecuteResult<NodeType> {
): Promise<ExecuteResult<NodeType>> {
const scope =
codeFile?.runResult?.scope ||
new Scope({
Expand All @@ -22,7 +22,7 @@ export function executer<NodeType extends Executable>(
const callFrame = new CallFrame(node, undefined)

try {
const result = node.execute(scope, callFrame) as ReturnType<
const result = (await node.execute(scope, callFrame)) as ReturnType<
NodeType['execute']
>
return { scope, result }
Expand Down
16 changes: 10 additions & 6 deletions src/node/IfStatement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,29 @@ export class IfStatement extends Executable {
super()
}

override execute(scope: Scope, _callFrame: CallFrame) {
override async execute(scope: Scope, _callFrame: CallFrame) {
const callFrame = new CallFrame(this, _callFrame)

for (const { condition, body } of this.cases) {
const shouldStop = this.shouldStop(condition, scope, callFrame)
const shouldStop = await this.shouldStop(
condition,
scope,
callFrame,
)
if (!shouldStop) continue

body.execute(scope, callFrame)
await body.execute(scope, callFrame)
break
}
}

shouldStop(
async shouldStop(
condition: Evaluable | undefined,
scope: Scope,
_callFrame: CallFrame,
): boolean {
): Promise<boolean> {
const callFrame = new CallFrame(this, _callFrame)
return !condition || isTruthy(condition.execute(scope, callFrame))
return !condition || isTruthy(await condition.execute(scope, callFrame))
}
}

Expand Down
10 changes: 6 additions & 4 deletions src/node/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ export class Node {
throw new Error(`${this.constructor.name} has no toPrint method`)
}
}

export class Executable extends Node {
static override friendlyName = '실행 가능한 노드'

execute(_scope: Scope, _callFrame: CallFrame) {
execute(_scope: Scope, _callFrame: CallFrame): Promise<unknown> {
throw new Error(`${this.constructor.name} has no execute method`)
}

override toPrint(): string {
throw new Error(`${this.constructor.name} has no toPrint method`)
}
Expand All @@ -34,7 +36,7 @@ export class Executable extends Node {
export class Evaluable<T extends ValueType = ValueType> extends Executable {
static override friendlyName = '값이 있는 노드'

override execute(_scope: Scope, _callFrame: CallFrame): T {
override execute(_scope: Scope, _callFrame: CallFrame): Promise<T> {
throw new Error(`${this.constructor.name} has no execute method`)
}
}
Expand All @@ -50,9 +52,9 @@ export class Identifier extends Evaluable {
return this.value
}

override execute(scope: Scope, _callFrame: CallFrame): ValueType {
override execute(scope: Scope, _callFrame: CallFrame): Promise<ValueType> {
try {
return scope.getVariable(this.value)
return Promise.resolve(scope.getVariable(this.value))
} catch (e) {
if (e instanceof NotDefinedIdentifierError) {
e.position = this.position
Expand Down
4 changes: 2 additions & 2 deletions src/node/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ export class Block extends Executable {
this.children = content
}

override execute(scope: Scope, _callFrame: CallFrame) {
override async execute(scope: Scope, _callFrame: CallFrame) {
const callFrame = new CallFrame(this, _callFrame)

for (const child of this.children) {
if (child instanceof Executable) {
child.execute(scope, callFrame)
await child.execute(scope, callFrame)
} else if (child instanceof EOL) {
continue
} else {
Expand Down
15 changes: 9 additions & 6 deletions src/node/calculation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class ValueWithParenthesis extends Evaluable {
super()
}

override execute(scope: Scope, _callFrame: CallFrame): ValueType {
override execute(scope: Scope, _callFrame: CallFrame): Promise<ValueType> {
const callFrame = new CallFrame(this, _callFrame)
return this.value.execute(scope, callFrame)
}
Expand All @@ -59,7 +59,10 @@ export class Formula extends Evaluable {
super()
}

override execute(scope: Scope, _callFrame: CallFrame): ValueType {
override async execute(
scope: Scope,
_callFrame: CallFrame,
): Promise<ValueType> {
const callFrame = new CallFrame(this, _callFrame)
const terms = [...this.terms]

Expand All @@ -68,7 +71,7 @@ export class Formula extends Evaluable {
currentPrecedence >= 0;
currentPrecedence--
) {
this.calculateOperatorWithPrecedence(
await this.calculateOperatorWithPrecedence(
terms,
currentPrecedence,
scope,
Expand All @@ -79,7 +82,7 @@ export class Formula extends Evaluable {
return terms[0] as ValueType
}

calculateOperatorWithPrecedence(
async calculateOperatorWithPrecedence(
terms: (Evaluable | Operator | ValueType)[],
precedence: number,
scope: Scope,
Expand All @@ -103,12 +106,12 @@ export class Formula extends Evaluable {
const left =
leftTerm instanceof ValueType
? leftTerm
: leftTerm.execute(scope, callFrame)
: await leftTerm.execute(scope, callFrame)

const right =
rightTerm instanceof ValueType
? rightTerm
: rightTerm.execute(scope, callFrame)
: await rightTerm.execute(scope, callFrame)

const result = term.call(left, right)
terms.splice(i - 1, 3, result)
Expand Down
3 changes: 2 additions & 1 deletion src/node/ffi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ export class DeclareFFI extends Executable {
this.position = props.position
}

override execute(scope: Scope): void {
override execute(scope: Scope): Promise<void> {
scope.addFunctionObject(this.toFFIObject(scope))
return Promise.resolve()
}

toFFIObject(scope: Scope): FFIObject {
Expand Down
Loading

0 comments on commit cb7b1dd

Please sign in to comment.