Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: range operator to handle non-integer numbers #70

Merged
merged 5 commits into from
Jan 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
},
"name": "@dalbit-yaksok/core",
"exports": "./mod.ts",
"version": "0.2.0-RC.3"
"version": "0.2.0-RC.4"
}
28 changes: 28 additions & 0 deletions core/error/calculation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,31 @@ export class InvalidTypeForOperatorError extends YaksokError {
)}할 수 없어요.`
}
}

export class RangeStartMustBeIntegerError extends YaksokError {
constructor(props: {
tokens?: Token[]
resource: {
start: ValueType
}
}) {
super(props)

const startText = valueTypeToText(props.resource.start)
this.message = `범위의 시작은 정수여야 해요. ${startText}는 정수가 아니에요.`
}
}

export class RangeEndMustBeIntegerError extends YaksokError {
constructor(props: {
tokens?: Token[]
resource: {
end: ValueType
}
}) {
super(props)

const endText = valueTypeToText(props.resource.end)
this.message = `범위의 끝은 정수여야 해요. ${endText}는 정수가 아니에요.`
}
}
4 changes: 1 addition & 3 deletions core/error/indexed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export class ListIndexMustBeGreaterThan1Error extends YaksokError {

export class RangeEndMustBeNumberError extends YaksokError {
constructor(props: {
position?: Position
tokens?: Token[]
resource: {
end: ValueType
}
Expand Down Expand Up @@ -86,7 +86,6 @@ export class ListIndexTypeError extends YaksokError {

export class RangeStartMustBeNumberError extends YaksokError {
constructor(props: {
position?: Position
tokens?: Token[]
resource: {
start: ValueType
Expand All @@ -102,7 +101,6 @@ export class RangeStartMustBeNumberError extends YaksokError {
export class TargetIsNotIndexedValueError extends YaksokError {
constructor(props: {
tokens: Token[]

resource: {
target: Evaluable
}
Expand Down
16 changes: 13 additions & 3 deletions core/node/calculation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ import {
import { Evaluable, Operator, OperatorClass } from './base.ts'
import { ValueType } from '../value/base.ts'
import type { Token } from '../prepare/tokenize/token.ts'
import { InvalidTypeForOperatorError } from '../error/calculation.ts'
import {
InvalidTypeForOperatorError,
RangeEndMustBeIntegerError,
RangeStartMustBeIntegerError,
} from '../error/calculation.ts'
import { YaksokError } from '../error/common.ts'
import {
RangeEndMustBeNumberError,
Expand Down Expand Up @@ -153,9 +157,15 @@ export class Formula extends Evaluable {
if (e instanceof YaksokError && !e.tokens) {
if (e instanceof InvalidTypeForOperatorError) {
e.tokens = mergedTokens
} else if (e instanceof RangeStartMustBeNumberError) {
} else if (
e instanceof RangeStartMustBeNumberError ||
e instanceof RangeStartMustBeIntegerError
) {
e.tokens = termsWithToken[i - 1].tokens
} else if (e instanceof RangeEndMustBeNumberError) {
} else if (
e instanceof RangeEndMustBeNumberError ||
e instanceof RangeEndMustBeIntegerError
) {
e.tokens = termsWithToken[i + 1].tokens
} else if (e instanceof RangeStartMustBeLessThanEndError) {
e.tokens = mergedTokens
Expand Down
50 changes: 34 additions & 16 deletions core/node/operator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
RangeEndMustBeNumberError,
RangeStartMustBeLessThanEndError,
RangeStartMustBeNumberError,
RangeStartMustBeIntegerError,
RangeEndMustBeIntegerError,
} from '../error/index.ts'
import { Token } from '../prepare/tokenize/token.ts'

Expand Down Expand Up @@ -445,9 +447,11 @@ export class RangeOperator extends Operator {
this.assertProperOperands(operands)

const [start, end] = operands
const items = new Array(end.value - start.value + 1)
const roundedStart = Math.round(start.value)
const roundedEnd = Math.round(end.value)
const items = new Array(roundedEnd - roundedStart + 1)
.fill(null)
.map((_, index) => new NumberValue(start.value + index))
.map((_, index) => new NumberValue(roundedStart + index))

return new ListValue(items)
}
Expand All @@ -465,25 +469,39 @@ export class RangeOperator extends Operator {
private assertProperStartType(
start: ValueType,
): asserts start is NumberValue {
if (start instanceof NumberValue) return
if (!(start instanceof NumberValue)) {
throw new RangeStartMustBeNumberError({
resource: {
start,
},
})
}

throw new RangeStartMustBeNumberError({
position: this.tokens[0].position,
resource: {
start,
},
})
if (!Number.isInteger(start.value)) {
throw new RangeStartMustBeIntegerError({
resource: {
start,
},
})
}
}

private assertProperEndType(end: ValueType): asserts end is NumberValue {
if (end instanceof NumberValue) return
if (!(end instanceof NumberValue)) {
throw new RangeEndMustBeNumberError({
resource: {
end,
},
})
}

throw new RangeEndMustBeNumberError({
position: this.tokens[0].position,
resource: {
end,
},
})
if (!Number.isInteger(end.value)) {
throw new RangeEndMustBeIntegerError({
resource: {
end,
},
})
}
}

private assertRangeStartLessThanEnd(start: number, end: number) {
Expand Down
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"test",
"monaco-language-provider"
],
"version": "0.2.0-RC.3",
"version": "0.2.0-RC.4",
"tasks": {
"apply-version": "deno run --allow-read --allow-write apply-version.ts",
"publish": "deno task --recursive test && deno publish --allow-dirty"
Expand Down
2 changes: 1 addition & 1 deletion monaco-language-provider/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
},
"name": "@dalbit-yaksok/monaco-language-provider",
"exports": "./mod.ts",
"version": "0.2.0-RC.3"
"version": "0.2.0-RC.4"
}
2 changes: 1 addition & 1 deletion quickjs/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
"check-deploy": "deno publish --dry-run --allow-dirty",
"test": "deno test --quiet --allow-net --allow-read --parallel & deno lint & deno task check-deploy"
},
"version": "0.2.0-RC.3"
"version": "0.2.0-RC.4"
}
6 changes: 1 addition & 5 deletions runtest.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { yaksok } from '@dalbit-yaksok/core'

await yaksok(`
약속, (10) 말하기
반복
결과: "안녕하세요"

말하기 보여주기
3 ~ 8.3 보여주기
`)
27 changes: 27 additions & 0 deletions test/errors/loop.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
RangeEndMustBeNumberError,
RangeStartMustBeLessThanEndError,
RangeStartMustBeNumberError,
RangeStartMustBeIntegerError,
RangeEndMustBeIntegerError,
TargetIsNotIndexedValueError,
ListIndexMustBeGreaterThan1Error,
} from '../../core/error/index.ts'
Expand Down Expand Up @@ -75,6 +77,31 @@ Deno.test('Range end must be number', async () => {
}
})

Deno.test('Range start must be an integer', async () => {
try {
await yaksok(`1.5 ~ 5`)
unreachable()
} catch (e) {
assertIsError(e, RangeStartMustBeIntegerError)
}

try {
await yaksok(`1.5 ~ 3.2`)
unreachable()
} catch (e) {
assertIsError(e, RangeStartMustBeIntegerError)
}
})

Deno.test('Range end must be an integer', async () => {
try {
await yaksok(`1 ~ 5.5`)
unreachable()
} catch (e) {
assertIsError(e, RangeEndMustBeIntegerError)
}
})

Deno.test('Index set target is must be indexable', async () => {
try {
await yaksok(`목록: 5
Expand Down
Loading