Compare commits

..

6 Commits

Author SHA1 Message Date
117e696430 Merge branch 'nrc-uom-cmds' into franknoirot/tweak-test-nrc-uom-cmds 2025-04-22 14:19:09 -04:00
c18a2223f3 Make useDemoCode use updateModelingState like everyone else 2025-04-22 13:18:22 -04:00
c5492a7937 Tweak test connection step on failing test 2025-04-22 09:52:01 -04:00
19a3c255c3 Convert all lengths to mm for engine calls
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-22 16:04:15 +12:00
f93979c648 Convert all lengths to mm for engine calls
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-22 12:15:28 +12:00
6288ab5f2f Turn on uom checks
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-22 11:12:20 +12:00
176 changed files with 4709 additions and 18600 deletions

View File

@ -19,11 +19,6 @@
"plugin:jsx-a11y/recommended",
"plugin:react-hooks/recommended"
],
"settings": {
"react": {
"version": "detect"
}
},
"rules": {
"no-array-constructor": "off", // This is wrong; use the @typescript-eslint one instead.
"@typescript-eslint/no-array-constructor": "error",
@ -66,7 +61,6 @@
"jsx-a11y/click-events-have-key-events": "off",
"jsx-a11y/no-autofocus": "off",
"jsx-a11y/no-noninteractive-element-interactions": "off",
"react/no-unknown-property": "error",
"no-restricted-globals": [
"error",
{

View File

@ -26,8 +26,8 @@ max_retries=1
# Retry failed tests, doing our own retries because using inbuilt Playwright retries causes connection issues
while [[ $retry -le $max_retries ]]; do
if [[ -f "test-results/.last-run.json" ]]; then
status=$(jq -r '.status' test-results/.last-run.json)
if [[ "$status" == "failed" ]]; then
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
if [[ $failed_tests -gt 0 ]]; then
echo "retried=true" >>$GITHUB_OUTPUT
echo "run playwright with last failed tests and retry $retry"
if [[ "$3" == *ubuntu* ]]; then
@ -56,11 +56,10 @@ done
echo "retried=false" >>$GITHUB_OUTPUT
if [[ -f "test-results/.last-run.json" ]]; then
status=$(jq -r '.status' test-results/.last-run.json)
if [[ "$status" == "failed" ]]; then
# If it still fails after retries, then fail the job
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
if [[ $failed_tests -gt 0 ]]; then
# If it still fails after 3 retries, then fail the job
exit 1
fi
fi
exit 0

View File

@ -225,7 +225,7 @@ jobs:
uses: nick-fields/retry@v3.0.2
with:
shell: bash
command: npm run test:snapshots
command: npm run test:snapshots || true
timeout_minutes: 5
max_attempts: 5
env:
@ -285,7 +285,8 @@ jobs:
# TODO: enable namespace-profile-windows-latest once available
os:
- "runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64"
- namespace-profile-macos-8-cores
# TODO: renable this when macoOS runner seem more stable
# - namespace-profile-macos-6-cores
- windows-latest-8-cores
shardIndex: [1, 2, 3, 4]
shardTotal: [4]
@ -295,7 +296,7 @@ jobs:
isScheduled:
- ${{ github.event_name == 'schedule' }}
exclude:
- os: namespace-profile-macos-8-cores
- os: namespace-profile-macos-6-cores
isScheduled: true
- os: windows-latest-8-cores
isScheduled: true

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -40,7 +40,11 @@ layout: manual
* [`arc`](kcl/arc)
* [`asin`](kcl/asin)
* [`assert`](kcl/assert)
* [`assertIs`](kcl/assertIs)
* [`assertEqual`](kcl/assertEqual)
* [`assertGreaterThan`](kcl/assertGreaterThan)
* [`assertGreaterThanOrEq`](kcl/assertGreaterThanOrEq)
* [`assertLessThan`](kcl/assertLessThan)
* [`assertLessThanOrEq`](kcl/assertLessThanOrEq)
* [`atan`](kcl/atan)
* [`atan2`](kcl/atan2)
* [`bezierCurve`](kcl/bezierCurve)

View File

@ -34,7 +34,7 @@ int(num: number): number
```js
n = int(ceil(5 / 2))
assert(n, isEqualTo = 3, error = "5/2 = 2.5, rounded up makes 3")
assertEqual(n, 3, 0.0001, "5/2 = 2.5, rounded up makes 3")
// Draw n cylinders.
startSketchOn(XZ)
|> circle(center = [0, 0], radius = 2)

View File

@ -21,7 +21,4 @@ once fixed in engine will just start working here with no language changes.
- **Chamfers**: Chamfers cannot intersect, you will get an error. Only simple
chamfer cases work currently.
- **Appearance**: Changing the appearance on a loft does not work.
Changing the appearance on an imported model does not work.
- **CSG Booleans**: Coplanar (bodies that share a plane) unions, subtractions, and intersections are not currently supported.
- **Appearance**: Changing the appearance on a loft does not work.

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@ Extract the provided 2-dimensional sketch's profile's origin value.
```js
profileStart(profile: Sketch): [number]
profileStart(sketch: Sketch): [number]
```
@ -17,7 +17,7 @@ profileStart(profile: Sketch): [number]
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `profile` | [`Sketch`](/docs/kcl/types/Sketch) | Profile whose start is being used | Yes |
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | | Yes |
### Returns

View File

@ -9,7 +9,7 @@ Extract the provided 2-dimensional sketch's profile's origin's 'x' value.
```js
profileStartX(profile: Sketch): number
profileStartX(sketch: Sketch): number
```
@ -17,7 +17,7 @@ profileStartX(profile: Sketch): number
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `profile` | [`Sketch`](/docs/kcl/types/Sketch) | Profile whose start is being used | Yes |
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | | Yes |
### Returns

View File

@ -9,7 +9,7 @@ Extract the provided 2-dimensional sketch's profile's origin's 'y' value.
```js
profileStartY(profile: Sketch): number
profileStartY(sketch: Sketch): number
```
@ -17,7 +17,7 @@ profileStartY(profile: Sketch): number
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `profile` | [`Sketch`](/docs/kcl/types/Sketch) | Profile whose start is being used | Yes |
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | | Yes |
### Returns

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -29,11 +29,11 @@ test.describe('Electron app header tests', () => {
test(
'User settings has correct shortcut',
{ tag: '@electron' },
async ({ page, toolbar }, testInfo) => {
async ({ page }, testInfo) => {
await page.setBodyDimensions({ width: 1200, height: 500 })
// Open the user sidebar menu.
await toolbar.userSidebarButton.click()
await page.getByTestId('user-sidebar-toggle').click()
// No space after "User settings" since it's textContent.
const text =

View File

@ -1,53 +0,0 @@
import { expect, test } from '@e2e/playwright/zoo-test'
// test file is for testing auth functionality
test.describe('Authentication tests', () => {
test(
`The user can sign out and back in`,
{ tag: ['@electron'] },
async ({ page, homePage, signInPage, toolbar, tronApp }) => {
if (!tronApp) {
fail()
}
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.projectSection.waitFor()
await test.step('Click on sign out and expect sign in page', async () => {
await toolbar.userSidebarButton.click()
await toolbar.signOutButton.click()
await expect(signInPage.signInButton).toBeVisible()
})
await test.step('Click on sign in and cancel, click again and expect different code', async () => {
await signInPage.signInButton.click()
await expect(signInPage.userCode).toBeVisible()
const firstUserCode = await signInPage.userCode.textContent()
await signInPage.cancelSignInButton.click()
await expect(signInPage.signInButton).toBeVisible()
await signInPage.signInButton.click()
await expect(signInPage.userCode).toBeVisible()
const secondUserCode = await signInPage.userCode.textContent()
expect(secondUserCode).not.toEqual(firstUserCode)
})
await test.step('Press back button and remain on home page', async () => {
await page.goBack()
await expect(homePage.projectSection).not.toBeVisible()
await expect(signInPage.signInButton).toBeVisible()
})
await test.step('Sign in, activate, and expect home page', async () => {
await signInPage.signInButton.click()
await expect(signInPage.userCode).toBeVisible()
const userCode = await signInPage.userCode.textContent()
expect(userCode).not.toBeNull()
await signInPage.verifyAndConfirmAuth(userCode!)
// Longer timeout than usual here for the wait on home page
await expect(homePage.projectSection).toBeVisible({ timeout: 10000 })
})
}
)
})

View File

@ -18,7 +18,6 @@ import type { Settings } from '@rust/kcl-lib/bindings/Settings'
import { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
import { EditorFixture } from '@e2e/playwright/fixtures/editorFixture'
import { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture'
import { SignInPageFixture } from '@e2e/playwright/fixtures/signInPageFixture'
import { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
import { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
@ -67,7 +66,6 @@ export interface Fixtures {
toolbar: ToolbarFixture
scene: SceneFixture
homePage: HomePageFixture
signInPage: SignInPageFixture
}
export class ElectronZoo {
@ -389,9 +387,6 @@ const fixturesBasedOnProcessEnvPlatform = {
homePage: async ({ page }: { page: Page }, use: FnUse) => {
await use(new HomePageFixture(page))
},
signInPage: async ({ page }: { page: Page }, use: FnUse) => {
await use(new SignInPageFixture(page))
},
}
if (process.env.PLATFORM === 'web') {

View File

@ -1,48 +0,0 @@
import type { Locator, Page } from '@playwright/test'
import { secrets } from '@e2e/playwright/secrets'
export class SignInPageFixture {
public page: Page
signInButton!: Locator
cancelSignInButton!: Locator
userCode!: Locator
apiBaseUrl!: string
constructor(page: Page) {
this.page = page
this.signInButton = this.page.getByTestId('sign-in-button')
this.cancelSignInButton = this.page.getByTestId('cancel-sign-in-button')
this.userCode = this.page.getByTestId('sign-in-user-code')
// TODO: set this thru env var
this.apiBaseUrl = 'https://api.dev.zoo.dev'
}
async verifyAndConfirmAuth(userCode: string) {
// Device flow: stolen from the tauri days
// https://github.com/KittyCAD/modeling-app/blob/d916c7987452e480719004e6d11fd2e595c7d0eb/e2e/tauri/specs/app.spec.ts#L19
const headers = {
Authorization: `Bearer ${secrets.token}`,
Accept: 'application/json',
'Content-Type': 'application/json',
}
const verifyUrl = `${this.apiBaseUrl}/oauth2/device/verify?user_code=${userCode}`
console.log(`GET ${verifyUrl}`)
const vr = await fetch(verifyUrl, { headers })
console.log(vr.status)
// Device flow: confirm
const confirmUrl = `${this.apiBaseUrl}/oauth2/device/confirm`
const data = JSON.stringify({ user_code: userCode })
console.log(`POST ${confirmUrl} ${data}`)
const cr = await fetch(confirmUrl, {
headers,
method: 'POST',
body: data,
})
console.log(cr.status)
}
}

View File

@ -46,9 +46,6 @@ export class ToolbarFixture {
gizmo!: Locator
gizmoDisabled!: Locator
loadButton!: Locator
/** User button for the user sidebar menu */
userSidebarButton!: Locator
signOutButton!: Locator
constructor(page: Page) {
this.page = page
@ -85,9 +82,6 @@ export class ToolbarFixture {
// element or two different elements can represent these states.
this.gizmo = page.getByTestId('gizmo')
this.gizmoDisabled = page.getByTestId('gizmo-disabled')
this.userSidebarButton = page.getByTestId('user-sidebar-toggle')
this.signOutButton = page.getByTestId('user-sidebar-sign-out')
}
get logoLink() {

View File

@ -1,25 +1,6 @@
import type {
Reporter,
TestCase,
TestResult,
FullResult,
} from '@playwright/test/reporter'
import type { Reporter, TestCase, TestResult } from '@playwright/test/reporter'
class MyAPIReporter implements Reporter {
private pendingRequests: Promise<void>[] = []
private allResults: Record<string, any>[] = []
private blockingResults: Record<string, any>[] = []
async onEnd(result: FullResult): Promise<void> {
await Promise.all(this.pendingRequests)
if (this.allResults.length > 0 && this.blockingResults.length === 0) {
result.status = 'passed'
if (!process.env.CI) {
console.error('TAB API - Marked failures as non-blocking')
}
}
}
onTestEnd(test: TestCase, result: TestResult): void {
if (!process.env.TAB_API_URL || !process.env.TAB_API_KEY) {
return
@ -39,7 +20,6 @@ class MyAPIReporter implements Reporter {
platform: process.env.RUNNER_OS || process.platform,
// Extra test and result data
annotations: test.annotations.map((a) => a.type), // e.g. 'fail' or 'fixme'
id: test.id, // computed file/test/project ID used for reruns
retry: result.retry,
tags: test.tags, // e.g. '@snapshot' or '@skipWin'
// Extra environment variables
@ -55,7 +35,7 @@ class MyAPIReporter implements Reporter {
RUNNER_ARCH: process.env.RUNNER_ARCH || null,
}
const request = (async () => {
void (async () => {
try {
const response = await fetch(`${process.env.TAB_API_URL}/api/results`, {
method: 'POST',
@ -66,27 +46,18 @@ class MyAPIReporter implements Reporter {
body: JSON.stringify(payload),
})
if (response.ok) {
const result = await response.json()
this.allResults.push(result)
if (result.block) {
this.blockingResults.push(result)
}
} else {
const error = await response.json()
if (!process.env.CI) {
console.error('TAB API - Failed to send test result:', error)
}
if (!response.ok && !process.env.CI) {
console.error(
'TAB API - Failed to send test result:',
await response.text()
)
}
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
} catch {
if (!process.env.CI) {
console.error('TAB API - Unable to send test result:', message)
console.error('TAB API - Unable to send test result')
}
}
})()
this.pendingRequests.push(request)
}
}

View File

@ -409,7 +409,11 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
)
.toBe(true)
})
test('Home.Help.Report a bug', async ({ tronApp, cmdBar, page }) => {
test('Home.Help.Refresh and report a bug', async ({
tronApp,
cmdBar,
page,
}) => {
if (!tronApp) fail()
// Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
@ -420,8 +424,9 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
if (!app || !app.applicationMenu) {
return false
}
const menu =
app.applicationMenu.getMenuItemById('Help.Report a bug')
const menu = app.applicationMenu.getMenuItemById(
'Help.Refresh and report a bug'
)
if (!menu) return false
menu.click()
return true
@ -2286,7 +2291,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
if (!menu) fail()
})
})
test('Modeling.Help.Report a bug', async ({
test('Modeling.Help.Refresh and report a bug', async ({
tronApp,
cmdBar,
page,
@ -2310,8 +2315,9 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
async () =>
await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) return false
const menu =
app.applicationMenu.getMenuItemById('Help.Report a bug')
const menu = app.applicationMenu.getMenuItemById(
'Help.Refresh and report a bug'
)
if (!menu) return false
menu.click()
return true

View File

@ -331,7 +331,6 @@ test.describe('Onboarding tests', () => {
test('Avatar text updates depending on image load success', async ({
context,
page,
toolbar,
homePage,
tronApp,
}) => {
@ -363,7 +362,7 @@ test.describe('Onboarding tests', () => {
await homePage.goToModelingScene()
// Test that the text in this step is correct
const avatarLocator = toolbar.userSidebarButton.locator('img')
const avatarLocator = page.getByTestId('user-sidebar-toggle').locator('img')
const onboardingOverlayLocator = page
.getByTestId('onboarding-content')
.locator('div')
@ -405,7 +404,6 @@ test.describe('Onboarding tests', () => {
test("Avatar text doesn't mention avatar when no avatar", async ({
context,
page,
toolbar,
homePage,
tronApp,
}) => {
@ -437,7 +435,7 @@ test.describe('Onboarding tests', () => {
await homePage.goToModelingScene()
// Test that the text in this step is correct
const sidebar = toolbar.userSidebarButton
const sidebar = page.getByTestId('user-sidebar-toggle')
const avatar = sidebar.locator('img')
const onboardingOverlayLocator = page
.getByTestId('onboarding-content')
@ -466,7 +464,6 @@ test.describe('Onboarding tests', () => {
test('Restarting onboarding on desktop takes one attempt', async ({
context,
page,
toolbar,
tronApp,
}) => {
test.fixme(orRunWhenFullSuiteEnabled())
@ -505,7 +502,7 @@ test('Restarting onboarding on desktop takes one attempt', async ({
.filter({ hasText: 'Tutorial Project 00' })
const tutorialModalText = page.getByText('Welcome to Design Studio!')
const tutorialDismissButton = page.getByRole('button', { name: 'Dismiss' })
const userMenuButton = toolbar.userSidebarButton
const userMenuButton = page.getByTestId('user-sidebar-toggle')
const userMenuSettingsButton = page.getByRole('button', {
name: 'User settings',
})

View File

@ -400,6 +400,11 @@ test(
await expect(page.getByText('broken-code')).toBeVisible()
await page.getByText('broken-code').click()
// Gotcha: You can not use scene.settled() since the KCL code is going to fail
await expect(
page.getByTestId('model-state-indicator-playing')
).toBeAttached()
// Gotcha: Scroll to the text content in code mirror because CodeMirror lazy loads DOM content
await editor.scrollToText(
"|> line(end = [0, wallMountL], tag = 'outerEdge')"
@ -774,9 +779,7 @@ test.describe(`Project management commands`, () => {
// Constants and locators
const projectHomeLink = page.getByTestId('project-link')
const commandButton = page.getByRole('button', { name: 'Commands' })
const commandOption = page.getByRole('option', {
name: 'rename project',
})
const commandOption = page.getByRole('option', { name: 'rename project' })
const projectNameOption = page.getByRole('option', { name: projectName })
const projectRenamedName = `untitled`
// const projectMenuButton = page.getByTestId('project-sidebar-toggle')
@ -836,9 +839,7 @@ test.describe(`Project management commands`, () => {
// Constants and locators
const projectHomeLink = page.getByTestId('project-link')
const commandButton = page.getByRole('button', { name: 'Commands' })
const commandOption = page.getByRole('option', {
name: 'delete project',
})
const commandOption = page.getByRole('option', { name: 'delete project' })
const projectNameOption = page.getByRole('option', { name: projectName })
const commandWarning = page.getByText('Are you sure you want to delete?')
const commandSubmitButton = page.getByRole('button', {
@ -890,9 +891,7 @@ test.describe(`Project management commands`, () => {
// Constants and locators
const projectHomeLink = page.getByTestId('project-link')
const commandButton = page.getByRole('button', { name: 'Commands' })
const commandOption = page.getByRole('option', {
name: 'rename project',
})
const commandOption = page.getByRole('option', { name: 'rename project' })
const projectNameOption = page.getByRole('option', { name: projectName })
const projectRenamedName = `untitled`
const commandContinueButton = page.getByRole('button', {
@ -948,9 +947,7 @@ test.describe(`Project management commands`, () => {
// Constants and locators
const projectHomeLink = page.getByTestId('project-link')
const commandButton = page.getByRole('button', { name: 'Commands' })
const commandOption = page.getByRole('option', {
name: 'delete project',
})
const commandOption = page.getByRole('option', { name: 'delete project' })
const projectNameOption = page.getByRole('option', { name: projectName })
const commandWarning = page.getByText('Are you sure you want to delete?')
const commandSubmitButton = page.getByRole('button', {
@ -1965,13 +1962,13 @@ test(
test(
'Settings persist across restarts',
{ tag: '@electron' },
async ({ page, toolbar }, testInfo) => {
async ({ page, scene, cmdBar }, testInfo) => {
await test.step('We can change a user setting like theme', async () => {
await page.setBodyDimensions({ width: 1200, height: 500 })
page.on('console', console.log)
await toolbar.userSidebarButton.click()
await page.getByTestId('user-sidebar-toggle').click()
await page.getByTestId('user-settings').click()
@ -1998,7 +1995,7 @@ test(
test(
'Original project name persist after onboarding',
{ tag: '@electron' },
async ({ page, toolbar }, testInfo) => {
async ({ page }, testInfo) => {
test.fixme(orRunWhenFullSuiteEnabled())
await page.setBodyDimensions({ width: 1200, height: 500 })
@ -2010,7 +2007,7 @@ test(
})
await test.step('Should go through onboarding', async () => {
await toolbar.userSidebarButton.click()
await page.getByTestId('user-sidebar-toggle').click()
await page.getByTestId('user-settings').click()
await page.getByRole('button', { name: 'Replay Onboarding' }).click()

View File

@ -1,3 +1,4 @@
import { orRunWhenFullSuiteEnabled } from '@e2e/playwright/test-utils'
import { expect, test } from '@e2e/playwright/zoo-test'
/* eslint-disable jest/no-conditional-expect */
@ -50,6 +51,7 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
page,
scene,
}) => {
test.fixme(orRunWhenFullSuiteEnabled())
await context.addInitScript((file) => {
localStorage.setItem('persistCode', file)
}, file)
@ -198,6 +200,7 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
page,
scene,
}) => {
test.fixme(orRunWhenFullSuiteEnabled())
const body1CapCoords = { x: 571, y: 311 }
await context.addInitScript((file) => {
@ -257,6 +260,7 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
page,
scene,
}) => {
test.fixme(orRunWhenFullSuiteEnabled())
const body1CapCoords = { x: 571, y: 311 }
const body2WallCoords = { x: 620, y: 152 }
const [clickBody1Cap] = scene.makeMouseHelpers(

View File

@ -3295,7 +3295,7 @@ profile003 = startProfileAt([-201.08, 254.17], sketch002)
)
await editor.expectState({
activeLines: [],
diagnostics: ['`badBadBadFn`isnotdefined'],
diagnostics: ['memoryitemkey`badBadBadFn`isnotdefined'],
highlightedCode: '',
})
await expect(

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 63 KiB

View File

@ -36,6 +36,7 @@ export const headerMasks = (page: Page) => [
]
export const networkingMasks = (page: Page) => [
page.getByTestId('model-state-indicator'),
page.getByTestId('network-toggle'),
]
@ -84,6 +85,12 @@ async function waitForPageLoadWithRetry(page: Page) {
await expect(async () => {
await page.goto('/')
const errorMessage = 'App failed to load - 🔃 Retrying ...'
await expect(
page.getByTestId('model-state-indicator-playing'),
errorMessage
).toBeAttached({
timeout: 20_000,
})
await expect(
page.getByRole('button', { name: 'sketch Start Sketch' }),
@ -96,6 +103,11 @@ async function waitForPageLoadWithRetry(page: Page) {
// lee: This needs to be replaced by scene.settled() eventually.
async function waitForPageLoad(page: Page) {
// wait for all spinners to be gone
await expect(page.getByTestId('model-state-indicator-playing')).toBeVisible({
timeout: 20_000,
})
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeEnabled({
timeout: 20_000,
})

View File

@ -181,6 +181,7 @@ test.describe('Testing settings', () => {
})
test('Project and user settings can be reset', async ({ page, homePage }) => {
test.fixme(orRunWhenFullSuiteEnabled())
const u = await getUtils(page)
await test.step(`Setup`, async () => {
await page.setBodyDimensions({ width: 1200, height: 500 })

View File

@ -25,10 +25,10 @@ shelfMountingHolePlacementOffset = shelfMountingHoleDiameter * 1.5
wallMountingHolePlacementOffset = wallMountingHoleDiameter * 1.5
// Add checks to ensure bracket is possible. These make sure that there is adequate distance between holes and edges.
assert(wallMountLength, isGreaterThanOrEqual = wallMountingHoleDiameter * 3, error = "Holes not possible. Either decrease hole diameter or increase wallMountLength")
assert(shelfMountLength, isGreaterThanOrEqual = shelfMountingHoleDiameter * 5.5, error = "wallMountLength must be longer for hole sizes to work. Either decrease mounting hole diameters or increase shelfMountLength")
assert(width, isGreaterThanOrEqual = shelfMountingHoleDiameter * 5.5, error = "Holes not possible. Either decrease hole diameter or increase width")
assert(width, isGreaterThanOrEqual = wallMountingHoleDiameter * 5.5, error = "Holes not possible. Either decrease hole diameter or increase width")
assertGreaterThanOrEq(wallMountLength, wallMountingHoleDiameter * 3, "Holes not possible. Either decrease hole diameter or increase wallMountLength")
assertGreaterThanOrEq(shelfMountLength, shelfMountingHoleDiameter * 5.5, "wallMountLength must be longer for hole sizes to work. Either decrease mounting hole diameters or increase shelfMountLength")
assertGreaterThanOrEq(width, shelfMountingHoleDiameter * 5.5, "Holes not possible. Either decrease hole diameter or increase width")
assertGreaterThanOrEq(width, wallMountingHoleDiameter * 5.5, "Holes not possible. Either decrease hole diameter or increase width")
// Create the body of the bracket
bracketBody = startSketchOn(XZ)

View File

@ -18,7 +18,7 @@ topTotalThickness = totalThickness - (bottomThickness + baseThickness)
nHoles = 4
// Add assertion so nHoles are always greater than 1
assert(nHoles, isGreaterThan = 1, error = "nHoles must be greater than 1")
assertGreaterThan(nHoles, 1, "nHoles must be greater than 1")
// Create the circular pattern for the mounting holes
circles = startSketchOn(XY)

View File

@ -22,8 +22,8 @@ lSegments = totalLength / lbumps
wSegments = totalWidth / wbumps
// Add assertions to ensure that the number of bumps are greater than 1
assert(lbumps, isGreaterThan = 1, error = "lbumps must be greater than 1")
assert(wbumps, isGreaterThan = 1, error = "wbumps must be greater than 1")
assertGreaterThan(lbumps, 1, "lbumps must be greater than 1")
assertGreaterThan(wbumps, 1, "wbumps must be greater than 1")
// Make the base
base = startSketchOn(XY)

20
rust/Cargo.lock generated
View File

@ -1792,7 +1792,7 @@ dependencies = [
[[package]]
name = "kcl-bumper"
version = "0.1.63"
version = "0.1.62"
dependencies = [
"anyhow",
"clap",
@ -1803,7 +1803,7 @@ dependencies = [
[[package]]
name = "kcl-derive-docs"
version = "0.1.63"
version = "0.1.62"
dependencies = [
"Inflector",
"anyhow",
@ -1822,7 +1822,7 @@ dependencies = [
[[package]]
name = "kcl-directory-test-macro"
version = "0.1.63"
version = "0.1.62"
dependencies = [
"proc-macro2",
"quote",
@ -1831,7 +1831,7 @@ dependencies = [
[[package]]
name = "kcl-language-server"
version = "0.2.63"
version = "0.2.62"
dependencies = [
"anyhow",
"clap",
@ -1852,7 +1852,7 @@ dependencies = [
[[package]]
name = "kcl-language-server-release"
version = "0.1.63"
version = "0.1.62"
dependencies = [
"anyhow",
"clap",
@ -1872,7 +1872,7 @@ dependencies = [
[[package]]
name = "kcl-lib"
version = "0.2.63"
version = "0.2.62"
dependencies = [
"anyhow",
"approx 0.5.1",
@ -1943,7 +1943,7 @@ dependencies = [
[[package]]
name = "kcl-python-bindings"
version = "0.3.63"
version = "0.3.62"
dependencies = [
"anyhow",
"kcl-lib",
@ -1958,7 +1958,7 @@ dependencies = [
[[package]]
name = "kcl-test-server"
version = "0.1.63"
version = "0.1.62"
dependencies = [
"anyhow",
"hyper 0.14.32",
@ -1971,7 +1971,7 @@ dependencies = [
[[package]]
name = "kcl-to-core"
version = "0.1.63"
version = "0.1.62"
dependencies = [
"anyhow",
"async-trait",
@ -1985,7 +1985,7 @@ dependencies = [
[[package]]
name = "kcl-wasm-lib"
version = "0.1.63"
version = "0.1.62"
dependencies = [
"bson",
"console_error_panic_hook",

View File

@ -1,7 +1,7 @@
[package]
name = "kcl-bumper"
version = "0.1.63"
version = "0.1.62"
edition = "2021"
repository = "https://github.com/KittyCAD/modeling-api"
rust-version = "1.76"

View File

@ -1,7 +1,7 @@
[package]
name = "kcl-derive-docs"
description = "A tool for generating documentation from Rust derive macros"
version = "0.1.63"
version = "0.1.62"
edition = "2021"
license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app"

View File

@ -1,7 +1,7 @@
[package]
name = "kcl-directory-test-macro"
description = "A tool for generating tests from a directory of kcl files"
version = "0.1.63"
version = "0.1.62"
edition = "2021"
license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app"

View File

@ -1,6 +1,6 @@
[package]
name = "kcl-language-server-release"
version = "0.1.63"
version = "0.1.62"
edition = "2021"
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
publish = false

View File

@ -2,7 +2,7 @@
name = "kcl-language-server"
description = "A language server for KCL."
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
version = "0.2.63"
version = "0.2.62"
edition = "2021"
license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,7 +1,7 @@
[package]
name = "kcl-lib"
description = "KittyCAD Language implementation and tools"
version = "0.2.63"
version = "0.2.62"
edition = "2021"
license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app"

View File

@ -19,30 +19,6 @@ We've built a lot of tooling to make contributing to KCL easier. If you are inte
11. Run `just redo-kcl-stdlib-docs` to generate new Markdown documentation for your function that will be used [to generate docs on our website](https://zoo.dev/docs/kcl).
12. Create a PR in GitHub.
## Making a Simulation Test
If you have KCL code that you want to test, simulation tests are the preferred way to do that.
Make a new sim test. Replace `foo_bar` with the snake case name of your test. The name needs to be unique.
```shell
just new-sim-test foo_bar
```
It will show the commands it ran, including the path to a new file `foo_bar/input.kcl`. Edit that with your KCL. If you need additional KCL files to import, include them in this directory.
Then run it.
```shell
just overwrite-sim-test foo_bar
```
The above should create a bunch of output files in the same directory.
Make sure you actually look at them. Specifically, if there's an `execution_error.snap`, it means the execution failed. Depending on the test, this may be what you expect. But if it's not, delete the snap file and run it again.
When it looks good, commit all the files, including `input.kcl`, generated output files in the test directory, and changes to `simulation_tests.rs`.
## Bumping the version
If you bump the version of kcl-lib and push it to crates, be sure to update the repos we own that use it as well. These are:

View File

@ -4,7 +4,7 @@ const arr = [0, 0, 0, 10]
const i = 3
const ten = arr[i]
assert(ten, isEqualTo = 10, error = "oops")
assertEqual(ten, 10, 0.000001, "oops")
const p = "foo"
const obj = {
@ -13,4 +13,4 @@ const obj = {
}
const one = obj[p]
assert(one, isEqualTo = 1, error = "oops")
assertEqual(one, 1, 0.0000001, "oops")

View File

@ -1,25 +1,25 @@
import identity from "identity.kcl"
answer = identity(42)
assert(answer, isEqualTo = 42, error = "identity")
assertEqual(answer, 42, 0.0001, "identity")
import identity as id from "identity.kcl"
answer43 = id(43)
assert(answer43, isEqualTo = 43, error = "identity")
assertEqual(answer43, 43, 0.0001, "identity")
import increment, decrement from "numbers.kcl"
answer3 = increment(2)
assert(answer3, isEqualTo = 3, error = "increment")
assertEqual(answer3, 3, 0.0001, "increment")
answer5 = decrement(6)
assert(answer5, isEqualTo = 5, error = "decrement")
assertEqual(answer5, 5, 0.0001, "decrement")
import increment as inc, decrement as dec from "numbers.kcl"
answer4 = inc(3)
assert(answer4, isEqualTo = 4, error = "inc")
assertEqual(answer4, 4, 0.0001, "inc")
answer6 = dec(7)
assert(answer6, isEqualTo = 6, error = "dec")
assertEqual(answer6, 6, 0.0001, "dec")

View File

@ -0,0 +1,52 @@
wall_thickness = 0.125
back_walls_width = 2
front_walls_width = 2.5
height = 5.5
filletRadius = 0.050
back_length = 7
exit_height = 1
front_length = 6
Fx = 0.5
Fy = 0.5
sketch001 = startSketchOn('-YZ')
|> startProfileAt([back_walls_width / 2, 0], %)
|> xLine(length = wall_thickness / 2)
|> angledLine(angle = 45, endAbsoluteX = back_walls_width, tag = $seg01)
|> yLine(endAbsolute = height)
|> xLine(length = -wall_thickness)
|> yLine(endAbsolute = segEndY(seg01))
|> angledLine(angle = 45, endAbsoluteX = back_walls_width / 2 + wall_thickness / 2)
|> xLine(length = -wall_thickness)
|> angledLine(angle = 180 - 45, endAbsoluteX = wall_thickness)
|> yLine(endAbsolute = height)
|> xLine(endAbsolute = 0)
|> yLine(endAbsolute = segEndY(seg01))
|> angledLine(angle = 180 - 45, endAbsoluteY = 0)
|> close()
part001 = revolve({
angle: 90,
axis: {
custom: {
axis: [1.0, 0.0],
origin: [0.0, height + .0000001]
}
}
}, sketch001)
sketch002 = startSketchOn('-YZ')
|> startProfileAt([back_walls_width / 2, 0], %)
|> xLine(length = wall_thickness / 2)
|> angledLine(angle = 45, endAbsoluteX = back_walls_width, tag = $seg02)
|> yLine(endAbsolute = height)
|> xLine(length = -wall_thickness)
|> yLine(endAbsolute = segEndY(seg01))
|> angledLine(angle = 45, endAbsoluteX = back_walls_width / 2 + wall_thickness / 2)
|> xLine(length = -wall_thickness)
|> angledLine(angle = 180 - 45, endAbsoluteX = wall_thickness)
|> yLine(endAbsolute = height)
|> xLine(endAbsolute = 0)
|> yLine(endAbsolute = segEndY(seg02))
|> angledLine(angle = 180 - 45, endAbsoluteY = 0)
|> close()
|> extrude(length = back_length - height)

View File

@ -365,7 +365,7 @@ impl ProgramMemory {
}
Err(KclError::UndefinedValue(KclErrorDetails {
message: format!("`{}` is not defined", var),
message: format!("memory item key `{}` is not defined", var),
source_ranges: vec![source_range],
}))
}
@ -486,7 +486,7 @@ impl ProgramMemory {
}
Err(KclError::UndefinedValue(KclErrorDetails {
message: format!("`{}` is not defined", var),
message: format!("memory item key `{}` is not defined", var),
source_ranges: vec![],
}))
}

View File

@ -95,7 +95,8 @@ pub struct DefaultPlanes {
pub struct TagIdentifier {
pub value: String,
// Multi-version representation of info about the tag. Kept ordered. The usize is the epoch at which the info
// was written.
// was written. Note that there might be multiple versions of tag info from the same epoch, the version with
// the higher index will be the most recent.
#[serde(skip)]
pub info: Vec<(usize, TagEngineInfo)>,
#[serde(skip)]
@ -122,16 +123,10 @@ impl TagIdentifier {
/// Add info from a different instance of this tag.
pub fn merge_info(&mut self, other: &TagIdentifier) {
assert_eq!(&self.value, &other.value);
for (oe, ot) in &other.info {
if let Some((e, t)) = self.info.last_mut() {
// If there is newer info, then skip this iteration.
if *e > *oe {
continue;
}
// If we're in the same epoch, then overwrite.
if e == oe {
*t = ot.clone();
continue;
'new_info: for (oe, ot) in &other.info {
for (e, _) in &self.info {
if e > oe {
continue 'new_info;
}
}
self.info.push((*oe, ot.clone()));
@ -1590,7 +1585,7 @@ const answer = returnX()"#;
assert_eq!(
err,
KclError::UndefinedValue(KclErrorDetails {
message: "`x` is not defined".to_owned(),
message: "memory item key `x` is not defined".to_owned(),
source_ranges: vec![
SourceRange::new(64, 65, ModuleId::default()),
SourceRange::new(97, 106, ModuleId::default())
@ -1674,7 +1669,7 @@ let shape = layer() |> patternTransform(instances = 10, transform = transform)
assert_eq!(
err,
KclError::UndefinedValue(KclErrorDetails {
message: "`x` is not defined".to_owned(),
message: "memory item key `x` is not defined".to_owned(),
source_ranges: vec![SourceRange::new(80, 81, ModuleId::default())],
}),
);
@ -1820,21 +1815,33 @@ const bracket = startSketchOn(XY)
parse_execute(ast).await.unwrap();
}
#[tokio::test(flavor = "multi_thread")]
async fn test_bad_arg_count_std() {
let ast = "startSketchOn(XY)
|> startProfileAt([0, 0], %)
|> profileStartX()";
assert!(parse_execute(ast)
.await
.unwrap_err()
.message()
.contains("Expected a sketch argument"));
}
#[tokio::test(flavor = "multi_thread")]
async fn test_unary_operator_not_succeeds() {
let ast = r#"
fn returnTrue() { return !false }
t = true
f = false
notTrue = !t
notFalse = !f
c = !!true
d = !returnTrue()
fn returnTrue = () => { return !false }
const t = true
const f = false
let notTrue = !t
let notFalse = !f
let c = !!true
let d = !returnTrue()
assertIs(!false, error = "expected to pass")
assert(!false, "expected to pass")
fn check = (x) => {
assertIs(!x, error = "expected argument to be false")
assert(!x, "expected argument to be false")
return true
}
check(false)

View File

@ -1628,12 +1628,6 @@ fn function_body(i: &mut TokenSlice) -> PResult<Node<Program>> {
}
handle_pending_non_code!(attr);
if attr.is_inner() {
if !body.is_empty() {
ParseContext::warn(CompilationError::err(
attr.as_source_range(),
"Named attributes should appear before any declarations or expressions.\n\nBecause named attributes apply to the whole function or module, including code written before them, it can be confusing for readers to not have these attributes at the top of code blocks.",
));
}
inner_attrs.push(attr);
} else {
pending_attrs.push(attr);
@ -2074,7 +2068,6 @@ fn possible_operands(i: &mut TokenSlice) -> PResult<Expr> {
member_expression.map(Box::new).map(Expr::MemberExpression),
literal.map(Expr::Literal),
fn_call.map(Box::new).map(Expr::CallExpression),
fn_call_kw.map(Box::new).map(Expr::CallExpressionKw),
name.map(Box::new).map(Expr::Name),
binary_expr_in_parens.map(Box::new).map(Expr::BinaryExpression),
unnecessarily_bracketed,
@ -3255,14 +3248,6 @@ mod tests {
assert_eq!(err.message, "Unexpected end of file. The compiler expected )");
}
#[test]
fn kw_call_as_operand() {
let tokens = crate::parsing::token::lex("f(x = 1)", ModuleId::default()).unwrap();
let tokens = tokens.as_slice();
let op = operand.parse(tokens).unwrap();
println!("{op:#?}");
}
#[test]
fn weird_program_just_a_pipe() {
let tokens = crate::parsing::token::lex("|", ModuleId::default()).unwrap();
@ -4527,15 +4512,6 @@ export fn cos(num: number(rad)): number(_) {}"#;
assert_eq!(errs.len(), 1, "{errs:#?}");
}
#[test]
fn warn_late_settings() {
let some_program_string = r#"foo = 42
@settings(defaultLengthUnit = mm)
"#;
let (_, errs) = assert_no_err(some_program_string);
assert_eq!(errs.len(), 1, "{errs:#?}");
}
#[test]
fn fn_decl_uom_ty() {
let some_program_string = r#"fn foo(x: number(mm)): number(_) { return 1 }"#;
@ -5398,7 +5374,6 @@ my14 = 4 ^ 2 - 3 ^ 2 * 2
bar = x,
)"#
);
snapshot_test!(kw_function_in_binary_op, r#"val = f(x = 1) + 1"#);
}
#[allow(unused)]

View File

@ -1,99 +0,0 @@
---
source: kcl-lib/src/parsing/parser.rs
expression: actual
---
{
"body": [
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 18,
"id": {
"commentStart": 0,
"end": 3,
"name": "val",
"start": 0,
"type": "Identifier"
},
"init": {
"commentStart": 6,
"end": 18,
"left": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 8,
"end": 9,
"name": "x",
"start": 8,
"type": "Identifier"
},
"arg": {
"commentStart": 12,
"end": 13,
"raw": "1",
"start": 12,
"type": "Literal",
"type": "Literal",
"value": {
"value": 1.0,
"suffix": "None"
}
}
}
],
"callee": {
"abs_path": false,
"commentStart": 6,
"end": 7,
"name": {
"commentStart": 6,
"end": 7,
"name": "f",
"start": 6,
"type": "Identifier"
},
"path": [],
"start": 6,
"type": "Name"
},
"commentStart": 6,
"end": 14,
"start": 6,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
},
"operator": "+",
"right": {
"commentStart": 17,
"end": 18,
"raw": "1",
"start": 17,
"type": "Literal",
"type": "Literal",
"value": {
"value": 1.0,
"suffix": "None"
}
},
"start": 6,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
"start": 0,
"type": "VariableDeclarator"
},
"end": 18,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"commentStart": 0,
"end": 18,
"start": 0
}

View File

@ -2600,24 +2600,3 @@ mod import_async {
super::execute(TEST_NAME, true).await
}
}
mod loop_tag {
const TEST_NAME: &str = "loop_tag";
/// Test parsing KCL.
#[test]
fn parse() {
super::parse(TEST_NAME)
}
/// Test that parsing and unparsing KCL produces the original KCL input.
#[tokio::test(flavor = "multi_thread")]
async fn unparse() {
super::unparse(TEST_NAME).await
}
/// Test that KCL is executed correctly.
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_execute() {
super::execute(TEST_NAME, true).await
}
}

View File

@ -675,6 +675,28 @@ impl Args {
Ok((sketches, sketch))
}
pub(crate) fn get_sketch(&self, exec_state: &mut ExecState) -> Result<Sketch, KclError> {
let Some(arg0) = self.args.first() else {
return Err(KclError::Semantic(KclErrorDetails {
message: "Expected a sketch argument".to_owned(),
source_ranges: vec![self.source_range],
}));
};
let sarg = arg0
.value
.coerce(&RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)
.map_err(|_| {
KclError::Type(KclErrorDetails {
message: format!("Expected a sketch, found {}", arg0.value.human_friendly_type()),
source_ranges: vec![self.source_range],
})
})?;
match sarg {
KclValue::Sketch { value } => Ok(*value),
_ => unreachable!(),
}
}
pub(crate) fn get_data<'a, T>(&'a self) -> Result<T, KclError>
where
T: FromArgs<'a>,
@ -686,6 +708,49 @@ impl Args {
FromArgs::from_args(self, 0)
}
pub(crate) fn get_length_and_solid(&self, exec_state: &mut ExecState) -> Result<(TyF64, Box<Solid>), KclError> {
let Some(arg0) = self.args.first() else {
return Err(KclError::Semantic(KclErrorDetails {
message: "Expected a `number(Length)` for first argument".to_owned(),
source_ranges: vec![self.source_range],
}));
};
let val0 = arg0.value.coerce(&RuntimeType::length(), exec_state).map_err(|_| {
KclError::Type(KclErrorDetails {
message: format!(
"Expected a `number(Length)` for first argument, found {}",
arg0.value.human_friendly_type()
),
source_ranges: vec![self.source_range],
})
})?;
let data = TyF64::from_kcl_val(&val0).unwrap();
let Some(arg1) = self.args.get(1) else {
return Err(KclError::Semantic(KclErrorDetails {
message: "Expected a solid for second argument".to_owned(),
source_ranges: vec![self.source_range],
}));
};
let sarg = arg1
.value
.coerce(&RuntimeType::Primitive(PrimitiveType::Solid), exec_state)
.map_err(|_| {
KclError::Type(KclErrorDetails {
message: format!(
"Expected a solid for second argument, found {}",
arg1.value.human_friendly_type()
),
source_ranges: vec![self.source_range],
})
})?;
let solid = match sarg {
KclValue::Solid { value } => value,
_ => unreachable!(),
};
Ok((data, solid))
}
pub(crate) fn get_tag_to_number_sketch(&self) -> Result<(TagIdentifier, TyF64, Sketch), KclError> {
FromArgs::from_args(self, 0)
}

View File

@ -115,9 +115,9 @@ pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
/// return sumSoFar
/// */
///
/// // We use `assert` to check that our `sum` function gives the
/// // We use `assertEqual` to check that our `sum` function gives the
/// // expected result. It's good to check your work!
/// assert(sum([1, 2, 3]), isEqualTo = 6, tolerance = 0.1, error = "1 + 2 + 3 summed is 6")
/// assertEqual(sum([1, 2, 3]), 6, 0.00001, "1 + 2 + 3 summed is 6")
/// ```
/// ```no_run
/// // This example works just like the previous example above, but it uses
@ -126,9 +126,9 @@ pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
/// arr = [1, 2, 3]
/// sum = reduce(arr, 0, (i, result_so_far) => { return i + result_so_far })
///
/// // We use `assert` to check that our `sum` function gives the
/// // We use `assertEqual` to check that our `sum` function gives the
/// // expected result. It's good to check your work!
/// assert(sum, isEqualTo = 6, tolerance = 0.1, error = "1 + 2 + 3 summed is 6")
/// assertEqual(sum, 6, 0.00001, "1 + 2 + 3 summed is 6")
/// ```
/// ```no_run
/// // Declare a function that sketches a decagon.
@ -224,7 +224,7 @@ async fn call_reduce_closure(
/// ```no_run
/// arr = [1, 2, 3]
/// new_arr = push(arr, 4)
/// assert(new_arr[3], isEqualTo = 4, tolerance = 0.1, error = "4 was added to the end of the array")
/// assertEqual(new_arr[3], 4, 0.00001, "4 was added to the end of the array")
/// ```
#[stdlib {
name = "push",
@ -260,9 +260,9 @@ pub async fn push(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
/// ```no_run
/// arr = [1, 2, 3, 4]
/// new_arr = pop(arr)
/// assert(new_arr[0], isEqualTo = 1, tolerance = 0.00001, error = "1 is the first element of the array")
/// assert(new_arr[1], isEqualTo = 2, tolerance = 0.00001, error = "2 is the second element of the array")
/// assert(new_arr[2], isEqualTo = 3, tolerance = 0.00001, error = "3 is the third element of the array")
/// assertEqual(new_arr[0], 1, 0.00001, "1 is the first element of the array")
/// assertEqual(new_arr[1], 2, 0.00001, "2 is the second element of the array")
/// assertEqual(new_arr[2], 3, 0.00001, "3 is the third element of the array")
/// ```
#[stdlib {
name = "pop",

View File

@ -21,165 +21,135 @@ async fn _assert(value: bool, message: &str, args: &Args) -> Result<(), KclError
Ok(())
}
pub async fn assert_is(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let actual = args.get_unlabeled_kw_arg("actual")?;
let error = args.get_kw_arg_opt("error")?;
inner_assert_is(actual, error, &args).await?;
Ok(KclValue::none())
}
/// Check that the provided value is true, or raise a [KclError]
/// with the provided description.
pub async fn assert(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let actual = args.get_unlabeled_kw_arg("actual")?;
let gt = args.get_kw_arg_opt("isGreaterThan")?;
let lt = args.get_kw_arg_opt("isLessThan")?;
let gte = args.get_kw_arg_opt("isGreaterThanOrEqual")?;
let lte = args.get_kw_arg_opt("isLessThanOrEqual")?;
let eq = args.get_kw_arg_opt("isEqualTo")?;
let tolerance = args.get_kw_arg_opt("tolerance")?;
let error = args.get_kw_arg_opt("error")?;
inner_assert(actual, gt, lt, gte, lte, eq, tolerance, error, &args).await?;
let (data, description): (bool, String) = args.get_data()?;
inner_assert(data, &description, &args).await?;
Ok(KclValue::none())
}
/// Asserts that a value is the boolean value true.
/// ```no_run
/// kclIsFun = true
/// assertIs(kclIsFun)
/// ```
#[stdlib{
name = "assertIs",
keywords = true,
unlabeled_first = true,
args = {
actual = { docs = "Value to check. If this is the boolean value true, assert passes. Otherwise it fails." },
error = { docs = "If the value was false, the program will terminate with this error message" },
}
}]
async fn inner_assert_is(actual: bool, error: Option<String>, args: &Args) -> Result<(), KclError> {
let error_msg = match &error {
Some(x) => x,
None => "should have been true, but it was not",
};
_assert(actual, error_msg, args).await
}
/// Check a value meets some expected conditions at runtime. Program terminates with an error if conditions aren't met.
/// If you provide multiple conditions, they will all be checked and all must be met.
/// Check a value at runtime, and raise an error if the argument provided
/// is false.
///
/// ```no_run
/// n = 10
/// assert(n, isEqualTo = 10)
/// assert(n, isGreaterThanOrEqual = 0, isLessThan = 100, error = "number should be between 0 and 100")
/// assert(1.0000000000012, isEqualTo = 1, tolerance = 0.0001, error = "number should be almost exactly 1")
/// myVar = true
/// assert(myVar, "should always be true")
/// ```
#[stdlib {
name = "assert",
keywords = true,
unlabeled_first = true,
args = {
actual = { docs = "Value to check. It will be compared with one of the comparison arguments." },
is_greater_than = { docs = "Comparison argument. If given, checks the `actual` value is greater than this." },
is_less_than = { docs = "Comparison argument. If given, checks the `actual` value is less than this." },
is_greater_than_or_equal = { docs = "Comparison argument. If given, checks the `actual` value is greater than or equal to this." },
is_less_than_or_equal = { docs = "Comparison argument. If given, checks the `actual` value is less than or equal to this." },
is_equal_to = { docs = "Comparison argument. If given, checks the `actual` value is less than or equal to this.", include_in_snippet = true },
tolerance = { docs = "If `isEqualTo` is used, this is the tolerance to allow for the comparison. This tolerance is used because KCL's number system has some floating-point imprecision when used with very large decimal places." },
error = { docs = "If the value was false, the program will terminate with this error message" },
}
}]
#[allow(clippy::too_many_arguments)]
async fn inner_assert(
actual: TyF64,
is_greater_than: Option<TyF64>,
is_less_than: Option<TyF64>,
is_greater_than_or_equal: Option<TyF64>,
is_less_than_or_equal: Option<TyF64>,
is_equal_to: Option<TyF64>,
tolerance: Option<TyF64>,
error: Option<String>,
args: &Args,
) -> Result<(), KclError> {
// Validate the args
let no_condition_given = [
&is_greater_than,
&is_less_than,
&is_greater_than_or_equal,
&is_less_than_or_equal,
&is_equal_to,
]
.iter()
.all(|cond| cond.is_none());
if no_condition_given {
return Err(KclError::Type(KclErrorDetails {
message: "You must provide at least one condition in this assert (for example, isEqualTo)".to_owned(),
source_ranges: vec![args.source_range],
}));
}
if tolerance.is_some() && is_equal_to.is_none() {
return Err(KclError::Type(KclErrorDetails {
message:
"The `tolerance` arg is only used with `isEqualTo`. Either remove `tolerance` or add an `isEqualTo` arg."
.to_owned(),
source_ranges: vec![args.source_range],
}));
}
let suffix = if let Some(err_string) = error {
format!(": {err_string}")
} else {
Default::default()
};
let actual = actual.n;
// Run the checks.
if let Some(exp) = is_greater_than {
let exp = exp.n;
_assert(
actual > exp,
&format!("Expected {actual} to be greater than {exp} but it wasn't{suffix}"),
args,
)
.await?;
}
if let Some(exp) = is_less_than {
let exp = exp.n;
_assert(
actual < exp,
&format!("Expected {actual} to be less than {exp} but it wasn't{suffix}"),
args,
)
.await?;
}
if let Some(exp) = is_greater_than_or_equal {
let exp = exp.n;
_assert(
actual >= exp,
&format!("Expected {actual} to be greater than or equal to {exp} but it wasn't{suffix}"),
args,
)
.await?;
}
if let Some(exp) = is_less_than_or_equal {
let exp = exp.n;
_assert(
actual <= exp,
&format!("Expected {actual} to be less than or equal to {exp} but it wasn't{suffix}"),
args,
)
.await?;
}
if let Some(exp) = is_equal_to {
let exp = exp.n;
let tolerance = tolerance.map(|e| e.n).unwrap_or(0.0000000001);
_assert(
(actual - exp).abs() < tolerance,
&format!("Expected {actual} to be equal to {exp} but it wasn't{suffix}"),
args,
)
.await?;
}
Ok(())
async fn inner_assert(data: bool, message: &str, args: &Args) -> Result<(), KclError> {
_assert(data, message, args).await
}
pub async fn assert_lt(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (left, right, description): (TyF64, TyF64, String) = args.get_data()?;
inner_assert_lt(left.n, right.n, &description, &args).await?;
Ok(KclValue::none())
}
/// Check that a numerical value is less than to another at runtime,
/// otherwise raise an error.
///
/// ```no_run
/// assertLessThan(1, 2, "1 is less than 2")
/// ```
#[stdlib {
name = "assertLessThan",
}]
async fn inner_assert_lt(left: f64, right: f64, message: &str, args: &Args) -> Result<(), KclError> {
_assert(left < right, message, args).await
}
pub async fn assert_gt(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (left, right, description): (TyF64, TyF64, String) = args.get_data()?;
inner_assert_gt(left.n, right.n, &description, &args).await?;
Ok(KclValue::none())
}
/// Check that a numerical value equals another at runtime,
/// otherwise raise an error.
///
/// ```no_run
/// n = 1.0285
/// o = 1.0286
/// assertEqual(n, o, 0.01, "n is within the given tolerance for o")
/// ```
#[stdlib {
name = "assertEqual",
}]
async fn inner_assert_equal(left: f64, right: f64, epsilon: f64, message: &str, args: &Args) -> Result<(), KclError> {
if epsilon <= 0.0 {
Err(KclError::Type(KclErrorDetails {
message: "assertEqual epsilon must be greater than zero".to_owned(),
source_ranges: vec![args.source_range],
}))
} else if (right - left).abs() < epsilon {
Ok(())
} else {
Err(KclError::Type(KclErrorDetails {
message: format!("assert failed because {left} != {right}: {message}"),
source_ranges: vec![args.source_range],
}))
}
}
pub async fn assert_equal(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (left, right, epsilon, description): (TyF64, TyF64, TyF64, String) = args.get_data()?;
inner_assert_equal(left.n, right.n, epsilon.n, &description, &args).await?;
Ok(KclValue::none())
}
/// Check that a numerical value is greater than another at runtime,
/// otherwise raise an error.
///
/// ```no_run
/// assertGreaterThan(2, 1, "2 is greater than 1")
/// ```
#[stdlib {
name = "assertGreaterThan",
}]
async fn inner_assert_gt(left: f64, right: f64, message: &str, args: &Args) -> Result<(), KclError> {
_assert(left > right, message, args).await
}
pub async fn assert_lte(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (left, right, description): (TyF64, TyF64, String) = args.get_data()?;
inner_assert_lte(left.n, right.n, &description, &args).await?;
Ok(KclValue::none())
}
/// Check that a numerical value is less than or equal to another at runtime,
/// otherwise raise an error.
///
/// ```no_run
/// assertLessThanOrEq(1, 2, "1 is less than 2")
/// assertLessThanOrEq(1, 1, "1 is equal to 1")
/// ```
#[stdlib {
name = "assertLessThanOrEq",
}]
async fn inner_assert_lte(left: f64, right: f64, message: &str, args: &Args) -> Result<(), KclError> {
_assert(left <= right, message, args).await
}
pub async fn assert_gte(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (left, right, description): (TyF64, TyF64, String) = args.get_data()?;
inner_assert_gte(left.n, right.n, &description, &args).await?;
Ok(KclValue::none())
}
/// Check that a numerical value is greater than or equal to another at runtime,
/// otherwise raise an error.
///
/// ```no_run
/// assertGreaterThanOrEq(2, 1, "2 is greater than 1")
/// assertGreaterThanOrEq(1, 1, "1 is equal to 1")
/// ```
#[stdlib {
name = "assertGreaterThanOrEq",
}]
async fn inner_assert_gte(left: f64, right: f64, message: &str, args: &Args) -> Result<(), KclError> {
_assert(left >= right, message, args).await
}

View File

@ -22,7 +22,7 @@ pub async fn int(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
///
/// ```no_run
/// n = int(ceil(5/2))
/// assert(n, isEqualTo = 3, error = "5/2 = 2.5, rounded up makes 3")
/// assertEqual(n, 3, 0.0001, "5/2 = 2.5, rounded up makes 3")
/// // Draw n cylinders.
/// startSketchOn('XZ')
/// |> circle(center = [0, 0], radius = 2 )

View File

@ -35,12 +35,12 @@ pub async fn rem(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kcl
/// If `num` is negative, the result will be too.
///
/// ```no_run
/// assert(rem( 7, divisor = 4), isEqualTo = 3, error = "remainder is 3")
/// assert(rem(-7, divisor = 4), isEqualTo = -3, error = "remainder is -3")
/// assert(rem( 7, divisor = -4), isEqualTo = 3, error = "remainder is 3")
/// assert(rem( 6, divisor = 2.5), isEqualTo = 1, error = "remainder is 1")
/// assert(rem( 6.5, divisor = 2.5), isEqualTo = 1.5, error = "remainder is 1.5")
/// assert(rem( 6.5, divisor = 2), isEqualTo = 0.5, error = "remainder is 0.5")
/// assertEqual(rem( 7, divisor = 4), 3, 0.01, "remainder is 3" )
/// assertEqual(rem(-7, divisor = 4), -3, 0.01, "remainder is -3")
/// assertEqual(rem( 7, divisor = -4), 3, 0.01, "remainder is 3" )
/// assertEqual(rem( 6, divisor = 2.5), 1, 0.01, "remainder is 1" )
/// assertEqual(rem( 6.5, divisor = 2.5), 1.5, 0.01, "remainder is 1.5" )
/// assertEqual(rem( 6.5, divisor = 2), 0.5, 0.01, "remainder is 0.5" )
/// ```
#[stdlib {
name = "rem",

View File

@ -135,7 +135,11 @@ lazy_static! {
Box::new(crate::std::units::FromCm),
Box::new(crate::std::units::FromYd),
Box::new(crate::std::assert::Assert),
Box::new(crate::std::assert::AssertIs),
Box::new(crate::std::assert::AssertEqual),
Box::new(crate::std::assert::AssertLessThan),
Box::new(crate::std::assert::AssertGreaterThan),
Box::new(crate::std::assert::AssertLessThanOrEq),
Box::new(crate::std::assert::AssertGreaterThanOrEq),
Box::new(crate::std::transform::Scale),
Box::new(crate::std::transform::Translate),
Box::new(crate::std::transform::Rotate),

View File

@ -247,10 +247,9 @@ async fn inner_shell(
/// Make the inside of a 3D object hollow.
pub async fn hollow(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let solid = args.get_unlabeled_kw_arg_typed("solid", &RuntimeType::solid(), exec_state)?;
let thickness: TyF64 = args.get_kw_arg_typed("thickness", &RuntimeType::length(), exec_state)?;
let (thickness, solid) = args.get_length_and_solid(exec_state)?;
let value = inner_hollow(solid, thickness, exec_state, args).await?;
let value = inner_hollow(thickness, solid, exec_state, args).await?;
Ok(KclValue::Solid { value })
}
@ -268,7 +267,7 @@ pub async fn hollow(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
/// |> line(end = [-24, 0])
/// |> close()
/// |> extrude(length = 6)
/// |> hollow(thickness = 0.25)
/// |> hollow (0.25, %)
/// ```
///
/// ```no_run
@ -280,7 +279,7 @@ pub async fn hollow(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
/// |> line(end = [-24, 0])
/// |> close()
/// |> extrude(length = 6)
/// |> hollow(thickness = 0.5)
/// |> hollow (0.5, %)
/// ```
///
/// ```no_run
@ -302,21 +301,15 @@ pub async fn hollow(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
/// |> circle( center = [size / 2, -size / 2], radius = 25 )
/// |> extrude(length = 50)
///
/// hollow(case, thickness = 0.5)
/// hollow(0.5, case)
/// ```
#[stdlib {
name = "hollow",
feature_tree_operation = true,
keywords = true,
unlabeled_first = true,
args = {
solid = { docs = "Which solid to shell out" },
thickness = {docs = "The thickness of the shell" },
}
}]
async fn inner_hollow(
solid: Box<Solid>,
thickness: TyF64,
solid: Box<Solid>,
exec_state: &mut ExecState,
args: Args,
) -> Result<Box<Solid>, KclError> {

View File

@ -1454,7 +1454,7 @@ pub(crate) async fn inner_start_profile_at(
/// Returns the X component of the sketch profile start point.
pub async fn profile_start_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let sketch: Sketch = args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::sketch(), exec_state)?;
let sketch: Sketch = args.get_sketch(exec_state)?;
let ty = sketch.units.into();
let x = inner_profile_start_x(sketch)?;
Ok(args.make_user_val_from_f64_with_type(TyF64::new(x, ty)))
@ -1471,20 +1471,15 @@ pub async fn profile_start_x(exec_state: &mut ExecState, args: Args) -> Result<K
/// |> angledLine(angle = 30, endAbsoluteX = profileStartX(%))
/// ```
#[stdlib {
name = "profileStartX",
keywords = true,
unlabeled_first = true,
args = {
profile = {docs = "Profile whose start is being used"},
}
name = "profileStartX"
}]
pub(crate) fn inner_profile_start_x(profile: Sketch) -> Result<f64, KclError> {
Ok(profile.start.to[0])
pub(crate) fn inner_profile_start_x(sketch: Sketch) -> Result<f64, KclError> {
Ok(sketch.start.to[0])
}
/// Returns the Y component of the sketch profile start point.
pub async fn profile_start_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let sketch: Sketch = args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::sketch(), exec_state)?;
let sketch: Sketch = args.get_sketch(exec_state)?;
let ty = sketch.units.into();
let x = inner_profile_start_y(sketch)?;
Ok(args.make_user_val_from_f64_with_type(TyF64::new(x, ty)))
@ -1500,20 +1495,15 @@ pub async fn profile_start_y(exec_state: &mut ExecState, args: Args) -> Result<K
/// |> angledLine(angle = 30, endAbsoluteY = profileStartY(%))
/// ```
#[stdlib {
name = "profileStartY",
keywords = true,
unlabeled_first = true,
args = {
profile = {docs = "Profile whose start is being used"},
}
name = "profileStartY"
}]
pub(crate) fn inner_profile_start_y(profile: Sketch) -> Result<f64, KclError> {
Ok(profile.start.to[1])
pub(crate) fn inner_profile_start_y(sketch: Sketch) -> Result<f64, KclError> {
Ok(sketch.start.to[1])
}
/// Returns the sketch profile start point.
pub async fn profile_start(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let sketch: Sketch = args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::sketch(), exec_state)?;
let sketch: Sketch = args.get_sketch(exec_state)?;
let ty = sketch.units.into();
let point = inner_profile_start(sketch)?;
Ok(KclValue::from_point2d(point, ty, args.into()))
@ -1532,15 +1522,10 @@ pub async fn profile_start(exec_state: &mut ExecState, args: Args) -> Result<Kcl
/// |> extrude(length = 20)
/// ```
#[stdlib {
name = "profileStart",
keywords = true,
unlabeled_first = true,
args = {
profile = {docs = "Profile whose start is being used"},
}
name = "profileStart"
}]
pub(crate) fn inner_profile_start(profile: Sketch) -> Result<[f64; 2], KclError> {
Ok(profile.start.to)
pub(crate) fn inner_profile_start(sketch: Sketch) -> Result<[f64; 2], KclError> {
Ok(sketch.start.to)
}
/// Close the current sketch.

View File

@ -2798,45 +2798,53 @@ description: Result of parsing add_lots.kcl
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "isEqualTo",
"name": "x",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "3660",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 3660.0,
"suffix": "None"
}
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
},
{
"commentStart": 0,
"end": 0,
"raw": "3660",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 3660.0,
"suffix": "None"
}
},
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "error",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "\"Big sum\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "Big sum"
"commentStart": 0,
"end": 0,
"raw": "0.1",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.1,
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"raw": "\"Big sum\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "Big sum"
}
],
"callee": {
@ -2846,7 +2854,7 @@ description: Result of parsing add_lots.kcl
"name": {
"commentStart": 0,
"end": 0,
"name": "assert",
"name": "assertEqual",
"start": 0,
"type": "Identifier"
},
@ -2857,24 +2865,8 @@ description: Result of parsing add_lots.kcl
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "x",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
"type": "CallExpression",
"type": "CallExpression"
},
"start": 0,
"type": "ExpressionStatement",

View File

@ -4,4 +4,4 @@ fn f(i) {
x = f(0) + f(1) + f(2) + f(3) + f(4) + f(5) + f(6) + f(7) + f(8) + f(9) + f(10) + f(11) + f(12) + f(13) + f(14) + f(15) + f(16) + f(17) + f(18) + f(19) + f(20) + f(21) + f(22) + f(23) + f(24) + f(25) + f(26) + f(27) + f(28) + f(29) + f(30) + f(31) + f(32) + f(33) + f(34) + f(35) + f(36) + f(37) + f(38) + f(39) + f(40) + f(41) + f(42) + f(43) + f(44) + f(45) + f(46) + f(47) + f(48) + f(49) + f(50) + f(51) + f(52) + f(53) + f(54) + f(55) + f(56) + f(57) + f(58) + f(59) + f(60)
assert(x, isEqualTo = 3660, error = "Big sum")
assertEqual(x, 3660, 0.1, "Big sum")

View File

@ -8,4 +8,4 @@ fn f(i) {
x = f(0) + f(1) + f(2) + f(3) + f(4) + f(5) + f(6) + f(7) + f(8) + f(9) + f(10) + f(11) + f(12) + f(13) + f(14) + f(15) + f(16) + f(17) + f(18) + f(19) + f(20) + f(21) + f(22) + f(23) + f(24) + f(25) + f(26) + f(27) + f(28) + f(29) + f(30) + f(31) + f(32) + f(33) + f(34) + f(35) + f(36) + f(37) + f(38) + f(39) + f(40) + f(41) + f(42) + f(43) + f(44) + f(45) + f(46) + f(47) + f(48) + f(49) + f(50) + f(51) + f(52) + f(53) + f(54) + f(55) + f(56) + f(57) + f(58) + f(59) + f(60)
assert(x, isEqualTo = 3660, error = "Big sum")
assertEqual(x, 3660, 0.1, "Big sum")

View File

@ -260,180 +260,34 @@ description: Result of parsing array_elem_pop.kcl
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"computed": false,
"end": 0,
"object": {
"commentStart": 0,
"end": 0,
"name": "isEqualTo",
"name": "new_arr1",
"start": 0,
"type": "Identifier",
"type": "Identifier"
},
"arg": {
"property": {
"commentStart": 0,
"end": 0,
"raw": "1",
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 1.0,
"value": 0.0,
"suffix": "None"
}
}
},
"start": 0,
"type": "MemberExpression",
"type": "MemberExpression"
},
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "error",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "\"element 0 should not have changed\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "element 0 should not have changed"
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "assert",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"commentStart": 0,
"computed": false,
"end": 0,
"object": {
"commentStart": 0,
"end": 0,
"name": "new_arr1",
"start": 0,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"commentStart": 0,
"end": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
},
"start": 0,
"type": "MemberExpression",
"type": "MemberExpression"
}
},
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "isEqualTo",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "2",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 2.0,
"suffix": "None"
}
}
},
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "error",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "\"element 1 should not have changed\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "element 1 should not have changed"
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "assert",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"commentStart": 0,
"computed": false,
"end": 0,
"object": {
"commentStart": 0,
"end": 0,
"name": "new_arr1",
"start": 0,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"commentStart": 0,
"end": 0,
"raw": "1",
@ -445,10 +299,48 @@ description: Result of parsing array_elem_pop.kcl
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"raw": "0.00001",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.00001,
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"raw": "\"element 0 should not have changed\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "element 0 should not have changed"
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "assertEqual",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "MemberExpression",
"type": "MemberExpression"
}
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpression",
"type": "CallExpression"
},
"start": 0,
"type": "ExpressionStatement",
@ -460,15 +352,18 @@ description: Result of parsing array_elem_pop.kcl
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"computed": false,
"end": 0,
"object": {
"commentStart": 0,
"end": 0,
"name": "isEqualTo",
"name": "new_arr1",
"start": 0,
"type": "Identifier",
"type": "Identifier"
},
"arg": {
"property": {
"commentStart": 0,
"end": 0,
"raw": "1",
@ -479,26 +374,43 @@ description: Result of parsing array_elem_pop.kcl
"value": 1.0,
"suffix": "None"
}
},
"start": 0,
"type": "MemberExpression",
"type": "MemberExpression"
},
{
"commentStart": 0,
"end": 0,
"raw": "2",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 2.0,
"suffix": "None"
}
},
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "error",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "\"element 0 should not have changed\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "element 0 should not have changed"
"commentStart": 0,
"end": 0,
"raw": "0.00001",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.00001,
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"raw": "\"element 1 should not have changed\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "element 1 should not have changed"
}
],
"callee": {
@ -508,7 +420,7 @@ description: Result of parsing array_elem_pop.kcl
"name": {
"commentStart": 0,
"end": 0,
"name": "assert",
"name": "assertEqual",
"start": 0,
"type": "Identifier"
},
@ -519,36 +431,100 @@ description: Result of parsing array_elem_pop.kcl
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"commentStart": 0,
"computed": false,
"end": 0,
"object": {
"type": "CallExpression",
"type": "CallExpression"
},
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"arguments": [
{
"commentStart": 0,
"computed": false,
"end": 0,
"name": "new_arr2",
"object": {
"commentStart": 0,
"end": 0,
"name": "new_arr2",
"start": 0,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"commentStart": 0,
"end": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
},
"start": 0,
"type": "Identifier",
"type": "Identifier"
"type": "MemberExpression",
"type": "MemberExpression"
},
"property": {
{
"commentStart": 0,
"end": 0,
"raw": "0",
"raw": "1",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"value": 1.0,
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"raw": "0.00001",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.00001,
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"raw": "\"element 0 should not have changed\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "element 0 should not have changed"
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "assertEqual",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "MemberExpression",
"type": "MemberExpression"
}
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpression",
"type": "CallExpression"
},
"start": 0,
"type": "ExpressionStatement",

View File

@ -2,6 +2,6 @@ arr = [1, 2, 3]
new_arr1 = pop(arr)
new_arr2 = pop(new_arr1)
new_arr3 = pop(new_arr2)
assert(new_arr1[0], isEqualTo = 1, error = "element 0 should not have changed")
assert(new_arr1[1], isEqualTo = 2, error = "element 1 should not have changed")
assert(new_arr2[0], isEqualTo = 1, error = "element 0 should not have changed")
assertEqual(new_arr1[0], 1, 0.00001, "element 0 should not have changed")
assertEqual(new_arr1[1], 2, 0.00001, "element 1 should not have changed")
assertEqual(new_arr2[0], 1, 0.00001, "element 0 should not have changed")

View File

@ -6,6 +6,6 @@ arr = [1, 2, 3]
new_arr1 = pop(arr)
new_arr2 = pop(new_arr1)
new_arr3 = pop(new_arr2)
assert(new_arr1[0], isEqualTo = 1, error = "element 0 should not have changed")
assert(new_arr1[1], isEqualTo = 2, error = "element 1 should not have changed")
assert(new_arr2[0], isEqualTo = 1, error = "element 0 should not have changed")
assertEqual(new_arr1[0], 1, 0.00001, "element 0 should not have changed")
assertEqual(new_arr1[1], 2, 0.00001, "element 1 should not have changed")
assertEqual(new_arr2[0], 1, 0.00001, "element 0 should not have changed")

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,12 @@
arr = [1, 2, 3]
new_arr1 = push(arr, 4)
new_arr2 = push(new_arr1, 5)
assert(new_arr1[0], isEqualTo = 1, error = "element 0 should not have changed")
assert(new_arr1[1], isEqualTo = 2, error = "element 1 should not have changed")
assert(new_arr1[2], isEqualTo = 3, error = "element 2 should not have changed")
assert(new_arr1[3], isEqualTo = 4, error = "4 was added to the end of the array")
assert(new_arr2[0], isEqualTo = 1, error = "element 0 should not have changed")
assert(new_arr2[1], isEqualTo = 2, error = "element 1 should not have changed")
assert(new_arr2[2], isEqualTo = 3, error = "element 2 should not have changed")
assert(new_arr2[3], isEqualTo = 4, error = "4 was added to the end of the array")
assert(new_arr2[4], isEqualTo = 5, error = "5 was added to the end of the array")
assertEqual(new_arr1[0], 1, 0.00001, "element 0 should not have changed")
assertEqual(new_arr1[1], 2, 0.00001, "element 1 should not have changed")
assertEqual(new_arr1[2], 3, 0.00001, "element 2 should not have changed")
assertEqual(new_arr1[3], 4, 0.00001, "4 was added to the end of the array")
assertEqual(new_arr2[0], 1, 0.00001, "element 0 should not have changed")
assertEqual(new_arr2[1], 2, 0.00001, "element 1 should not have changed")
assertEqual(new_arr2[2], 3, 0.00001, "element 2 should not have changed")
assertEqual(new_arr2[3], 4, 0.00001, "4 was added to the end of the array")
assertEqual(new_arr2[4], 5, 0.00001, "5 was added to the end of the array")

View File

@ -5,12 +5,12 @@ description: Result of unparsing array_elem_push.kcl
arr = [1, 2, 3]
new_arr1 = push(arr, 4)
new_arr2 = push(new_arr1, 5)
assert(new_arr1[0], isEqualTo = 1, error = "element 0 should not have changed")
assert(new_arr1[1], isEqualTo = 2, error = "element 1 should not have changed")
assert(new_arr1[2], isEqualTo = 3, error = "element 2 should not have changed")
assert(new_arr1[3], isEqualTo = 4, error = "4 was added to the end of the array")
assert(new_arr2[0], isEqualTo = 1, error = "element 0 should not have changed")
assert(new_arr2[1], isEqualTo = 2, error = "element 1 should not have changed")
assert(new_arr2[2], isEqualTo = 3, error = "element 2 should not have changed")
assert(new_arr2[3], isEqualTo = 4, error = "4 was added to the end of the array")
assert(new_arr2[4], isEqualTo = 5, error = "5 was added to the end of the array")
assertEqual(new_arr1[0], 1, 0.00001, "element 0 should not have changed")
assertEqual(new_arr1[1], 2, 0.00001, "element 1 should not have changed")
assertEqual(new_arr1[2], 3, 0.00001, "element 2 should not have changed")
assertEqual(new_arr1[3], 4, 0.00001, "4 was added to the end of the array")
assertEqual(new_arr2[0], 1, 0.00001, "element 0 should not have changed")
assertEqual(new_arr2[1], 2, 0.00001, "element 1 should not have changed")
assertEqual(new_arr2[2], 3, 0.00001, "element 2 should not have changed")
assertEqual(new_arr2[3], 4, 0.00001, "4 was added to the end of the array")
assertEqual(new_arr2[4], 5, 0.00001, "5 was added to the end of the array")

View File

@ -1,5 +1,5 @@
---
source: kcl-lib/src/simulation_tests.rs
source: kcl/src/simulation_tests.rs
description: Error from executing array_elem_push_fail.kcl
---
KCL UndefinedValue error

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,17 @@
r1 = [0..4]
assert(r1[4], isEqualTo = 4, error = "last element is included")
assertEqual(r1[4], 4, 0.00001, "last element is included")
four = 4
zero = 0
r2 = [zero..four]
assert(r2[4], isEqualTo = 4, error = "last element is included")
assertEqual(r2[4], 4, 0.00001, "last element is included")
five = int(four + 1)
r3 = [zero..five]
assert(r3[4], isEqualTo = 4, error = "second-to-last element is included")
assert(r3[5], isEqualTo = 5, error = "last element is included")
assertEqual(r3[4], 4, 0.00001, "second-to-last element is included")
assertEqual(r3[5], 5, 0.00001, "last element is included")
r4 = [int(zero + 1) .. int(five - 1)]
assert(r4[0], isEqualTo = 1, error = "first element is 1")
assert(r4[2], isEqualTo = 3, error = "second-to-last element is 3")
assert(r4[3], isEqualTo = 4, error = "last element is 4")
assertEqual(r4[0], 1, 0.00001, "first element is 1")
assertEqual(r4[2], 3, 0.00001, "second-to-last element is 3")
assertEqual(r4[3], 4, 0.00001, "last element is 4")

View File

@ -3,19 +3,19 @@ source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing array_range_expr.kcl
---
r1 = [0..4]
assert(r1[4], isEqualTo = 4, error = "last element is included")
assertEqual(r1[4], 4, 0.00001, "last element is included")
four = 4
zero = 0
r2 = [zero..four]
assert(r2[4], isEqualTo = 4, error = "last element is included")
assertEqual(r2[4], 4, 0.00001, "last element is included")
five = int(four + 1)
r3 = [zero..five]
assert(r3[4], isEqualTo = 4, error = "second-to-last element is included")
assert(r3[5], isEqualTo = 5, error = "last element is included")
assertEqual(r3[4], 4, 0.00001, "second-to-last element is included")
assertEqual(r3[5], 5, 0.00001, "last element is included")
r4 = [int(zero + 1) .. int(five - 1)]
assert(r4[0], isEqualTo = 1, error = "first element is 1")
assert(r4[2], isEqualTo = 3, error = "second-to-last element is 3")
assert(r4[3], isEqualTo = 4, error = "last element is 4")
assertEqual(r4[0], 1, 0.00001, "first element is 1")
assertEqual(r4[2], 3, 0.00001, "second-to-last element is 3")
assertEqual(r4[3], 4, 0.00001, "last element is 4")

View File

@ -96,53 +96,73 @@ description: Result of parsing array_range_negative_expr.kcl
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"computed": false,
"end": 0,
"object": {
"commentStart": 0,
"end": 0,
"name": "isEqualTo",
"name": "xs",
"start": 0,
"type": "Identifier",
"type": "Identifier"
},
"arg": {
"argument": {
"commentStart": 0,
"end": 0,
"raw": "5",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 5.0,
"suffix": "None"
}
},
"property": {
"commentStart": 0,
"end": 0,
"operator": "-",
"raw": "0",
"start": 0,
"type": "UnaryExpression",
"type": "UnaryExpression"
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
},
"start": 0,
"type": "MemberExpression",
"type": "MemberExpression"
},
{
"argument": {
"commentStart": 0,
"end": 0,
"raw": "5",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 5.0,
"suffix": "None"
}
},
"commentStart": 0,
"end": 0,
"operator": "-",
"start": 0,
"type": "UnaryExpression",
"type": "UnaryExpression"
},
{
"commentStart": 0,
"end": 0,
"raw": "0.001",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.001,
"suffix": "None"
}
},
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "error",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "\"first element is -5\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "first element is -5"
}
"commentStart": 0,
"end": 0,
"raw": "\"first element is -5\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "first element is -5"
}
],
"callee": {
@ -152,7 +172,7 @@ description: Result of parsing array_range_negative_expr.kcl
"name": {
"commentStart": 0,
"end": 0,
"name": "assert",
"name": "assertEqual",
"start": 0,
"type": "Identifier"
},
@ -163,36 +183,8 @@ description: Result of parsing array_range_negative_expr.kcl
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"commentStart": 0,
"computed": false,
"end": 0,
"object": {
"commentStart": 0,
"end": 0,
"name": "xs",
"start": 0,
"type": "Identifier",
"type": "Identifier"
},
"property": {
"commentStart": 0,
"end": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
},
"start": 0,
"type": "MemberExpression",
"type": "MemberExpression"
}
"type": "CallExpression",
"type": "CallExpression"
},
"start": 0,
"type": "ExpressionStatement",

View File

@ -1,2 +1,2 @@
xs = [int(-5) .. 5]
assert(xs[0], isEqualTo = -5, error = "first element is -5")
assertEqual(xs[0], -5, 0.001, "first element is -5")

View File

@ -3,4 +3,4 @@ source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing array_range_negative_expr.kcl
---
xs = [int(-5) .. 5]
assert(xs[0], isEqualTo = -5, error = "first element is -5")
assertEqual(xs[0], -5, 0.001, "first element is -5")

View File

@ -156,15 +156,26 @@ description: Result of parsing boolean_logical_and.kcl
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"left": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": "isEqualTo",
"name": {
"commentStart": 0,
"end": 0,
"name": "a",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Identifier"
"type": "Name",
"type": "Name"
},
"arg": {
"operator": "==",
"right": {
"commentStart": 0,
"end": 0,
"raw": "2",
@ -175,26 +186,19 @@ description: Result of parsing boolean_logical_and.kcl
"value": 2.0,
"suffix": "None"
}
}
},
"start": 0,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "error",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "\"right branch of and is false makes the whole expression false\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "right branch of and is false makes the whole expression false"
}
"commentStart": 0,
"end": 0,
"raw": "\"right branch of and is false makes the whole expression false\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "right branch of and is false makes the whole expression false"
}
],
"callee": {
@ -215,24 +219,8 @@ description: Result of parsing boolean_logical_and.kcl
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "a",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
"type": "CallExpression",
"type": "CallExpression"
},
"start": 0,
"type": "ExpressionStatement",
@ -389,15 +377,26 @@ description: Result of parsing boolean_logical_and.kcl
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"left": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": "isEqualTo",
"name": {
"commentStart": 0,
"end": 0,
"name": "b",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Identifier"
"type": "Name",
"type": "Name"
},
"arg": {
"operator": "==",
"right": {
"commentStart": 0,
"end": 0,
"raw": "2",
@ -408,26 +407,19 @@ description: Result of parsing boolean_logical_and.kcl
"value": 2.0,
"suffix": "None"
}
}
},
"start": 0,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "error",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "\"left branch of and is false makes the whole expression false\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "left branch of and is false makes the whole expression false"
}
"commentStart": 0,
"end": 0,
"raw": "\"left branch of and is false makes the whole expression false\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "left branch of and is false makes the whole expression false"
}
],
"callee": {
@ -448,24 +440,8 @@ description: Result of parsing boolean_logical_and.kcl
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "b",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
"type": "CallExpression",
"type": "CallExpression"
},
"start": 0,
"type": "ExpressionStatement",
@ -622,15 +598,26 @@ description: Result of parsing boolean_logical_and.kcl
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"left": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": "isEqualTo",
"name": {
"commentStart": 0,
"end": 0,
"name": "c",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Identifier"
"type": "Name",
"type": "Name"
},
"arg": {
"operator": "==",
"right": {
"commentStart": 0,
"end": 0,
"raw": "1",
@ -641,26 +628,19 @@ description: Result of parsing boolean_logical_and.kcl
"value": 1.0,
"suffix": "None"
}
}
},
"start": 0,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "error",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "\"both branches of and are true makes the whole expression true\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "both branches of and are true makes the whole expression true"
}
"commentStart": 0,
"end": 0,
"raw": "\"both branches of and are true makes the whole expression true\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "both branches of and are true makes the whole expression true"
}
],
"callee": {
@ -681,24 +661,8 @@ description: Result of parsing boolean_logical_and.kcl
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "c",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
"type": "CallExpression",
"type": "CallExpression"
},
"start": 0,
"type": "ExpressionStatement",
@ -855,15 +819,26 @@ description: Result of parsing boolean_logical_and.kcl
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"left": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": "isEqualTo",
"name": {
"commentStart": 0,
"end": 0,
"name": "d",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Identifier"
"type": "Name",
"type": "Name"
},
"arg": {
"operator": "==",
"right": {
"commentStart": 0,
"end": 0,
"raw": "2",
@ -874,26 +849,19 @@ description: Result of parsing boolean_logical_and.kcl
"value": 2.0,
"suffix": "None"
}
}
},
"start": 0,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "error",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "\"both branches of and are false makes the whole expression false\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "both branches of and are false makes the whole expression false"
}
"commentStart": 0,
"end": 0,
"raw": "\"both branches of and are false makes the whole expression false\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "both branches of and are false makes the whole expression false"
}
],
"callee": {
@ -914,24 +882,8 @@ description: Result of parsing boolean_logical_and.kcl
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "d",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
"type": "CallExpression",
"type": "CallExpression"
},
"start": 0,
"type": "ExpressionStatement",

View File

@ -4,7 +4,7 @@ a = if aa {
} else {
2
}
assert(a, isEqualTo = 2, error = "right branch of and is false makes the whole expression false")
assert(a == 2, "right branch of and is false makes the whole expression false")
bb = false & true
b = if bb {
@ -12,7 +12,7 @@ b = if bb {
} else {
2
}
assert(b, isEqualTo = 2, error = "left branch of and is false makes the whole expression false")
assert(b == 2, "left branch of and is false makes the whole expression false")
cc = true & true
c = if cc {
@ -20,7 +20,7 @@ c = if cc {
} else {
2
}
assert(c, isEqualTo = 1, error = "both branches of and are true makes the whole expression true")
assert(c == 1, "both branches of and are true makes the whole expression true")
dd = false & false
d = if dd {
@ -28,4 +28,4 @@ d = if dd {
} else {
2
}
assert(d, isEqualTo = 2, error = "both branches of and are false makes the whole expression false")
assert(d == 2, "both branches of and are false makes the whole expression false")

Some files were not shown because too many files have changed in this diff Show More