Skip to content

Commit 81d86b3

Browse files
committed
Basic generation and improve validation
1 parent eb8bfe4 commit 81d86b3

4 files changed

Lines changed: 51 additions & 4 deletions

File tree

apps/test/src/App.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import { Factor } from './Exercise'
44
const App: Component = () => {
55
const [data, setData] = createSignal({
66
name: 'math/factor' as const,
7-
question: { expr: '(x - 1)(x + 1)' },
7+
question: { expr: '(x + {a})(x + {b})' },
8+
params: {
9+
a: [1, 2, 3],
10+
b: [1, 2, 3],
11+
},
812
attempt: [],
913
})
1014
return (

apps/test/src/Exercise.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,18 @@ import { createMemo, Show } from 'solid-js'
1010
import * as v from 'valibot'
1111

1212
const Math = defineField({
13-
base: v.string(),
13+
base: v.pipe(
14+
v.string(),
15+
v.nonEmpty(),
16+
v.check((v) => {
17+
try {
18+
expr(v)
19+
return true
20+
} catch (error) {
21+
return false
22+
}
23+
}, 'Expression mathématique invalide'),
24+
),
1425
feedback: v.pipe(v.string(), v.transform(expr)),
1526
})
1627

packages/core/src/exercise/base.tsx

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Dynamic } from '@solidjs/web'
2-
import { mapAsync, mapValues } from 'es-toolkit'
2+
import { mapAsync, mapValues, sample } from 'es-toolkit'
33
import {
44
createMemo,
55
createStore,
@@ -147,19 +147,50 @@ function Attempt<T extends Schema, V extends 'base' | 'feedback'>(schema: T, sta
147147
)
148148
}
149149

150+
function Param<T extends boolean>(transform: T) {
151+
const value = v.union([v.number(), v.string()])
152+
const base = v.array(value)
153+
const withTransform = v.pipe(base, v.transform(sample))
154+
return (transform ? withTransform : base) as T extends true ? typeof withTransform : typeof base
155+
}
156+
157+
function Params<T extends boolean>(transform: T) {
158+
return v.record(v.string(), Param(transform))
159+
}
160+
150161
export function buildSchemas<T extends Schema>(
151162
schema: T,
152163
feedback: ReturnType<typeof defineFeedback<T>>,
153164
) {
154165
return {
166+
Teacher: v.object({
167+
name: v.literal(schema.name as T['name']),
168+
question: RawShapeSchema(schema.question as T['question'], 'base'),
169+
params: v.optional(Params(false)),
170+
}),
155171
Student: v.pipeAsync(
156172
v.object({
157173
name: v.literal(schema.name as T['name']),
158174
question: RawShapeSchema(schema.question as T['question'], 'base'),
159175
attempt: Attempt(schema, 'base'),
176+
params: v.optional(Params(true)),
160177
}),
161178
// TODO: calls need to be deduped, waiting for solid-router release?
162-
v.transformAsync(async ({ attempt, question, ...exercise }) => {
179+
v.transformAsync(async ({ attempt, question: q, params, ...exercise }) => {
180+
let question = q
181+
function subs<T extends any>(param: string, value: string, v: T): T {
182+
if (typeof v === 'string') {
183+
return v.replaceAll(`{${param}}`, value) as T
184+
} else if (Array.isArray(v)) {
185+
return v.map(subs.bind(null, param, value)) as T
186+
} else if (typeof v === 'object' && v !== null) {
187+
return mapValues(v, subs.bind(null, param, value)) as T
188+
}
189+
return v
190+
}
191+
for (const [param, value] of Object.entries(params ?? {})) {
192+
question = subs(param, String(value), question)
193+
}
163194
const parsedQuestion = v.parse(
164195
RawShapeSchema(schema.question as T['question'], 'feedback'),
165196
question,

packages/core/src/expr.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const integrateParams = v.union([
2020
const Math: v.GenericSchema<MathJsonExpression> = v.union([
2121
v.pipe(
2222
v.string(),
23+
v.check((expr) => ce.parse(expr).isValid, "Ceci n'est pas une expression mathématique valide"),
2324
v.transform((input) => ce.parse(input).json),
2425
),
2526
v.number(),

0 commit comments

Comments
 (0)