From 7b042a807af7a31ed2ffa47cc8bf2bf64d903bc7 Mon Sep 17 00:00:00 2001 From: adr Date: Wed, 19 Feb 2025 00:21:27 -0500 Subject: [PATCH 01/10] do obj schemas --- src/schemas/objs/bool.ts | 6 +- src/schemas/objs/built-in.ts | 2 +- src/schemas/objs/ident.ts | 19 +++ src/schemas/objs/infix.ts | 28 +++++ src/schemas/objs/union.ts | 8 ++ src/schemas/objs/unions/polynomial.ts | 23 ++++ src/schemas/polynomial-operator.ts | 12 ++ src/services/diff/obj.ts | 161 +++++++------------------- src/services/evaluator/index.ts | 3 +- src/services/object/builtins.ts | 10 +- src/services/object/index.ts | 52 +++------ 11 files changed, 162 insertions(+), 162 deletions(-) create mode 100644 src/schemas/objs/ident.ts create mode 100644 src/schemas/objs/infix.ts create mode 100644 src/schemas/objs/unions/polynomial.ts create mode 100644 src/schemas/polynomial-operator.ts diff --git a/src/schemas/objs/bool.ts b/src/schemas/objs/bool.ts index d37c725..1443d80 100644 --- a/src/schemas/objs/bool.ts +++ b/src/schemas/objs/bool.ts @@ -5,13 +5,13 @@ const fields = { } export interface BoolObj extends Schema.Struct.Type { - readonly _tag: 'BoolObj' + readonly _tag: 'BooleanObj' } export interface BoolObjEncoded extends Schema.Struct.Type { - readonly _tag: 'BoolObj' + readonly _tag: 'BooleanObj' } -export const boolObjSchema = Schema.TaggedStruct('BoolObj', { +export const boolObjSchema = Schema.TaggedStruct('BooleanObj', { ...fields, }) diff --git a/src/schemas/objs/built-in.ts b/src/schemas/objs/built-in.ts index 240fdfc..2020f8f 100644 --- a/src/schemas/objs/built-in.ts +++ b/src/schemas/objs/built-in.ts @@ -1,7 +1,7 @@ import { Schema } from 'effect' const fields = { - fn: Schema.Unknown, + fn: Schema.Literal('len', 'diff'), } export interface BuiltInObj extends Schema.Struct.Type { diff --git a/src/schemas/objs/ident.ts b/src/schemas/objs/ident.ts new file mode 100644 index 0000000..e38d5e2 --- /dev/null +++ b/src/schemas/objs/ident.ts @@ -0,0 +1,19 @@ +import { Schema } from 'effect' +import { IdentExp, IdentExpEncoded } from '../nodes/exps/ident' + +export interface IdentObj { + readonly _tag: 'IdentObj' + readonly identExp: IdentExp +} + +export interface IdentObjEncoded { + readonly _tag: 'IdentObj' + readonly identExp: IdentExpEncoded +} + +export const identObjSchema = Schema.TaggedStruct('IdentObj', { + identExp: Schema.suspend((): Schema.Schema => IdentExp) + +}) + +export const identObjEq = Schema.equivalence(identObjSchema) diff --git a/src/schemas/objs/infix.ts b/src/schemas/objs/infix.ts new file mode 100644 index 0000000..253e78c --- /dev/null +++ b/src/schemas/objs/infix.ts @@ -0,0 +1,28 @@ +import { Schema } from 'effect' +import { infixOperatorSchema } from '../infix-operator' +import { Obj, ObjEncoded, objSchema } from './union' + +const fields = { + operator: infixOperatorSchema +} + +export interface InfixObj extends Schema.Struct.Type { + readonly _tag: 'InfixObj' + readonly left: Obj + readonly right: Obj +} + +export interface InfixObjEncoded extends Schema.Struct.Type { + readonly _tag: 'InfixObj' + readonly left: ObjEncoded + readonly right: ObjEncoded +} + +export const infixObjSchema = Schema.TaggedStruct('InfixObj', { + ...fields, + left: Schema.suspend((): Schema.Schema => objSchema), + right: Schema.suspend((): Schema.Schema => objSchema), + operator: infixOperatorSchema +}) + +export const infixObjEq = Schema.equivalence(infixObjSchema) diff --git a/src/schemas/objs/union.ts b/src/schemas/objs/union.ts index a009262..7832b29 100644 --- a/src/schemas/objs/union.ts +++ b/src/schemas/objs/union.ts @@ -23,6 +23,8 @@ import { stringObjSchema, type StringObj, } from './string' +import { IdentObj, IdentObjEncoded, identObjSchema } from './ident' +import { InfixObj, InfixObjEncoded, infixObjSchema } from './infix' export type Obj = | BoolObj @@ -33,6 +35,8 @@ export type Obj = | NullObj | ReturnObj | StringObj + | IdentObj + | InfixObj export type ObjEncoded = | BoolObjEncoded @@ -43,6 +47,8 @@ export type ObjEncoded = | NullObjEncoded | ReturnObjEncoded | StringObjEncoded + | IdentObjEncoded + | InfixObjEncoded export const objSchema = Schema.suspend( (): Schema.Schema => @@ -55,5 +61,7 @@ export const objSchema = Schema.suspend( nullObjSchema, returnObjSchema, stringObjSchema, + identObjSchema, + infixObjSchema ), ) diff --git a/src/schemas/objs/unions/polynomial.ts b/src/schemas/objs/unions/polynomial.ts new file mode 100644 index 0000000..cc1d28c --- /dev/null +++ b/src/schemas/objs/unions/polynomial.ts @@ -0,0 +1,23 @@ +import { Schema } from "effect" +import { IdentObj, IdentObjEncoded, identObjSchema } from "../ident" +import { InfixObj, InfixObjEncoded, infixObjSchema } from "../infix" +import { IntObj, IntObjEncoded, intObjSchema } from "../int" + +export type PolynomialObj = + | IntObj + | IdentObj + | InfixObj + +export type PolynomialObjEncoded = + | IntObjEncoded + | IdentObjEncoded + | InfixObjEncoded + +export const polynomialObjSchema = Schema.suspend( + (): Schema.Schema => + Schema.Union( + intObjSchema, + identObjSchema, + infixObjSchema + ), +) diff --git a/src/schemas/polynomial-operator.ts b/src/schemas/polynomial-operator.ts new file mode 100644 index 0000000..43534fe --- /dev/null +++ b/src/schemas/polynomial-operator.ts @@ -0,0 +1,12 @@ +import { Schema } from 'effect' +import { TokenType } from './token-types/union' + +export const polynomialOperatorSchema = Schema.Literal( + TokenType.PLUS, + TokenType.MINUS, + TokenType.ASTERISK, + TokenType.SLASH, + TokenType.EXPONENT, +) + +export type PolynomialOperator = typeof polynomialOperatorSchema.Type diff --git a/src/services/diff/obj.ts b/src/services/diff/obj.ts index 987d592..fcccb69 100644 --- a/src/services/diff/obj.ts +++ b/src/services/diff/obj.ts @@ -1,25 +1,17 @@ import { Effect, Match, Schema } from 'effect' import { - createFunctionObj, - type IntegerObj, - type IdentObj, - type InfixObj, - type Obj, createIntegerObj, createInfixObj, createIdentObj, } from '../object' -import { IdentExp } from '../../schemas/nodes/exps/ident' -import { BlockStmt } from '../../schemas/nodes/stmts/block' -import { ExpStmt } from '../../schemas/nodes/stmts/exp' -import { polynomialExpSchema } from '../../schemas/nodes/exps/unions/int-ident-infix' +import { expectIdentEquivalence, IdentExp } from '../../schemas/nodes/exps/ident' import { TokenType } from '../../schemas/token-types/union' import type { ParseError } from 'effect/ParseResult' import { KennethParseError } from '../../errors/kenneth/parse' -import type { Environment } from '../object/environment' -import { FuncExp } from '../../schemas/nodes/exps/function' - -export type PolynomialObj = IntegerObj | IdentObj | InfixObj +import { PolynomialObj, polynomialObjSchema } from '@/schemas/objs/unions/polynomial' +import { polynomialOperatorSchema } from '@/schemas/polynomial-operator' +import { IdentObj, identObjSchema } from '@/schemas/objs/ident' +import { IntObj, intObjSchema } from '@/schemas/objs/int' export const newTerm = (coeff: number, x: IdentObj, power: number) => createInfixObj( @@ -38,9 +30,9 @@ const processTerm = (exp: PolynomialObj, x: IdentExp) => Schema.Literal(TokenType.ASTERISK, TokenType.EXPONENT), )(operator) return yield* Match.value(op).pipe( - Match.when('*', () => + Match.when(TokenType.ASTERISK, () => Effect.gen(function* () { - const coeff = left as IntegerObj + const coeff = yield* Schema.decodeUnknown(intObjSchema)(left) return yield* Match.value(right).pipe( Match.tag('IdentObj', (identExp) => @@ -59,13 +51,9 @@ const processTerm = (exp: PolynomialObj, x: IdentExp) => yield* Schema.decodeUnknown(Schema.Literal('**'))( secondOperator, ) - //const secondLeftParsed = secondLeft as IdentObj - - // yield* expectIdentEquivalence(secondLeftParsed, x) - - const power = secondRight as IntegerObj + + const power = yield* Schema.decodeUnknown(intObjSchema)(secondRight) - // TODO: FIX STUPID NUMBER VALUE return newTerm( coeff.value * power.value, createIdentObj(x), @@ -79,13 +67,13 @@ const processTerm = (exp: PolynomialObj, x: IdentExp) => ) }), ), - Match.when('**', () => + Match.when(TokenType.EXPONENT, () => Effect.gen(function* () { - //const identExp = left as IdentObj + const {identExp} = yield* Schema.decodeUnknown(identObjSchema)(left) - // yield* expectIdentEquivalence(identExp, x) + yield* expectIdentEquivalence(identExp, x) - const { value } = right as IntegerObj + const { value } = right as IntObj return yield* Effect.succeed( newTerm(value, createIdentObj(x), value - 1), @@ -108,33 +96,31 @@ export const diffPolynomial = ( Match.tag('IdentObj', () => processTerm(obj, x)), // leaf Match.tag('InfixObj', (infixObj) => Effect.gen(function* () { - const left = infixObj.left as PolynomialObj + const left = yield* Schema.decodeUnknown(polynomialObjSchema)(infixObj.left) - const right = infixObj.right as PolynomialObj + const right = yield* Schema.decodeUnknown(polynomialObjSchema)(infixObj.right) - const operator = yield* Schema.decodeUnknown( - Schema.Literal('+', '*', '/', '**'), - )(infixObj.operator) + const operator = yield* Schema.decodeUnknown(polynomialOperatorSchema)(infixObj.operator) return yield* Match.value(operator).pipe( - Match.when('*', () => + Match.when(TokenType.ASTERISK, () => Match.value(obj).pipe( Match.tag('InfixObj', () => Effect.gen(function* () { if ( - (left._tag === 'InfixObj' && left.operator === '+') || - (right._tag === 'InfixObj' && right.operator === '+') + (left._tag === 'InfixObj' && left.operator === TokenType.PLUS) || + (right._tag === 'InfixObj' && right.operator === TokenType.PLUS) ) { return createInfixObj( createInfixObj( yield* diffPolynomial(left, x), - '*', + TokenType.ASTERISK, right, ), - '+', + TokenType.PLUS, createInfixObj( left, - '*', + TokenType.ASTERISK, yield* diffPolynomial(right, x), ), ) @@ -145,30 +131,30 @@ export const diffPolynomial = ( Match.orElse(() => processTerm(obj, x)), // leaf ), ), - Match.when('/', () => + Match.when(TokenType.SLASH, () => Match.value(obj).pipe( Match.tag('InfixObj', () => Effect.gen(function* () { if ( - (left._tag === 'InfixObj' && left.operator === '+') || - (right._tag === 'InfixObj' && right.operator === '+') + (left._tag === 'InfixObj' && left.operator === TokenType.PLUS) || + (right._tag === 'InfixObj' && right.operator === TokenType.PLUS) ) { return createInfixObj( createInfixObj( createInfixObj( yield* diffPolynomial(left, x), - '*', + TokenType.ASTERISK, right, ), - '-', + TokenType.MINUS, createInfixObj( left, - '*', + TokenType.ASTERISK, yield* diffPolynomial(right, x), ), ), - '/', - createInfixObj(right, '**', createIntegerObj(2)), + TokenType.SLASH, + createInfixObj(right, TokenType.EXPONENT, createIntegerObj(2)), ) } return yield* processTerm(obj, x) // leaf @@ -177,13 +163,24 @@ export const diffPolynomial = ( Match.orElse(() => processTerm(obj, x)), // leaf ), ), - Match.when('**', () => processTerm(obj, x)), - Match.when('+', () => + Match.when(TokenType.EXPONENT, () => processTerm(obj, x)), + Match.when(TokenType.PLUS, () => Effect.gen(function* () { return yield* Effect.succeed( createInfixObj( yield* diffPolynomial(left, x), - '+', + TokenType.PLUS, + yield* diffPolynomial(right, x), + ), + ) + }), + ), + Match.when(TokenType.MINUS, () => + Effect.gen(function* () { + return yield* Effect.succeed( + createInfixObj( + yield* diffPolynomial(left, x), + TokenType.PLUS, yield* diffPolynomial(right, x), ), ) @@ -195,73 +192,3 @@ export const diffPolynomial = ( ), Match.exhaustive, ) - -export const diff = (...args: Obj[]) => - Effect.gen(function* () { - // A single argument of type function with a single ident argument. - // counter factually I can also accept a fn(fn: (x: number) -> number) | fn(x: number) later is status quo. - // either way the fact remains that args should be a single function. - const { - params, - body: { token, statements }, - env, - } = (yield* Schema.decodeUnknown( - Schema.Tuple( - Schema.Struct({ - params: Schema.Tuple(Schema.Union(IdentExp, FuncExp)), - body: BlockStmt, - env: Schema.Unknown, - }), - ), - )(args))[0] - - const { token: expStmtToken, expression } = (yield* Schema.decodeUnknown( - Schema.Tuple(ExpStmt), - )(statements))[0] - - const exp = yield* Schema.decodeUnknown(polynomialExpSchema)(expression) - - return yield* Match.value(params[0]).pipe( - Match.tag('IdentExp', (x) => - Effect.gen(function* () { - return createFunctionObj( - params as unknown as IdentExp[], // TODO: HACK - new BlockStmt({ - token, - statements: [ - new ExpStmt({ - token: expStmtToken, - expression: yield* diffPolynomial(exp, x), - }), - ], - }), - env as Environment, - ) - }), - ), - Match.tag('FuncExp', (g) => - Effect.gen(function* () { - const { parameters: gParams } = g - const x = (yield* Schema.decodeUnknown(Schema.Tuple(IdentExp))( - gParams, - ))[0] - return createFunctionObj( - params as unknown as IdentExp[], // TODO: HACK - new BlockStmt({ - token, - statements: [ - new ExpStmt({ - token: expStmtToken, - expression: yield* diffPolynomial(exp, x), - }), - ], - }), - env as Environment, - ) - - // chain rule - }), - ), - Match.exhaustive, - ) - }) diff --git a/src/services/evaluator/index.ts b/src/services/evaluator/index.ts index f5f3b9e..b9ac816 100644 --- a/src/services/evaluator/index.ts +++ b/src/services/evaluator/index.ts @@ -48,6 +48,7 @@ import { diffPolynomial, type PolynomialObj } from '../diff/obj' import { nativeToIntExp } from 'src/schemas/nodes/exps/int' import { InfixExp } from 'src/schemas/nodes/exps/infix' import { logDebug } from 'effect/Effect' +import { builtInFnMap } from '../object/builtins' // TODO move diff to ENV! @@ -162,7 +163,7 @@ export const applyFunction = (fn: FunctionObj | BuiltInObj, ident: IdentExp | undefined) => (args: Obj[]) => Match.value(fn).pipe( - Match.tag('BuiltInObj', (fn) => fn.fn(...args)), + Match.tag('BuiltInObj', (fn) => builtInFnMap[fn.fn](...args)), // naming is a bit off Match.tag('FunctionObj', (fn) => extendFunctionEnv(fn, args).pipe( Effect.flatMap((env) => Eval(fn.body)(env, ident)), diff --git a/src/services/object/builtins.ts b/src/services/object/builtins.ts index 1e18207..0e150cd 100644 --- a/src/services/object/builtins.ts +++ b/src/services/object/builtins.ts @@ -54,7 +54,12 @@ const diff2 = (...args: Obj[]) => }) export const builtins = { - len: createBuiltInObj((...args: Obj[]) => + len: createBuiltInObj('len'), + diff: createBuiltInObj('diff'), +} as const + +export const builtInFnMap = { + len: (...args: Obj[]) => Effect.gen(function* () { if (args.length !== 1) { return yield* Effect.succeed( @@ -79,8 +84,7 @@ export const builtins = { ), ) }), - ), - diff: createBuiltInObj(diff2), + diff: diff2 } as const const builtinKeys = Object.keys(builtins) as (keyof typeof builtins)[] // hack diff --git a/src/services/object/index.ts b/src/services/object/index.ts index 4a86f07..37817fa 100644 --- a/src/services/object/index.ts +++ b/src/services/object/index.ts @@ -1,41 +1,35 @@ -import { Data, type Effect } from 'effect' +import { Data } from 'effect' import type { Environment } from './environment' import type { IdentExp } from 'src/schemas/nodes/exps/ident' import type { BlockStmt } from 'src/schemas/nodes/stmts/block' -import type { KennethParseError } from 'src/errors/kenneth/parse' -import type { ParseError } from 'effect/ParseResult' import type { InfixOperator } from 'src/schemas/infix-operator' -import type { InfixExp } from 'src/schemas/nodes/exps/infix' export interface InternalObj { - readonly inspect: () => string } export type Obj = Data.TaggedEnum<{ - IntegerObj: InternalObj & { + IntegerObj: { readonly value: number } - BooleanObj: InternalObj & { readonly value: boolean } - NullObj: InternalObj - ReturnObj: InternalObj & { readonly value: Obj } - ErrorObj: InternalObj & { readonly message: string } - FunctionObj: InternalObj & { + BooleanObj: { readonly value: boolean } + NullObj: {} + ReturnObj: { readonly value: Obj } + ErrorObj: { readonly message: string } + FunctionObj: { readonly params: readonly IdentExp[] readonly body: BlockStmt readonly env: Environment } - StringObj: InternalObj & { + StringObj: { readonly value: string } - BuiltInObj: InternalObj & { - readonly fn: ( - ...args: Obj[] - ) => Effect.Effect + BuiltInObj: { + readonly fn: 'len' | 'diff' } - IdentObj: InternalObj & { + IdentObj: { readonly identExp: IdentExp } - InfixObj: InternalObj & { + InfixObj: { readonly left: Obj readonly right: Obj readonly operator: InfixOperator @@ -84,13 +78,11 @@ export const objMatch = $match export const createIntegerObj = (value: number) => IntegerObj({ value, - inspect: () => String(value), }) const createBooleanObj = (value: boolean) => BooleanObj({ value, - inspect: () => String(value), }) export const FALSE = createBooleanObj(false) @@ -99,19 +91,16 @@ export const TRUE = createBooleanObj(true) export const nativeBoolToObjectBool = (input: boolean) => (input ? TRUE : FALSE) const createNullObj = () => - NullObj({ - inspect: () => 'null', - }) + NullObj() export const NULL = createNullObj() export const createReturnObj = (value: Obj) => - ReturnObj({ value, inspect: value.inspect }) + ReturnObj({ value }) export const createErrorObj = (message: string) => ErrorObj({ message, - inspect: () => `ERROR: ${message}`, }) export const createFunctionObj = ( @@ -123,33 +112,23 @@ export const createFunctionObj = ( params, body, env, - inspect: () => ` - fn (${params.map((p) => p.string()).join(', ')}) { - ${body.string()} - } - `, }) export const createStringObj = (value: string) => StringObj({ value, - inspect: () => value, }) export const createBuiltInObj = ( - fn: ( - ...args: Obj[] - ) => Effect.Effect, + fn: 'len' | 'diff' ) => BuiltInObj({ fn, - inspect: () => 'builtin function', }) export const createIdentObj = (identExp: IdentExp) => IdentObj({ identExp, - inspect: () => identExp.value, }) export const createInfixObj = ( @@ -161,5 +140,4 @@ export const createInfixObj = ( left, operator, right, - inspect: () => 'infix obj', }) From a5244bcdf2c3747987431bffb4b7024dc7631415 Mon Sep 17 00:00:00 2001 From: adr Date: Wed, 19 Feb 2025 00:56:50 -0500 Subject: [PATCH 02/10] save --- src/schemas/objs/null.ts | 9 +--- src/services/cli.ts | 5 +- src/services/diff/obj.ts | 85 +++++++++++++++++---------------- src/services/evaluator/index.ts | 6 ++- 4 files changed, 54 insertions(+), 51 deletions(-) diff --git a/src/schemas/objs/null.ts b/src/schemas/objs/null.ts index 7d31bd1..fefce97 100644 --- a/src/schemas/objs/null.ts +++ b/src/schemas/objs/null.ts @@ -1,17 +1,12 @@ import { Schema } from 'effect' -const fields = { - inpect: Schema.String, -} - -export interface NullObj extends Schema.Struct.Type { +export interface NullObj { readonly _tag: 'NullObj' } -export interface NullObjEncoded extends Schema.Struct.Type { +export interface NullObjEncoded { readonly _tag: 'NullObj' } export const nullObjSchema = Schema.TaggedStruct('NullObj', { - ...fields, }) diff --git a/src/services/cli.ts b/src/services/cli.ts index 42f4331..fa98c57 100644 --- a/src/services/cli.ts +++ b/src/services/cli.ts @@ -2,11 +2,12 @@ import { Args, Command } from '@effect/cli' import { Terminal } from '@effect/platform' import { BunContext, BunRuntime } from '@effect/platform-bun' -import { Console, Effect, Layer } from 'effect' +import { Console, Effect, Layer, Pretty } from 'effect' import { PROMPT } from './repl/constants' import { Parser } from './parser' import { Eval } from './evaluator' import { createEnvironment } from './object/environment' +import { objSchema } from '@/schemas/objs/union' // Define the top-level command const topLevel = Command.make('hello-world', {}, () => @@ -36,7 +37,7 @@ const replCommand = Command.make( yield* parser.init(input) const program = yield* parser.parseProgram const evaluation = yield* Eval(program)(env) - yield* Console.log(evaluation.inspect()) + yield* Console.log(Pretty.make(objSchema)(evaluation)) } }), // Console.log(input), diff --git a/src/services/diff/obj.ts b/src/services/diff/obj.ts index fcccb69..008dff9 100644 --- a/src/services/diff/obj.ts +++ b/src/services/diff/obj.ts @@ -12,6 +12,7 @@ import { PolynomialObj, polynomialObjSchema } from '@/schemas/objs/unions/polyno import { polynomialOperatorSchema } from '@/schemas/polynomial-operator' import { IdentObj, identObjSchema } from '@/schemas/objs/ident' import { IntObj, intObjSchema } from '@/schemas/objs/int' +import { infixObjSchema } from '@/schemas/objs/infix' export const newTerm = (coeff: number, x: IdentObj, power: number) => createInfixObj( @@ -111,19 +112,19 @@ export const diffPolynomial = ( (left._tag === 'InfixObj' && left.operator === TokenType.PLUS) || (right._tag === 'InfixObj' && right.operator === TokenType.PLUS) ) { - return createInfixObj( - createInfixObj( - yield* diffPolynomial(left, x), - TokenType.ASTERISK, - right, - ), - TokenType.PLUS, - createInfixObj( - left, - TokenType.ASTERISK, - yield* diffPolynomial(right, x), - ), - ) + return infixObjSchema.make({ + left: infixObjSchema.make({ + left: yield* diffPolynomial(left, x), + operator: TokenType.ASTERISK, + right + }), + operator: TokenType.PLUS, + right: infixObjSchema.make({ + left, + operator: TokenType.ASTERISK, + right: yield* diffPolynomial(right, x) + }) + }) } return yield* processTerm(obj, x) // leaf }), @@ -139,23 +140,27 @@ export const diffPolynomial = ( (left._tag === 'InfixObj' && left.operator === TokenType.PLUS) || (right._tag === 'InfixObj' && right.operator === TokenType.PLUS) ) { - return createInfixObj( - createInfixObj( - createInfixObj( - yield* diffPolynomial(left, x), - TokenType.ASTERISK, - right, - ), - TokenType.MINUS, - createInfixObj( - left, - TokenType.ASTERISK, - yield* diffPolynomial(right, x), - ), - ), - TokenType.SLASH, - createInfixObj(right, TokenType.EXPONENT, createIntegerObj(2)), - ) + return infixObjSchema.make({ + left: infixObjSchema.make({ + left: infixObjSchema.make({ + left: yield* diffPolynomial(left, x), + operator: TokenType.ASTERISK, + right + }), + operator: TokenType.MINUS, + right: infixObjSchema.make({ + left, + operator: TokenType.ASTERISK, + right: yield* diffPolynomial(right, x) + }) + }), + operator: TokenType.SLASH, + right: infixObjSchema.make({ + left: right, + operator: TokenType.EXPONENT, + right: intObjSchema.make({value: 2}) + }) + }) } return yield* processTerm(obj, x) // leaf }), @@ -167,22 +172,22 @@ export const diffPolynomial = ( Match.when(TokenType.PLUS, () => Effect.gen(function* () { return yield* Effect.succeed( - createInfixObj( - yield* diffPolynomial(left, x), - TokenType.PLUS, - yield* diffPolynomial(right, x), - ), + infixObjSchema.make({ + left: yield* diffPolynomial(left, x), + operator: TokenType.PLUS, + right: yield* diffPolynomial(right, x) + }) ) }), ), Match.when(TokenType.MINUS, () => Effect.gen(function* () { return yield* Effect.succeed( - createInfixObj( - yield* diffPolynomial(left, x), - TokenType.PLUS, - yield* diffPolynomial(right, x), - ), + infixObjSchema.make({ + left: yield* diffPolynomial(left, x), + operator: TokenType.PLUS, + right: yield* diffPolynomial(right, x) + }) ) }), ), diff --git a/src/services/evaluator/index.ts b/src/services/evaluator/index.ts index b9ac816..836cb79 100644 --- a/src/services/evaluator/index.ts +++ b/src/services/evaluator/index.ts @@ -6,7 +6,6 @@ import { nativeBoolToObjectBool, NULL, TRUE, - type Obj, createReturnObj, isReturnObj, createFunctionObj, @@ -49,6 +48,8 @@ import { nativeToIntExp } from 'src/schemas/nodes/exps/int' import { InfixExp } from 'src/schemas/nodes/exps/infix' import { logDebug } from 'effect/Effect' import { builtInFnMap } from '../object/builtins' +import { infixObjSchema } from '@/schemas/objs/infix' +import { Obj } from '@/schemas/objs/union' // TODO move diff to ENV! @@ -164,6 +165,7 @@ export const applyFunction = (args: Obj[]) => Match.value(fn).pipe( Match.tag('BuiltInObj', (fn) => builtInFnMap[fn.fn](...args)), // naming is a bit off + // TODO Move env to a applicator, and keep just raw data in the function obj, which can be operated on by a function. Match.tag('FunctionObj', (fn) => extendFunctionEnv(fn, args).pipe( Effect.flatMap((env) => Eval(fn.body)(env, ident)), @@ -260,7 +262,7 @@ export const evalInfixExpression = isInfixObj(left) || isInfixObj(right) ) { - return createInfixObj(left, operator, right) + return infixObjSchema.make({left, operator, right}) } if (isIntegerObj(left) && isIntegerObj(right)) { return evalIntegerInfixExpression(operator, left, right) From 923577f64ac17f5cffdd39c3c1b41968fe5ebc75 Mon Sep 17 00:00:00 2001 From: adr Date: Thu, 20 Feb 2025 22:06:21 -0500 Subject: [PATCH 03/10] remove soft --- src/services/evaluator/index.ts | 4 +- src/services/evaluator/soft.ts | 312 ----------------------------- src/services/object/environment.ts | 16 +- 3 files changed, 17 insertions(+), 315 deletions(-) delete mode 100644 src/services/evaluator/soft.ts diff --git a/src/services/evaluator/index.ts b/src/services/evaluator/index.ts index 836cb79..8be8d21 100644 --- a/src/services/evaluator/index.ts +++ b/src/services/evaluator/index.ts @@ -23,7 +23,7 @@ import { } from '../object' import { Effect, Match, Schema } from 'effect' import { Parser } from '../parser' -import { createEnvironment, type Environment } from '../object/environment' +import { createEnvironment, get, type Environment } from '../object/environment' import type { InfixOperator } from '../../schemas/infix-operator' import type { ParseError } from 'effect/ParseResult' import { KennethEvalError } from '../../errors/kenneth/eval' @@ -198,7 +198,7 @@ export const evalExpressions = ( ) export const evalIdentExpression = (ident: IdentExp, env: Environment) => - env.get(ident.value).pipe(Effect.withSpan('eval.evalIdentExpression')) + get(env)(ident.value).pipe(Effect.withSpan('eval.evalIdentExpression')) export const evalIfExpression = ( ie: IfExp, diff --git a/src/services/evaluator/soft.ts b/src/services/evaluator/soft.ts deleted file mode 100644 index 8426d24..0000000 --- a/src/services/evaluator/soft.ts +++ /dev/null @@ -1,312 +0,0 @@ -import { - createIntegerObj, - FALSE, - type IntegerObj, - isIntegerObj, - nativeBoolToObjectBool, - NULL, - TRUE, - type Obj, - createReturnObj, - isReturnObj, - createFunctionObj, - type FunctionObj, - isFunctionObj, - createStringObj, - type BuiltInObj, - isBuiltInObj, - isStringObj, - type StringObj, -} from '../object' -import { Effect, Match, Schema } from 'effect' -import { Parser } from '../parser' -import { createEnvironment, type Environment } from '../object/environment' -import type { InfixOperator } from '../../schemas/infix-operator' -import type { ParseError } from 'effect/ParseResult' -import { KennethEvalError } from '../../errors/kenneth/eval' -import type { PrefixOperator } from 'src/schemas/prefix-operator' -import type { KennethParseError } from 'src/errors/kenneth/parse' -import { TokenType } from 'src/schemas/token-types/union' -import { type KNode, matchKnode } from 'src/schemas/nodes/union' -import type { IdentExp } from 'src/schemas/nodes/exps/ident' -import type { IfExp } from 'src/schemas/nodes/exps/if' -import type { BlockStmt } from 'src/schemas/nodes/stmts/block' -import type { Program } from 'src/schemas/nodes/program' -import type { Token } from 'src/schemas/token/unions/all' -import type { Exp } from 'src/schemas/nodes/exps/union' -import type { Stmt } from 'src/schemas/nodes/stmts/union' -import { - OPERATOR_TO_FUNCTION_MAP, - STRING_OPERATOR_TO_FUNCTION_MAP, -} from './constants' -import type { DiffExp } from 'src/schemas/nodes/exps/diff' - -const nodeEvalMatch = (env: Environment, identExp?: IdentExp | undefined) => - matchKnode({ - Program: ({ statements }) => - evalProgram(statements, env).pipe(Effect.map(unwrapReturnvalue)), - IdentExp: (ident) => evalIdentExpression(ident, env), - LetStmt: ({ value, name }) => - Effect.gen(function* () { - const val = yield* Eval(value)(env) - env.set(name.value, val) - return NULL - }), - ReturnStmt: ({ value }) => - Eval(value)(env).pipe(Effect.map(createReturnObj)), - ExpStmt: ({ expression }) => Eval(expression)(env), - IntExp: ({ value }) => Effect.succeed(createIntegerObj(+value)), - PrefixExp: ({ operator, right }) => - Effect.gen(function* () { - const r = yield* Eval(right)(env) - return yield* evalPrefixExpression(operator)(r) - }), - InfixExp: ({ left, operator, right }) => - Effect.gen(function* () { - const leftEval = yield* Eval(left)(env) - const rightEval = yield* Eval(right)(env) - return yield* evalInfixExpression(operator)(leftEval)(rightEval) - }), - BoolExp: ({ value }) => Effect.succeed(nativeBoolToObjectBool(value)), - IfExp: (ie) => evalIfExpression(ie, env), - BlockStmt: (stmt) => evalBlockStatement(stmt, env), - FuncExp: ({ parameters, body }) => - Effect.succeed(createFunctionObj(parameters, body, env)), - CallExp: ({ fn, args }) => - Effect.gen(function* () { - const fnEval = yield* Eval(fn)(env) - const argsEval = yield* evalExpressions(args, env) - - return yield* isFunctionObj(fnEval) || isBuiltInObj(fnEval) - ? applyFunction(fnEval)(argsEval) - : new KennethEvalError({ message: `not a function: ${fnEval._tag}` }) - }), - StrExp: ({ value }) => Effect.succeed(createStringObj(value)), - DiffExp: (diffExp) => evalDiff(diffExp)(env), - }) - -export const Eval = - (node: KNode) => - ( - env: Environment, - identExp?: IdentExp | undefined, - ): Effect.Effect< - Obj, - KennethEvalError | ParseError | KennethParseError, - never - > => - nodeEvalMatch(env, ident)(node).pipe(Effect.withSpan('eval.Eval')) - -export const evalDiff = (diffExp: DiffExp) => (env: Environment) => - Effect.gen(function* () { - // this is during running this function. the diff function to be sure! so it will return a number - // NEED to do a soft eval of all the expressions here. - // need a way to indicate the **special** variable. - const softEval = yield* Eval(diffExp.exp)(env, diffExp.params[0]) - }) - -export const applyFunction = (fn: FunctionObj | BuiltInObj) => (args: Obj[]) => - Match.value(fn).pipe( - Match.tag('BuiltInObj', (fn) => fn.fn(...args)), - Match.tag('FunctionObj', (fn) => - extendFunctionEnv(fn, args).pipe( - Effect.flatMap((env) => Eval(fn.body)(env)), - Effect.map(unwrapReturnvalue), - ), - ), - Match.exhaustive, - Effect.withSpan('eval.applyFunction'), - ) - -export const extendFunctionEnv = (fn: FunctionObj, args: Obj[]) => - Effect.gen(function* () { - const env = createEnvironment(fn.env) - for (let i = 0; i < fn.params.length; i++) { - env.set(fn.params[i].value, args[i]) - } - return yield* Effect.succeed(env) - }).pipe(Effect.withSpan('eval.extendFunctionEnv')) - -export const unwrapReturnvalue = (obj: Obj) => - isReturnObj(obj) ? obj.value : obj - -export const evalExpressions = (exps: readonly Exp[], env: Environment) => - Effect.all(exps.map((exp) => Eval(exp)(env))).pipe( - Effect.withSpan('eval.evalExpressions'), - ) - -export const evalIdentExpression = (ident: IdentExp, env: Environment) => - env.get(ident.value).pipe(Effect.withSpan('eval.evalIdentExpression')) - -export const evalIfExpression = (ie: IfExp, env: Environment) => - Effect.gen(function* () { - const condition = yield* Eval(ie.condition)(env).pipe(Effect.map(isTruthy)) - return condition - ? yield* Eval(ie.consequence)(env) - : ie.alternative - ? yield* Eval(ie.alternative)(env) - : NULL - }).pipe(Effect.withSpan('eval.evalIfExpression')) - -export const isTruthy = (obj: Obj) => - Match.value(obj).pipe( - Match.tag('NullObj', () => false), - Match.tag('BooleanObj', (obj) => obj.value), - Match.orElse(() => true), - ) - -const evalStatements = (stmts: readonly Stmt[], env: Environment) => - Effect.gen(function* () { - let result: Obj = NULL - for (const stmt of stmts) { - result = yield* Eval(stmt)(env) - if (isReturnObj(result)) { - return result - } - } - return result - }) - -export const evalProgram = (stmts: readonly Stmt[], env: Environment) => - evalStatements(stmts, env).pipe(Effect.withSpan('eval.evalProgram')) - -export const evalBlockStatement = (block: BlockStmt, env: Environment) => - evalStatements(block.statements, env).pipe( - Effect.withSpan('eval.evalBlockStatement'), - ) - -export const evalInfixExpression = - (operator: InfixOperator) => (left: Obj) => (right: Obj) => - Effect.gen(function* () { - if (isIntegerObj(left) && isIntegerObj(right)) { - return evalIntegerInfixExpression(operator, left, right) - } - if (isStringObj(left) && isStringObj(right)) { - const plus = yield* Schema.decodeUnknown( - Schema.Literal(TokenType.PLUS), - )(operator) - return evalStringInfixExpression(plus, left, right) - } - if (operator === TokenType.EQ) { - return nativeBoolToObjectBool(left === right) // we do object equality I guess? - } - if (operator === TokenType.NOT_EQ) { - return nativeBoolToObjectBool(left !== right) - } - return yield* new KennethEvalError({ - message: - left._tag !== right._tag - ? `type mismatch: ${left._tag} ${operator} ${right._tag}` - : `unknown operator: ${left._tag} ${operator} ${right._tag}`, - }) - }).pipe(Effect.withSpan('eval.evalInfixExpression')) - -const nativeToObj = (result: number | boolean | string) => - Match.value(result).pipe( - Match.when(Match.boolean, (bool) => nativeBoolToObjectBool(bool)), - Match.when(Match.number, (num) => createIntegerObj(num)), - Match.when(Match.string, (str) => createStringObj(str)), - Match.exhaustive, - ) - -export const evalIntegerInfixExpression = ( - operator: InfixOperator, - left: IntegerObj, - right: IntegerObj, -) => nativeToObj(OPERATOR_TO_FUNCTION_MAP[operator](left.value, right.value)) - -export const evalStringInfixExpression = ( - operator: typeof TokenType.PLUS, - left: StringObj, - right: StringObj, -) => - nativeToObj( - STRING_OPERATOR_TO_FUNCTION_MAP[operator](left.value, right.value), - ) - -export const evalPrefixExpression = - (operator: PrefixOperator) => (right: Obj) => - Match.value(operator).pipe( - Match.when(TokenType.BANG, () => evalBangOperatorExpression(right)), - Match.when(TokenType.MINUS, () => - Match.value(right).pipe( - Match.tag('IntegerObj', (intObj) => - evalMinusPrefixOperatorExpression(intObj), - ), - Match.orElse( - () => - new KennethEvalError({ - message: `unknown operator: -${right._tag}`, - }), - ), - ), - ), - Match.exhaustive, - ) - -export const evalMinusPrefixOperatorExpression = (right: IntegerObj) => - Effect.succeed(createIntegerObj(-right.value)) - -export const evalBangOperatorExpression = (right: Obj) => - Effect.succeed( - Match.value(right).pipe( - Match.tag('BooleanObj', (obj) => (obj.value ? FALSE : TRUE)), - Match.tag('NullObj', () => TRUE), - Match.orElse(() => FALSE), - ), - ).pipe(Effect.withSpan('eval.evalBangOperatorExpression')) - -export type ProgramInterpretation = { - program: Program - evaluation: Obj - lexerStory: { - input: string - tokens: Token[] - pos1History: number[][] - pos2History: number[][] - } -} - -export class SoftEvaluator extends Effect.Service()( - 'SoftEvaluator', - { - effect: Effect.gen(function* () { - const parser = yield* Parser - const run = (input: string) => - Effect.gen(function* () { - yield* parser.init(input) - const program = yield* parser.parseProgramOptimized - const env = createEnvironment() - return yield* nodeEvalMatch(env)(program) - }).pipe(Effect.withSpan('eval.run')) - - const runAndInterpret = ( - input: string, - ): Effect.Effect< - ProgramInterpretation, - ParseError | KennethEvalError | KennethParseError, - never - > => - Effect.gen(function* () { - yield* parser.init(input) - const program = yield* parser.parseProgram - const lexerStory = yield* parser.getLexerStory - const parserStory = yield* parser.getParserStory - const env = createEnvironment() - const evaluation = yield* nodeEvalMatch(env)(program) - return { - program, - evaluation, - lexerStory, - parserStory, - } - }) - - return { - run, - runAndInterpret, - } - }), - dependencies: [Parser.Default], - }, -) {} diff --git a/src/services/object/environment.ts b/src/services/object/environment.ts index 0c0a56f..4702879 100644 --- a/src/services/object/environment.ts +++ b/src/services/object/environment.ts @@ -6,6 +6,20 @@ import type { ParseError } from 'effect/ParseResult' // this should also be a class/service +export const get = (env: Environment) => (key: string) => Effect.gen(function* () { + return ( + env.store.get(key) ?? + (env.outer + ? yield* env.outer.get(key) + : builtins[yield* Schema.decodeUnknown(builtinsKeySchema)(key)]) + ) + }) + +export const set = (env: Environment) => (key: string, value: Obj) => { + env.store.set(key, value) + return value +} + export type Environment = { store: Map outer: Environment | undefined @@ -38,7 +52,7 @@ export const createEnvironment = ( export const printStore = (env: Environment): string => { let output = 'Environment Store:\n' env.store.forEach((value, key) => { - output += `${key}: ${value.inspect()}\n` + output += `${key}: ${value}\n` }) if (env.outer) { From 8a05fdb9759c2ba5f8b9529b7864304237b2dcc9 Mon Sep 17 00:00:00 2001 From: adr Date: Thu, 20 Feb 2025 22:29:48 -0500 Subject: [PATCH 04/10] separating functions and data --- src/schemas/nodes/exps/diff.ts | 1 - src/services/evaluator/index.ts | 10 +++--- src/services/object/environment.ts | 50 +++++++++++++++--------------- 3 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/schemas/nodes/exps/diff.ts b/src/schemas/nodes/exps/diff.ts index 146c7a2..1081c35 100644 --- a/src/schemas/nodes/exps/diff.ts +++ b/src/schemas/nodes/exps/diff.ts @@ -16,7 +16,6 @@ export class DiffExp extends Schema.TaggedClass()('DiffExp', { params: Schema.Array( Schema.suspend((): Schema.Schema => IdentExp), ), - // x: Schema.suspend((): Schema.Schema => IdentExp), }) { tokenLiteral() { return this.token.literal diff --git a/src/services/evaluator/index.ts b/src/services/evaluator/index.ts index 8be8d21..ea0c46d 100644 --- a/src/services/evaluator/index.ts +++ b/src/services/evaluator/index.ts @@ -23,7 +23,7 @@ import { } from '../object' import { Effect, Match, Schema } from 'effect' import { Parser } from '../parser' -import { createEnvironment, get, type Environment } from '../object/environment' +import { createEnvironment, get, set, type Environment } from '../object/environment' import type { InfixOperator } from '../../schemas/infix-operator' import type { ParseError } from 'effect/ParseResult' import { KennethEvalError } from '../../errors/kenneth/eval' @@ -43,13 +43,13 @@ import { STRING_OPERATOR_TO_FUNCTION_MAP, } from './constants' import type { DiffExp } from 'src/schemas/nodes/exps/diff' -import { diffPolynomial, type PolynomialObj } from '../diff/obj' +import { diffPolynomial } from '../diff/obj' import { nativeToIntExp } from 'src/schemas/nodes/exps/int' import { InfixExp } from 'src/schemas/nodes/exps/infix' -import { logDebug } from 'effect/Effect' import { builtInFnMap } from '../object/builtins' import { infixObjSchema } from '@/schemas/objs/infix' import { Obj } from '@/schemas/objs/union' +import { PolynomialObj } from '@/schemas/objs/unions/polynomial' // TODO move diff to ENV! @@ -64,7 +64,7 @@ const nodeEvalMatch = (env: Environment, identExp: IdentExp | undefined) => LetStmt: ({ value, name }) => Effect.gen(function* () { const val = yield* Eval(value)(env, identExp) - env.set(name.value, val) + set(env)(name.value, val) return NULL }), ReturnStmt: ({ value }) => @@ -180,7 +180,7 @@ export const extendFunctionEnv = (fn: FunctionObj, args: Obj[]) => Effect.gen(function* () { const env = createEnvironment(fn.env) for (let i = 0; i < fn.params.length; i++) { - env.set(fn.params[i].value, args[i]) + set(env)(fn.params[i].value, args[i]) } return yield* Effect.succeed(env) }).pipe(Effect.withSpan('eval.extendFunctionEnv')) diff --git a/src/services/object/environment.ts b/src/services/object/environment.ts index 4702879..69e81b5 100644 --- a/src/services/object/environment.ts +++ b/src/services/object/environment.ts @@ -1,16 +1,16 @@ import { Effect, Schema } from 'effect' -import type { Obj } from '.' -import type { KennethEvalError } from 'src/errors/kenneth/eval' import { builtins, builtinsKeySchema } from './builtins' -import type { ParseError } from 'effect/ParseResult' +import { Obj, ObjEncoded, objSchema } from '@/schemas/objs/union' +import { ParseError } from 'effect/ParseResult' +import { KennethEvalError } from '@/errors/kenneth/eval' // this should also be a class/service -export const get = (env: Environment) => (key: string) => Effect.gen(function* () { +export const get = (env: Environment) => (key: string): Effect.Effect => Effect.gen(function* () { return ( env.store.get(key) ?? (env.outer - ? yield* env.outer.get(key) + ? yield* get(env.outer)(key) : builtins[yield* Schema.decodeUnknown(builtinsKeySchema)(key)]) ) }) @@ -20,33 +20,33 @@ export const set = (env: Environment) => (key: string, value: Obj) => { return value } -export type Environment = { - store: Map - outer: Environment | undefined - get: (key: string) => Effect.Effect - set: (key: string, value: Obj) => Obj + +export interface Environment { + readonly outer: Environment | undefined + readonly store: Map +} + +export interface EnvironmentEncoded { + readonly outer: Environment | undefined + readonly store: Map } + +export const environmentSchema = Schema.Struct({ + store: Schema.suspend((): Schema.Schema, Map> => Schema.Map({key: Schema.String, value: objSchema})), + outer: Schema.suspend((): Schema.Schema => environmentSchema) +}) + +// export type Environment = { +// store: Map +// outer: Environment | undefined +// } + export const createEnvironment = ( outer?: Environment | undefined, ): Environment => ({ store: new Map(), outer, - get: function (key: string) { - const store = this.store - return Effect.gen(function* () { - return ( - store.get(key) ?? - (outer - ? yield* outer.get(key) - : builtins[yield* Schema.decodeUnknown(builtinsKeySchema)(key)]) - ) - }) - }, - set: function (key: string, value: Obj) { - this.store.set(key, value) - return value - }, }) export const printStore = (env: Environment): string => { From a13f931a7be24b50fd08b5ce4f8a902ebc4b26af Mon Sep 17 00:00:00 2001 From: adr Date: Thu, 20 Feb 2025 22:58:28 -0500 Subject: [PATCH 05/10] fix type --- src/schemas/nodes/exps/diff.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/schemas/nodes/exps/diff.ts b/src/schemas/nodes/exps/diff.ts index 1081c35..e95f699 100644 --- a/src/schemas/nodes/exps/diff.ts +++ b/src/schemas/nodes/exps/diff.ts @@ -7,7 +7,7 @@ export type DiffExpEncoded = { readonly _tag: 'DiffExp' readonly token: DiffToken readonly exp: ExpEncoded - readonly params: readonly IdentExp[] + readonly params: readonly IdentExpEncoded[] } export class DiffExp extends Schema.TaggedClass()('DiffExp', { From 0372e43e4fe1053a90e31e390e41536acf31be5e Mon Sep 17 00:00:00 2001 From: adr Date: Thu, 20 Feb 2025 23:14:51 -0500 Subject: [PATCH 06/10] fixed types --- src/services/diff/obj.ts | 37 +++++++++++++++--------------- src/services/object/environment.ts | 4 ++-- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/services/diff/obj.ts b/src/services/diff/obj.ts index 008dff9..427dab6 100644 --- a/src/services/diff/obj.ts +++ b/src/services/diff/obj.ts @@ -1,30 +1,29 @@ import { Effect, Match, Schema } from 'effect' -import { - createIntegerObj, - createInfixObj, - createIdentObj, -} from '../object' import { expectIdentEquivalence, IdentExp } from '../../schemas/nodes/exps/ident' import { TokenType } from '../../schemas/token-types/union' import type { ParseError } from 'effect/ParseResult' import { KennethParseError } from '../../errors/kenneth/parse' import { PolynomialObj, polynomialObjSchema } from '@/schemas/objs/unions/polynomial' -import { polynomialOperatorSchema } from '@/schemas/polynomial-operator' import { IdentObj, identObjSchema } from '@/schemas/objs/ident' import { IntObj, intObjSchema } from '@/schemas/objs/int' -import { infixObjSchema } from '@/schemas/objs/infix' +import { InfixObj, infixObjSchema } from '@/schemas/objs/infix' +import { polynomialOperatorSchema } from '@/schemas/polynomial-operator' export const newTerm = (coeff: number, x: IdentObj, power: number) => - createInfixObj( - createIntegerObj(coeff), - '*', - createInfixObj(x, '**', createIntegerObj(power)), - ) + infixObjSchema.make({ + left: intObjSchema.make({value: coeff}), + operator: '*', + right: infixObjSchema.make({ + left: x, + operator: '**', + right: intObjSchema.make({value:power}) + }) + }) -const processTerm = (exp: PolynomialObj, x: IdentExp) => - Match.value(exp).pipe( - Match.tag('IntegerObj', () => Effect.succeed(createIntegerObj(0))), - Match.tag('IdentObj', () => Effect.succeed(createIntegerObj(1))), +const processTerm = (obj: PolynomialObj, x: IdentExp) => + Match.value(obj).pipe( + Match.tag('IntegerObj', () => Effect.succeed(intObjSchema.make({value: 0}))), + Match.tag('IdentObj', () => Effect.succeed(intObjSchema.make({value: 1}))), Match.tag('InfixObj', ({ left, operator, right }) => Effect.gen(function* () { const op = yield* Schema.decodeUnknown( @@ -57,7 +56,7 @@ const processTerm = (exp: PolynomialObj, x: IdentExp) => return newTerm( coeff.value * power.value, - createIdentObj(x), + identObjSchema.make({identExp: x}), power.value - 1, ) }), @@ -77,7 +76,7 @@ const processTerm = (exp: PolynomialObj, x: IdentExp) => const { value } = right as IntObj return yield* Effect.succeed( - newTerm(value, createIdentObj(x), value - 1), + newTerm(value, identObjSchema.make({identExp: x}), value - 1), ) }), ), @@ -196,4 +195,4 @@ export const diffPolynomial = ( }), ), Match.exhaustive, - ) + ) \ No newline at end of file diff --git a/src/services/object/environment.ts b/src/services/object/environment.ts index 69e81b5..0278039 100644 --- a/src/services/object/environment.ts +++ b/src/services/object/environment.ts @@ -27,14 +27,14 @@ export interface Environment { } export interface EnvironmentEncoded { - readonly outer: Environment | undefined + readonly outer: EnvironmentEncoded | undefined readonly store: Map } export const environmentSchema = Schema.Struct({ store: Schema.suspend((): Schema.Schema, Map> => Schema.Map({key: Schema.String, value: objSchema})), - outer: Schema.suspend((): Schema.Schema => environmentSchema) + outer: Schema.Union(Schema.suspend((): Schema.Schema => environmentSchema), Schema.Undefined) }) // export type Environment = { From cedac3ed2983f158b9c8c6168fdc6b73a37bfdc9 Mon Sep 17 00:00:00 2001 From: adr Date: Thu, 20 Feb 2025 23:16:27 -0500 Subject: [PATCH 07/10] delete unused --- src/services/parser/index.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/services/parser/index.ts b/src/services/parser/index.ts index c2fd90f..b7722a7 100644 --- a/src/services/parser/index.ts +++ b/src/services/parser/index.ts @@ -46,8 +46,6 @@ import { TokenType } from 'src/schemas/token-types/union' import type { Token } from 'src/schemas/token/unions/all' import type { Exp } from 'src/schemas/nodes/exps/union' import { constantFoldingOverStmt } from './constant-folding' -import type { DiffToken } from 'src/schemas/token/diff' -import { DiffExp } from 'src/schemas/nodes/exps/diff' export class Parser extends Effect.Service()('Parser', { effect: Effect.gen(function* () { @@ -213,18 +211,6 @@ export class Parser extends Effect.Service()('Parser', { }) }) - const parseDiff = (curToken: DiffToken) => - Effect.gen(function* () { - yield* expectPeek(TokenType.LPAREN) - const fn = yield* parseFunctionParameters - yield* expectPeek(TokenType.LBRACE) - - return new DiffExp({ - token: curToken, - fn, - }) - }) - const parseStringLiteral = (curToken: StringToken) => Effect.gen(function* () { return yield* Effect.succeed( From 6ebb55d2741a4cddf94169c1c41cb578d68d3900 Mon Sep 17 00:00:00 2001 From: adr Date: Fri, 21 Feb 2025 00:27:46 -0500 Subject: [PATCH 08/10] save --- biome.json | 17 +++ src/schemas/objs/function.ts | 13 ++- src/schemas/objs/null.ts | 2 + src/schemas/objs/string.ts | 2 +- src/schemas/objs/union.ts | 29 +++++- src/services/cli.ts | 2 +- src/services/diff/obj.ts | 22 ++-- src/services/evaluator/index.ts | 129 ++++++++++++----------- src/services/object/builtins.ts | 32 +++--- src/services/object/index.ts | 143 -------------------------- src/tests/evaluator/evaluator.test.ts | 18 ++-- src/tests/evaluator/utils.ts | 31 +++--- 12 files changed, 171 insertions(+), 269 deletions(-) create mode 100644 biome.json delete mode 100644 src/services/object/index.ts diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..a206a11 --- /dev/null +++ b/biome.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "formatter": { + "enabled": false + }, + "javascript": { + "formatter": { + "quoteStyle": "single" + } + }, + "linter": { + "enabled": false + }, + "organizeImports": { + "enabled": false + } +} \ No newline at end of file diff --git a/src/schemas/objs/function.ts b/src/schemas/objs/function.ts index 6abbcf4..32f15fa 100644 --- a/src/schemas/objs/function.ts +++ b/src/schemas/objs/function.ts @@ -1,29 +1,28 @@ import { Schema } from 'effect' import { IdentExp, type IdentExpEncoded } from '../nodes/exps/ident' import { BlockStmt, type BlockStmtEncoded } from '../nodes/stmts/block' +import { Environment, EnvironmentEncoded, environmentSchema } from '@/services/object/environment' -const fields = { - env: Schema.Unknown, -} - -export interface FunctionObj extends Schema.Struct.Type { +export interface FunctionObj { readonly _tag: 'FunctionObj' readonly params: readonly IdentExp[] readonly body: BlockStmt + readonly env: Environment } -export interface FunctionObjEncoded extends Schema.Struct.Type { +export interface FunctionObjEncoded { readonly _tag: 'FunctionObj' readonly params: readonly IdentExpEncoded[] readonly body: BlockStmtEncoded + readonly env: EnvironmentEncoded } export const functionObjSchema = Schema.TaggedStruct('FunctionObj', { - ...fields, params: Schema.Array( Schema.suspend((): Schema.Schema => IdentExp), ), body: Schema.suspend( (): Schema.Schema => BlockStmt, ), + env: Schema.suspend((): Schema.Schema => environmentSchema) }) diff --git a/src/schemas/objs/null.ts b/src/schemas/objs/null.ts index fefce97..748a09a 100644 --- a/src/schemas/objs/null.ts +++ b/src/schemas/objs/null.ts @@ -10,3 +10,5 @@ export interface NullObjEncoded { export const nullObjSchema = Schema.TaggedStruct('NullObj', { }) + +export const NULL = nullObjSchema.make() \ No newline at end of file diff --git a/src/schemas/objs/string.ts b/src/schemas/objs/string.ts index 61e91da..dc4eebf 100644 --- a/src/schemas/objs/string.ts +++ b/src/schemas/objs/string.ts @@ -1,7 +1,7 @@ import { Schema } from 'effect' const fields = { - vlue: Schema.String, + value: Schema.String, } export interface StringObj extends Schema.Struct.Type { diff --git a/src/schemas/objs/union.ts b/src/schemas/objs/union.ts index 7832b29..7f9c665 100644 --- a/src/schemas/objs/union.ts +++ b/src/schemas/objs/union.ts @@ -1,4 +1,4 @@ -import { Schema } from 'effect' +import { Data, Pretty, Schema } from 'effect' import { type BoolObjEncoded, boolObjSchema, type BoolObj } from './bool' import { type BuiltInObjEncoded, @@ -65,3 +65,30 @@ export const objSchema = Schema.suspend( infixObjSchema ), ) + +export const prettyObj = Pretty.make(objSchema) + +const { + $is, + $match, +} = Data.taggedEnum() + +export const isIntegerObj = $is('IntegerObj') +export const isBooleanObj = $is('BooleanObj') +export const isNullObj = $is('NullObj') +export const isReturnObj = $is('ReturnObj') +export const isErrorObj = $is('ErrorObj') +export const isFunctionObj = $is('FunctionObj') +export const isStringObj = $is('StringObj') +export const isBuiltInObj = $is('BuiltInObj') +export const isIdentObj = $is('IdentObj') +export const isInfixObj = $is('InfixObj') + +export const objMatch = $match + +export const isObj = $is + +export const FALSE = boolObjSchema.make({value: false}) +export const TRUE = boolObjSchema.make({value: true}) + +export const nativeBoolToObjectBool = (input: boolean) => (input ? TRUE : FALSE) \ No newline at end of file diff --git a/src/services/cli.ts b/src/services/cli.ts index fa98c57..85bb357 100644 --- a/src/services/cli.ts +++ b/src/services/cli.ts @@ -36,7 +36,7 @@ const replCommand = Command.make( const parser = yield* Parser yield* parser.init(input) const program = yield* parser.parseProgram - const evaluation = yield* Eval(program)(env) + const evaluation = yield* Eval(program)(env, undefined) yield* Console.log(Pretty.make(objSchema)(evaluation)) } }), diff --git a/src/services/diff/obj.ts b/src/services/diff/obj.ts index 427dab6..6e7b07e 100644 --- a/src/services/diff/obj.ts +++ b/src/services/diff/obj.ts @@ -6,7 +6,7 @@ import { KennethParseError } from '../../errors/kenneth/parse' import { PolynomialObj, polynomialObjSchema } from '@/schemas/objs/unions/polynomial' import { IdentObj, identObjSchema } from '@/schemas/objs/ident' import { IntObj, intObjSchema } from '@/schemas/objs/int' -import { InfixObj, infixObjSchema } from '@/schemas/objs/infix' +import { infixObjSchema } from '@/schemas/objs/infix' import { polynomialOperatorSchema } from '@/schemas/polynomial-operator' export const newTerm = (coeff: number, x: IdentObj, power: number) => @@ -154,7 +154,7 @@ export const diffPolynomial = ( }) }), operator: TokenType.SLASH, - right: infixObjSchema.make({ + right: infixObjSchema.make({ left: right, operator: TokenType.EXPONENT, right: intObjSchema.make({value: 2}) @@ -169,15 +169,15 @@ export const diffPolynomial = ( ), Match.when(TokenType.EXPONENT, () => processTerm(obj, x)), Match.when(TokenType.PLUS, () => - Effect.gen(function* () { + Effect.gen(function*() { return yield* Effect.succeed( - infixObjSchema.make({ - left: yield* diffPolynomial(left, x), - operator: TokenType.PLUS, - right: yield* diffPolynomial(right, x) - }) - ) - }), + infixObjSchema.make({ + left: yield* diffPolynomial(left, x), + operator: TokenType.PLUS, + right: yield* diffPolynomial(right, x) + }) + ) + }) ), Match.when(TokenType.MINUS, () => Effect.gen(function* () { @@ -188,7 +188,7 @@ export const diffPolynomial = ( right: yield* diffPolynomial(right, x) }) ) - }), + }) ), Match.exhaustive, ) diff --git a/src/services/evaluator/index.ts b/src/services/evaluator/index.ts index ea0c46d..6a7415b 100644 --- a/src/services/evaluator/index.ts +++ b/src/services/evaluator/index.ts @@ -1,26 +1,25 @@ -import { - createIntegerObj, - FALSE, - type IntegerObj, - isIntegerObj, - nativeBoolToObjectBool, - NULL, - TRUE, - createReturnObj, - isReturnObj, - createFunctionObj, - type FunctionObj, - isFunctionObj, - createStringObj, - type BuiltInObj, - isBuiltInObj, - isStringObj, - type StringObj, - createIdentObj, - isIdentObj, - createInfixObj, - isInfixObj, -} from '../object' +// import { +// createIntegerObj, +// FALSE, +// type IntegerObj, +// isIntegerObj, +// nativeBoolToObjectBool, +// NULL, +// TRUE, +// createReturnObj, +// isReturnObj, +// createFunctionObj, +// type FunctionObj, +// isFunctionObj, +// createStringObj, +// type BuiltInObj, +// isBuiltInObj, +// isStringObj, +// type StringObj, +// createIdentObj, +// isIdentObj, +// isInfixObj, +// } from '../object' import { Effect, Match, Schema } from 'effect' import { Parser } from '../parser' import { createEnvironment, get, set, type Environment } from '../object/environment' @@ -36,7 +35,7 @@ import type { IfExp } from 'src/schemas/nodes/exps/if' import type { BlockStmt } from 'src/schemas/nodes/stmts/block' import type { Program } from 'src/schemas/nodes/program' import type { Token } from 'src/schemas/token/unions/all' -import { isInfixExp, type Exp } from 'src/schemas/nodes/exps/union' +import { type Exp } from 'src/schemas/nodes/exps/union' import type { Stmt } from 'src/schemas/nodes/stmts/union' import { OPERATOR_TO_FUNCTION_MAP, @@ -48,8 +47,15 @@ import { nativeToIntExp } from 'src/schemas/nodes/exps/int' import { InfixExp } from 'src/schemas/nodes/exps/infix' import { builtInFnMap } from '../object/builtins' import { infixObjSchema } from '@/schemas/objs/infix' -import { Obj } from '@/schemas/objs/union' +import { FALSE, isBuiltInObj, isFunctionObj, isIdentObj, isInfixObj, isIntegerObj, isReturnObj, isStringObj, nativeBoolToObjectBool, Obj, TRUE } from '@/schemas/objs/union' import { PolynomialObj } from '@/schemas/objs/unions/polynomial' +import { identObjSchema } from '@/schemas/objs/ident' +import { NULL } from '@/schemas/objs/null' +import { returnObjSchema } from '@/schemas/objs/return' +import { IntObj, intObjSchema } from '@/schemas/objs/int' +import { FunctionObj, functionObjSchema } from '@/schemas/objs/function' +import { StringObj, stringObjSchema } from '@/schemas/objs/string' +import { BuiltInObj } from '@/schemas/objs/built-in' // TODO move diff to ENV! @@ -59,7 +65,7 @@ const nodeEvalMatch = (env: Environment, identExp: IdentExp | undefined) => evalProgram(statements, env).pipe(Effect.map(unwrapReturnvalue)), IdentExp: (ident) => identExp && IdentExpEq(identExp, ident) - ? Effect.succeed(createIdentObj(ident)) + ? Effect.succeed(identObjSchema.make({ identExp: ident })) : evalIdentExpression(ident, env), LetStmt: ({ value, name }) => Effect.gen(function* () { @@ -68,9 +74,9 @@ const nodeEvalMatch = (env: Environment, identExp: IdentExp | undefined) => return NULL }), ReturnStmt: ({ value }) => - Eval(value)(env, identExp).pipe(Effect.map(createReturnObj)), + Eval(value)(env, identExp).pipe(Effect.map((value) => returnObjSchema.make({ value }))), ExpStmt: ({ expression }) => Eval(expression)(env, identExp), - IntExp: ({ value }) => Effect.succeed(createIntegerObj(+value)), + IntExp: ({ value }) => Effect.succeed(intObjSchema.make({ value })), PrefixExp: ({ operator, right }) => Effect.gen(function* () { const r = yield* Eval(right)(env, identExp) @@ -88,7 +94,7 @@ const nodeEvalMatch = (env: Environment, identExp: IdentExp | undefined) => IfExp: (ie) => evalIfExpression(ie, env, identExp), BlockStmt: (stmt) => evalBlockStatement(stmt, env, identExp), FuncExp: ({ parameters, body }) => - Effect.succeed(createFunctionObj(parameters, body, env)), + Effect.succeed(functionObjSchema.make({ params: parameters, body, env })), CallExp: ({ fn, args }) => Effect.gen(function* () { const fnEval = yield* Eval(fn)(env, identExp) @@ -98,21 +104,21 @@ const nodeEvalMatch = (env: Environment, identExp: IdentExp | undefined) => ? applyFunction(fnEval, identExp)(argsEval) : new KennethEvalError({ message: `not a function: ${fnEval._tag}` }) }), - StrExp: ({ value }) => Effect.succeed(createStringObj(value)), + StrExp: ({ value }) => Effect.succeed(stringObjSchema.make({ value })), DiffExp: (diffExp) => evalDiff(diffExp)(env), }) export const Eval = (node: KNode) => - ( - env: Environment, - identExp: IdentExp | undefined, - ): Effect.Effect< - Obj, - KennethEvalError | ParseError | KennethParseError, - never - > => - nodeEvalMatch(env, identExp)(node).pipe(Effect.withSpan('eval.Eval')) + ( + env: Environment, + identExp: IdentExp | undefined, + ): Effect.Effect< + Obj, + KennethEvalError | ParseError | KennethParseError, + never + > => + nodeEvalMatch(env, identExp)(node).pipe(Effect.withSpan('eval.Eval')) export const evalDiff = (diffExp: DiffExp) => (env: Environment) => Effect.gen(function* () { @@ -162,19 +168,19 @@ export const evalDiff = (diffExp: DiffExp) => (env: Environment) => export const applyFunction = (fn: FunctionObj | BuiltInObj, ident: IdentExp | undefined) => - (args: Obj[]) => - Match.value(fn).pipe( - Match.tag('BuiltInObj', (fn) => builtInFnMap[fn.fn](...args)), // naming is a bit off - // TODO Move env to a applicator, and keep just raw data in the function obj, which can be operated on by a function. - Match.tag('FunctionObj', (fn) => - extendFunctionEnv(fn, args).pipe( - Effect.flatMap((env) => Eval(fn.body)(env, ident)), - Effect.map(unwrapReturnvalue), + (args: Obj[]) => + Match.value(fn).pipe( + Match.tag('BuiltInObj', (fn) => builtInFnMap[fn.fn](...args)), // naming is a bit off + // TODO Move env to a applicator, and keep just raw data in the function obj, which can be operated on by a function. + Match.tag('FunctionObj', (fn) => + extendFunctionEnv(fn, args).pipe( + Effect.flatMap((env) => Eval(fn.body)(env, ident)), + Effect.map(unwrapReturnvalue), + ), ), - ), - Match.exhaustive, - Effect.withSpan('eval.applyFunction'), - ) + Match.exhaustive, + Effect.withSpan('eval.applyFunction'), + ) export const extendFunctionEnv = (fn: FunctionObj, args: Obj[]) => Effect.gen(function* () { @@ -186,7 +192,10 @@ export const extendFunctionEnv = (fn: FunctionObj, args: Obj[]) => }).pipe(Effect.withSpan('eval.extendFunctionEnv')) export const unwrapReturnvalue = (obj: Obj) => - isReturnObj(obj) ? obj.value : obj + Match.value(obj).pipe( + Match.tag('ReturnObj', (returnObj) => returnObj.value), + Match.orElse(() => obj) + ) export const evalExpressions = ( exps: readonly Exp[], @@ -262,7 +271,7 @@ export const evalInfixExpression = isInfixObj(left) || isInfixObj(right) ) { - return infixObjSchema.make({left, operator, right}) + return infixObjSchema.make({ left, operator, right }) } if (isIntegerObj(left) && isIntegerObj(right)) { return evalIntegerInfixExpression(operator, left, right) @@ -290,15 +299,15 @@ export const evalInfixExpression = const nativeToObj = (result: number | boolean | string) => Match.value(result).pipe( Match.when(Match.boolean, (bool) => nativeBoolToObjectBool(bool)), - Match.when(Match.number, (num) => createIntegerObj(num)), - Match.when(Match.string, (str) => createStringObj(str)), + Match.when(Match.number, (num) => intObjSchema.make({ value: num })), + Match.when(Match.string, (str) => stringObjSchema.make({ value: str })), Match.exhaustive, ) export const evalIntegerInfixExpression = ( operator: InfixOperator, - left: IntegerObj, - right: IntegerObj, + left: IntObj, + right: IntObj, ) => nativeToObj(OPERATOR_TO_FUNCTION_MAP[operator](left.value, right.value)) export const evalStringInfixExpression = ( @@ -330,8 +339,8 @@ export const evalPrefixExpression = Match.exhaustive, ) -export const evalMinusPrefixOperatorExpression = (right: IntegerObj) => - Effect.succeed(createIntegerObj(-right.value)) +export const evalMinusPrefixOperatorExpression = (right: IntObj) => + Effect.succeed(intObjSchema.make({ value: -right.value })) export const evalBangOperatorExpression = (right: Obj) => Effect.succeed( @@ -392,4 +401,4 @@ export class Evaluator extends Effect.Service()('Evaluator', { } }), dependencies: [Parser.Default], -}) {} +}) { } diff --git a/src/services/object/builtins.ts b/src/services/object/builtins.ts index 0e150cd..3388ff6 100644 --- a/src/services/object/builtins.ts +++ b/src/services/object/builtins.ts @@ -1,16 +1,14 @@ import { Effect, Match, Schema } from 'effect' -import { - createBuiltInObj, - createErrorObj, - createFunctionObj, - createIntegerObj, - type Obj, -} from '.' import { IdentExp } from 'src/schemas/nodes/exps/ident' import { BlockStmt } from 'src/schemas/nodes/stmts/block' import { DiffExp } from 'src/schemas/nodes/exps/diff' -import type { Environment } from './environment' +import { environmentSchema } from './environment' import { ExpStmt } from 'src/schemas/nodes/stmts/exp' +import { Obj } from '@/schemas/objs/union' +import { builtInObjSchema } from '@/schemas/objs/built-in' +import { errorObjSchema } from '@/schemas/objs/error' +import { intObjSchema } from '@/schemas/objs/int' +import { functionObjSchema } from '@/schemas/objs/function' const diff2 = (...args: Obj[]) => Effect.gen(function* () { @@ -24,7 +22,7 @@ const diff2 = (...args: Obj[]) => Schema.Struct({ params: Schema.Tuple(IdentExp), body: BlockStmt, - env: Schema.Unknown, + env: environmentSchema, }), ), )(args))[0] @@ -50,12 +48,12 @@ const diff2 = (...args: Obj[]) => ], }) - return createFunctionObj(params, newBody, env as Environment) + return functionObjSchema.make({params, body: newBody, env}) }) export const builtins = { - len: createBuiltInObj('len'), - diff: createBuiltInObj('diff'), + len: builtInObjSchema.make({fn: 'len'}), + diff: builtInObjSchema.make({fn: 'diff'}), } as const export const builtInFnMap = { @@ -63,9 +61,7 @@ export const builtInFnMap = { Effect.gen(function* () { if (args.length !== 1) { return yield* Effect.succeed( - createErrorObj( - `wrong number of arguments. got=${args.length}, want=1`, - ), + errorObjSchema.make({message: `wrong number of arguments. got=${args.length}, want=1`}) ) } @@ -73,13 +69,11 @@ export const builtInFnMap = { return yield* Match.value(firstArg).pipe( Match.tag('StringObj', (strObj) => - Effect.succeed(createIntegerObj(strObj.value.length)), + Effect.succeed(intObjSchema.make({value: strObj.value.length })), ), Match.orElse(() => Effect.succeed( - createErrorObj( - `argument to "len" not supported, got ${firstArg._tag}`, - ), + errorObjSchema.make({message: `argument to "len" not supported, got ${firstArg._tag}` }) ), ), ) diff --git a/src/services/object/index.ts b/src/services/object/index.ts deleted file mode 100644 index 37817fa..0000000 --- a/src/services/object/index.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { Data } from 'effect' -import type { Environment } from './environment' -import type { IdentExp } from 'src/schemas/nodes/exps/ident' -import type { BlockStmt } from 'src/schemas/nodes/stmts/block' -import type { InfixOperator } from 'src/schemas/infix-operator' - -export interface InternalObj { -} - -export type Obj = Data.TaggedEnum<{ - IntegerObj: { - readonly value: number - } - BooleanObj: { readonly value: boolean } - NullObj: {} - ReturnObj: { readonly value: Obj } - ErrorObj: { readonly message: string } - FunctionObj: { - readonly params: readonly IdentExp[] - readonly body: BlockStmt - readonly env: Environment - } - StringObj: { - readonly value: string - } - BuiltInObj: { - readonly fn: 'len' | 'diff' - } - IdentObj: { - readonly identExp: IdentExp - } - InfixObj: { - readonly left: Obj - readonly right: Obj - readonly operator: InfixOperator - } -}> - -export type IntegerObj = Extract -export type BooleanObj = Extract -export type NullObj = Extract -export type ReturnObj = Extract -export type ErrorObj = Extract -export type FunctionObj = Extract -export type StringObj = Extract -export type BuiltInObj = Extract -export type IdentObj = Extract -export type InfixObj = Extract - -const { - $is, - $match, - IntegerObj, - BooleanObj, - NullObj, - ReturnObj, - ErrorObj, - FunctionObj, - StringObj, - BuiltInObj, - IdentObj, - InfixObj, -} = Data.taggedEnum() - -export const isIntegerObj = $is('IntegerObj') -export const isBooleanObj = $is('BooleanObj') -export const isNullObj = $is('NullObj') -export const isReturnObj = $is('ReturnObj') -export const isErrorObj = $is('ErrorObj') -export const isFunctionObj = $is('FunctionObj') -export const isStringObj = $is('StringObj') -export const isBuiltInObj = $is('BuiltInObj') -export const isIdentObj = $is('IdentObj') -export const isInfixObj = $is('InfixObj') - -export const objMatch = $match - -export const createIntegerObj = (value: number) => - IntegerObj({ - value, - }) - -const createBooleanObj = (value: boolean) => - BooleanObj({ - value, - }) - -export const FALSE = createBooleanObj(false) -export const TRUE = createBooleanObj(true) - -export const nativeBoolToObjectBool = (input: boolean) => (input ? TRUE : FALSE) - -const createNullObj = () => - NullObj() - -export const NULL = createNullObj() - -export const createReturnObj = (value: Obj) => - ReturnObj({ value }) - -export const createErrorObj = (message: string) => - ErrorObj({ - message, - }) - -export const createFunctionObj = ( - params: readonly IdentExp[], - body: BlockStmt, - env: Environment, -) => - FunctionObj({ - params, - body, - env, - }) - -export const createStringObj = (value: string) => - StringObj({ - value, - }) - -export const createBuiltInObj = ( - fn: 'len' | 'diff' -) => - BuiltInObj({ - fn, - }) - -export const createIdentObj = (identExp: IdentExp) => - IdentObj({ - identExp, - }) - -export const createInfixObj = ( - left: Obj, - operator: InfixOperator, - right: Obj, -) => - InfixObj({ - left, - operator, - right, - }) diff --git a/src/tests/evaluator/evaluator.test.ts b/src/tests/evaluator/evaluator.test.ts index 71f6a2b..4c1255e 100644 --- a/src/tests/evaluator/evaluator.test.ts +++ b/src/tests/evaluator/evaluator.test.ts @@ -20,13 +20,13 @@ import { testIdentifier } from '../parser/utils/test-identifier' import { testInfixExpression } from '../parser/utils/test-infix-expression' import { testLetStatement } from '../parser/statements/let' import type { Program } from 'src/schemas/nodes/program' -import { - createErrorObj, - isErrorObj, - isFunctionObj, - isStringObj, - type Obj, -} from 'src/services/object' +// import { +// createErrorObj, +// isErrorObj, +// isFunctionObj, +// isStringObj, +// type Obj, +// } from 'src/services/object' import { Parser } from 'src/services/parser' import { Eval, Evaluator } from 'src/services/evaluator' import { createEnvironment } from 'src/services/object/environment' @@ -37,7 +37,7 @@ import { LetStmt } from 'src/schemas/nodes/stmts/let' import { ReturnStmt } from 'src/schemas/nodes/stmts/return' import { CallExp } from 'src/schemas/nodes/exps/call' import { PrefixExp } from 'src/schemas/nodes/exps/prefix' -import { logDebug } from 'effect/Effect' +import { isErrorObj, isFunctionObj, isStringObj, Obj } from '@/schemas/objs/union' type TestSuite = { description: string @@ -686,7 +686,7 @@ describe('eval', () => { const program = yield* parser.parseProgram const env = createEnvironment() - const evaluated = yield* Eval(program)(env) + const evaluated = yield* Eval(program)(env, undefined) expect(isStringObj(evaluated)).toBe(true) if (isStringObj(evaluated)) { diff --git a/src/tests/evaluator/utils.ts b/src/tests/evaluator/utils.ts index ad30304..a307865 100644 --- a/src/tests/evaluator/utils.ts +++ b/src/tests/evaluator/utils.ts @@ -1,8 +1,7 @@ +import { NULL } from '@/schemas/objs/null' +import { Obj, prettyObj } from '@/schemas/objs/union' import { Effect, Match } from 'effect' import { KennethParseError } from 'src/errors/kenneth/parse' -import { isErrorObj, NULL, type Obj } from 'src/services/object' - -// TODO: this seems quite redundant export const testIntegerObject = (obj: Obj, expected: number) => Match.value(obj).pipe( @@ -62,23 +61,21 @@ export const testNullOject = (obj: Obj) => Effect.gen(function* () { if (obj !== NULL) { return yield* new KennethParseError({ - message: `obj is not NULL. got ${obj.inspect()}`, + message: `obj is not NULL. got ${prettyObj(obj)}`, }) } return true }) export const testErrorObject = (obj: Obj, expected: string) => - Effect.gen(function* () { - if (!isErrorObj(obj)) { - return yield* new KennethParseError({ - message: `obj is not an ErrorObj. got ${JSON.stringify(obj)}`, - }) - } - if (obj.message !== expected) { - return yield* new KennethParseError({ - message: `expect obj.message to be ${expected}. got ${obj.message}`, - }) - } - return true - }) + Match.value(obj).pipe( + Match.tag('ErrorObj', (errObj) => Effect.if(errObj.message === expected, { + onTrue: () => Effect.succeed(true), + onFalse: () => Effect.fail(new KennethParseError({ + message: `expect obj.message to be ${expected}. got ${errObj.message}`, + })) + })), + Match.orElse(() => Effect.fail(new KennethParseError({ + message: `obj is not an ErrorObj. got ${JSON.stringify(obj)}`, + }))) + ) \ No newline at end of file From e5b999768b2ed7d9458f886a7740e3cd1cafa595 Mon Sep 17 00:00:00 2001 From: adr Date: Sat, 22 Feb 2025 12:53:43 -0500 Subject: [PATCH 09/10] save --- src/tests/evaluator/evaluator.test.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/tests/evaluator/evaluator.test.ts b/src/tests/evaluator/evaluator.test.ts index 4c1255e..7fd77c5 100644 --- a/src/tests/evaluator/evaluator.test.ts +++ b/src/tests/evaluator/evaluator.test.ts @@ -38,17 +38,18 @@ import { ReturnStmt } from 'src/schemas/nodes/stmts/return' import { CallExp } from 'src/schemas/nodes/exps/call' import { PrefixExp } from 'src/schemas/nodes/exps/prefix' import { isErrorObj, isFunctionObj, isStringObj, Obj } from '@/schemas/objs/union' +import { KennethParseError } from '@/errors/kenneth/parse' type TestSuite = { description: string tests: [input: string, expected: unknown][] fn: | (( - expected: unknown, - ) => (program: Program) => Effect.Effect) + expected: string | number, + ) => (program: Program) => Effect.Effect) | (( - expected: unknown, - ) => (evaluated: Obj) => Effect.Effect) + expected: string | number, + ) => (evaluated: Obj) => Effect.Effect) } const testSuites: { @@ -123,7 +124,7 @@ const testSuites: { fn: (expected: string) => (program: Program) => Effect.gen(function* () { const actual = program.string() - yield* Effect.succeed(expect(actual).toBe(expected)) + expect(actual).toBe(expected) }), }, { From da2fc1c0342ffef3c66ef1408acbf0ced9cce83e Mon Sep 17 00:00:00 2001 From: adr Date: Sat, 22 Feb 2025 15:17:15 -0500 Subject: [PATCH 10/10] save --- src/programs/run-and-interpret.ts | 4 +- src/schemas/nodes/exps/ident.ts | 9 +- src/services/diff/index.ts | 250 ------------------ src/services/expectations/nodes/ident.ts | 62 +++++ src/tests/evaluator/evaluator.test.ts | 5 +- src/tests/parser/utils/test-identifier.ts | 32 +-- .../parser/utils/test-literal-expression.ts | 35 +-- src/vite/App.tsx | 4 +- 8 files changed, 103 insertions(+), 298 deletions(-) delete mode 100644 src/services/diff/index.ts create mode 100644 src/services/expectations/nodes/ident.ts diff --git a/src/programs/run-and-interpret.ts b/src/programs/run-and-interpret.ts index 4aea016..c7e30c4 100644 --- a/src/programs/run-and-interpret.ts +++ b/src/programs/run-and-interpret.ts @@ -1,7 +1,7 @@ import { Effect } from 'effect' import { runPromiseInDefault } from '../runtimes/default' import { Evaluator } from '../services/evaluator' -import { createErrorObj } from '@/services/object' +import { errorObjSchema } from '@/schemas/objs/error' const runAndInterpretProgram = (input: string) => Effect.gen(function* () { @@ -12,5 +12,5 @@ const runAndInterpretProgram = (input: string) => export const runAndInterpret = (input: string) => runPromiseInDefault(runAndInterpretProgram(input).pipe( Effect.catchAll((error) => { - return Effect.succeed(createErrorObj(error.message)) + return Effect.succeed(errorObjSchema.make({message: error.message})) }))) diff --git a/src/schemas/nodes/exps/ident.ts b/src/schemas/nodes/exps/ident.ts index 107913f..25ac0e3 100644 --- a/src/schemas/nodes/exps/ident.ts +++ b/src/schemas/nodes/exps/ident.ts @@ -22,14 +22,9 @@ export class IdentExp export const IdentExpEq = Schema.equivalence(IdentExp) -export const expectIdentEquivalence = (a: IdentExp, b: IdentExp) => - Effect.gen(function* () { - return !IdentExpEq(a, b) - ? yield* new KennethParseError({ +export const expectIdentEquivalence = (a: IdentExp, b: IdentExp) => Effect.fail(new KennethParseError({ message: 'we expected ident to equal x', - }) - : undefined - }) + })).pipe(Effect.unless(() => IdentExpEq(a, b))) export const nativeToIdentExp = (value: string) => new IdentExp({ diff --git a/src/services/diff/index.ts b/src/services/diff/index.ts deleted file mode 100644 index 892d24e..0000000 --- a/src/services/diff/index.ts +++ /dev/null @@ -1,250 +0,0 @@ -import { Effect, Match, Schema } from 'effect' -import { createFunctionObj, type Obj } from '../object' -import { expectIdentEquivalence, IdentExp } from 'src/schemas/nodes/exps/ident' -import { BlockStmt } from 'src/schemas/nodes/stmts/block' -import { ExpStmt } from 'src/schemas/nodes/stmts/exp' -import { - type PolynomialExp, - polynomialExpSchema, -} from 'src/schemas/nodes/exps/unions/int-ident-infix' -import { InfixExp, OpInfixExp } from 'src/schemas/nodes/exps/infix' -import { IntExp, nativeToIntExp } from 'src/schemas/nodes/exps/int' -import { TokenType } from 'src/schemas/token-types/union' -import { newTerm } from 'src/schemas/nodes/exps/unions/term' -import type { ParseError } from 'effect/ParseResult' -import type { KennethParseError } from 'src/errors/kenneth/parse' -import { isInfixExp } from 'src/schemas/nodes/exps/union' -import type { Environment } from '../object/environment' -import { FuncExp } from 'src/schemas/nodes/exps/function' - -const processTerm = (exp: IntExp | IdentExp | InfixExp, x: IdentExp) => - Match.value(exp).pipe( - Match.tag('IntExp', () => Effect.succeed(nativeToIntExp(0))), - Match.tag('IdentExp', () => Effect.succeed(nativeToIntExp(1))), - Match.tag('InfixExp', ({ left, operator, right }) => - Effect.gen(function* () { - const op = yield* Schema.decodeUnknown( - Schema.Literal(TokenType.ASTERISK, TokenType.EXPONENT), - )(operator) - return yield* Match.value(op).pipe( - Match.when('*', () => - Effect.gen(function* () { - const coeff = yield* Schema.decodeUnknown(IntExp)(left) - - const rightParsed = yield* Schema.decodeUnknown( - Schema.Union(IdentExp, InfixExp), - )(right) - - return yield* Match.value(rightParsed).pipe( - Match.tag('IdentExp', (identExp) => - Effect.gen(function* () { - yield* expectIdentEquivalence(identExp, x) - return coeff - }), - ), - Match.tag( - 'InfixExp', - ({ - left: secondLeft, - operator: secondOperator, - right: secondRight, - }) => - Effect.gen(function* () { - yield* Schema.decodeUnknown(Schema.Literal('**'))( - secondOperator, - ) - const secondLeftParsed = - yield* Schema.decodeUnknown(IdentExp)(secondLeft) - - yield* expectIdentEquivalence(secondLeftParsed, x) - - const power = - yield* Schema.decodeUnknown(IntExp)(secondRight) - - // TODO: FIX STUPID NUMBER VALUE - return newTerm( - coeff.value * power.value, - x, - power.value - 1, - ) - }), - ), - Match.exhaustive, - ) - }), - ), - Match.when('**', () => - Effect.gen(function* () { - const identExp = yield* Schema.decodeUnknown(IdentExp)(left) - - yield* expectIdentEquivalence(identExp, x) - - const { value } = yield* Schema.decodeUnknown(IntExp)(right) - - return newTerm(value, x, value - 1) - }), - ), - Match.exhaustive, - ) - }), - ), - Match.exhaustive, - ) - -const diffPolynomial = ( - exp: PolynomialExp, - x: IdentExp, -): Effect.Effect< - IdentExp | IntExp | InfixExp, - ParseError | KennethParseError, - never -> => - Match.value(exp).pipe( - Match.tag('IntExp', () => processTerm(exp, x)), // leaf - Match.tag('IdentExp', () => processTerm(exp, x)), // leaf - Match.tag('InfixExp', (infixExp) => - Effect.gen(function* () { - const left = yield* Schema.decodeUnknown(polynomialExpSchema)( - infixExp.left, - ) - - const right = yield* Schema.decodeUnknown(polynomialExpSchema)( - infixExp.right, - ) - - const operator = yield* Schema.decodeUnknown( - Schema.Literal('+', '*', '/', '**'), - )(infixExp.operator) - - return yield* Match.value(operator).pipe( - Match.when('*', () => - Match.value(exp).pipe( - Match.tag('InfixExp', () => - Effect.gen(function* () { - if ( - (isInfixExp(left) && left.operator === '+') || - (isInfixExp(right) && right.operator === '+') - ) { - return OpInfixExp('+')( - OpInfixExp('*')(yield* diffPolynomial(left, x), right), - OpInfixExp('*')(left, yield* diffPolynomial(right, x)), - ) - } - return yield* processTerm(exp, x) // leaf - }), - ), - Match.orElse(() => processTerm(exp, x)), // leaf - ), - ), - Match.when('/', () => - Match.value(exp).pipe( - Match.tag('InfixExp', () => - Effect.gen(function* () { - if ( - (isInfixExp(left) && left.operator === '+') || - (isInfixExp(right) && right.operator === '+') - ) { - return OpInfixExp('/')( - OpInfixExp('-')( - OpInfixExp('*')(yield* diffPolynomial(left, x), right), - OpInfixExp('*')(left, yield* diffPolynomial(right, x)), - ), - OpInfixExp('**')(right, nativeToIntExp(2)), - ) - } - return yield* processTerm(exp, x) // leaf - }), - ), - Match.orElse(() => processTerm(exp, x)), // leaf - ), - ), - Match.when('**', () => processTerm(exp, x)), - Match.when('+', () => - Effect.gen(function* () { - return new InfixExp({ - token: { - _tag: '+', - literal: '+', - }, - left: yield* diffPolynomial(left, x), - operator: '+', - right: yield* diffPolynomial(right, x), - }) - }), - ), - Match.exhaustive, - ) - }), - ), - Match.exhaustive, - ) - -export const diff = (...args: Obj[]) => - Effect.gen(function* () { - // A single argument of type function with a single ident argument. - // counter factually I can also accept a fn(fn: (x: number) -> number) | fn(x: number) later is status quo. - // either way the fact remains that args should be a single function. - const { - params, - body: { token, statements }, - env, - } = (yield* Schema.decodeUnknown( - Schema.Tuple( - Schema.Struct({ - params: Schema.Tuple(Schema.Union(IdentExp, FuncExp)), - body: BlockStmt, - env: Schema.Unknown, - }), - ), - )(args))[0] - - const { token: expStmtToken, expression } = (yield* Schema.decodeUnknown( - Schema.Tuple(ExpStmt), - )(statements))[0] - - const exp = yield* Schema.decodeUnknown(polynomialExpSchema)(expression) - - return yield* Match.value(params[0]).pipe( - Match.tag('IdentExp', (x) => - Effect.gen(function* () { - return createFunctionObj( - params as unknown as IdentExp[], // TODO: HACK - new BlockStmt({ - token, - statements: [ - new ExpStmt({ - token: expStmtToken, - expression: yield* diffPolynomial(exp, x), - }), - ], - }), - env as Environment, - ) - }), - ), - Match.tag('FuncExp', (g) => - Effect.gen(function* () { - const { parameters: gParams } = g - const x = (yield* Schema.decodeUnknown(Schema.Tuple(IdentExp))( - gParams, - ))[0] - return createFunctionObj( - params as unknown as IdentExp[], // TODO: HACK - new BlockStmt({ - token, - statements: [ - new ExpStmt({ - token: expStmtToken, - expression: yield* diffPolynomial(exp, x), - }), - ], - }), - env as Environment, - ) - - // chain rule - }), - ), - Match.exhaustive, - ) - }) diff --git a/src/services/expectations/nodes/ident.ts b/src/services/expectations/nodes/ident.ts new file mode 100644 index 0000000..3531795 --- /dev/null +++ b/src/services/expectations/nodes/ident.ts @@ -0,0 +1,62 @@ +import { KennethEvalError } from "@/errors/kenneth/eval"; +import { BoolExp } from "@/schemas/nodes/exps/boolean"; +import { IdentExp } from "@/schemas/nodes/exps/ident"; +import { IntExp } from "@/schemas/nodes/exps/int"; +import { StrExp } from "@/schemas/nodes/exps/str"; +import { Exp } from "@/schemas/nodes/exps/union"; +import { Effect, pipe, Schema } from "effect"; + +const expectExp = ( + exp: Exp, + schema: Schema.Schema, + expected: T['value'] +) => + pipe( + exp, + Schema.decodeUnknown(schema), + Effect.filterOrFail( + (exp) => exp.value === expected, + (exp) => new KennethEvalError({ message: `Expected '${expected}' but got '${exp.value}'` }) + ) + ); + +export const expectIdentExp = (exp: Exp, expected: string) => + expectExp(exp, IdentExp, expected); +// pipe( +// exp, +// Schema.decodeUnknown(IdentExp), +// Effect.filterOrFail( +// (exp) => exp.value === expected, +// (exp) => new KennethEvalError({message:`Expected '${exp}' but got '${exp}'`}) +// ) +// ); + +export const expectStrExp = (exp: Exp, expected: string) => + pipe( + exp, + Schema.decodeUnknown(StrExp), + Effect.filterOrFail( + (exp) => exp.value === expected, + (exp) => new KennethEvalError({message:`Expected '${expected}' but got '${exp.value}'`}) + ) + ); + +export const expectIntExp = (exp: Exp, expected: number) => + pipe( + exp, + Schema.decodeUnknown(IntExp), + Effect.filterOrFail( + (exp) => exp.value === expected, + (exp) => new KennethEvalError({message:`Expected '${expected}' but got '${exp.value}'`}) + ) + ); + +export const expectBooleanExp = (exp: Exp, expected: boolean) => + pipe( + exp, + Schema.decodeUnknown(BoolExp), + Effect.filterOrFail( + (exp) => exp.value === expected, + (exp) => new KennethEvalError({message:`Expected '${expected}' but got '${exp.value}'`}) + ) + ); diff --git a/src/tests/evaluator/evaluator.test.ts b/src/tests/evaluator/evaluator.test.ts index 7fd77c5..1cd9b3f 100644 --- a/src/tests/evaluator/evaluator.test.ts +++ b/src/tests/evaluator/evaluator.test.ts @@ -39,6 +39,7 @@ import { CallExp } from 'src/schemas/nodes/exps/call' import { PrefixExp } from 'src/schemas/nodes/exps/prefix' import { isErrorObj, isFunctionObj, isStringObj, Obj } from '@/schemas/objs/union' import { KennethParseError } from '@/errors/kenneth/parse' +import { errorObjSchema } from '@/schemas/objs/error' type TestSuite = { description: string @@ -608,12 +609,12 @@ for (const { name, kind, suite } of testSuites) { Effect.catchAll((error) => { console.log('error', error) if (error._tag === 'KennethEvalError') { - return Effect.succeed(createErrorObj(error.message)).pipe( + return Effect.succeed(errorObjSchema.make({message: error.message})).pipe( Effect.tap(Effect.logDebug('blew up')), ) } if (error._tag === 'ParseError') { - return Effect.succeed(createErrorObj(error.message)) + return Effect.succeed(errorObjSchema.make({message: error.message})) } return expect(true).toBe(false) diff --git a/src/tests/parser/utils/test-identifier.ts b/src/tests/parser/utils/test-identifier.ts index a2d81e4..3d39598 100644 --- a/src/tests/parser/utils/test-identifier.ts +++ b/src/tests/parser/utils/test-identifier.ts @@ -1,21 +1,17 @@ -import { Effect } from 'effect' +import { expectIdentEquivalence, nativeToIdentExp } from '@/schemas/nodes/exps/ident' +import { Effect, Match } from 'effect' import { KennethParseError } from 'src/errors/kenneth/parse' import type { Exp } from 'src/schemas/nodes/exps/union' -import { isIdentExpression } from 'src/services/ast' -export const testIdentifier = (expression: Exp, value: string) => - Effect.gen(function* () { - return yield* !isIdentExpression(expression) - ? new KennethParseError({ - message: `Expected expression to be IdentExpression got ${expression.string()}`, - }) - : expression.value !== value - ? new KennethParseError({ - message: `Expected identExpression.Value to be ${value}, got ${expression.value}`, - }) - : expression.tokenLiteral() !== value - ? new KennethParseError({ - message: `Expected ident.TokenLiteral to be ${value}, got ${expression.tokenLiteral()}`, - }) - : Effect.succeed(true) - }) +export const testIdentifier = (expression: Exp, value: string) => + Match.value(expression).pipe( + Match.tag('IdentExp', (identExp) => + expectIdentEquivalence(identExp, nativeToIdentExp(value)) + ), + Match.orElse(() => Effect.fail(new KennethParseError({ + message: `Expected expression to be IdentExpression got ${expression.string()}`, // string should be replaced by pretty + }))) // doing this by hand sucksssss + ) + + +export \ No newline at end of file diff --git a/src/tests/parser/utils/test-literal-expression.ts b/src/tests/parser/utils/test-literal-expression.ts index e60a999..28b8994 100644 --- a/src/tests/parser/utils/test-literal-expression.ts +++ b/src/tests/parser/utils/test-literal-expression.ts @@ -1,22 +1,23 @@ +import { IdentExp, IdentExpEq, nativeToIdentExp } from '@/schemas/nodes/exps/ident' import { Effect, Match } from 'effect' import { KennethParseError } from 'src/errors/kenneth/parse' -import { BoolExpEq, type BoolExp } from 'src/schemas/nodes/exps/boolean' -import { IntExpEq, type IntExp } from 'src/schemas/nodes/exps/int' -import { StrExpEq, type StrExp } from 'src/schemas/nodes/exps/str' -import { nativeToExp } from 'src/schemas/nodes/exps/union' +import { BoolExpEq, nativeToBoolExp, type BoolExp } from 'src/schemas/nodes/exps/boolean' +import { IntExpEq, nativeToIntExp, type IntExp } from 'src/schemas/nodes/exps/int' +import { nativeToStrExp, StrExpEq, type StrExp } from 'src/schemas/nodes/exps/str' export const testLiteralExpression = ( - exp: IntExp | StrExp | BoolExp, + exp: IntExp | StrExp | BoolExp | IdentExp, value: string | number | boolean, -) => - Effect.gen(function* () { - return !Match.value(exp).pipe( - Match.tag('StrExp', (strExp) => StrExpEq(strExp, nativeToExp(value))), - Match.tag('IntExp', (intExp) => IntExpEq(intExp, nativeToExp(value))), - Match.tag('BoolExp', (boolExp) => BoolExpEq(boolExp, nativeToExp(value))), - ) - ? yield* new KennethParseError({ - message: `${exp._tag} not equal ${exp.value} !== ${value}`, - }) - : undefined - }) +) => Effect.fail( + new KennethParseError({ + message: `${exp._tag} not equal ${exp.value} !== ${value}`, + }) + ) + .pipe(Effect.when(() => !Match.value(exp).pipe( + Match.tag('StrExp', (strExp) => StrExpEq(strExp, nativeToStrExp(value as string))), // TODO: cheating + Match.tag('IntExp', (intExp) => IntExpEq(intExp, nativeToIntExp(value as number))), + Match.tag('BoolExp', (boolExp) => BoolExpEq(boolExp, nativeToBoolExp(value as boolean))), + Match.tag('IdentExp', (identExp) => IdentExpEq(identExp, nativeToIdentExp(value as string))) + ) )) + +// this function is kinda ass? diff --git a/src/vite/App.tsx b/src/vite/App.tsx index 4faf514..81b9785 100644 --- a/src/vite/App.tsx +++ b/src/vite/App.tsx @@ -3,12 +3,12 @@ import './App.css' import { Textarea } from '../components/ui/textarea' import { runAndInterpret } from '../programs/run-and-interpret' import { Match } from 'effect' +import { prettyObj } from '@/schemas/objs/union' const PROMPT = '>>' function App() { - const [count, setCount] = useState(0) const [text, setText] = useState(PROMPT) const [instructions, setInstructions] = useState('') const [evaluations, setEvaluations] = useState([]) @@ -36,7 +36,7 @@ function App() { console.log('instructions', instructions) const evaluated = Match.value(returnValue).pipe( Match.tag('ErrorObj', (errorObj) => {return errorObj.message}), - Match.orElse((r)=>{return r.evaluation.inspect()}) + Match.orElse((r)=>{return prettyObj(r.evaluation)}) ) setEvaluations(evals => [...evals, evaluated])