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/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/diff.ts b/src/schemas/nodes/exps/diff.ts index 146c7a2..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', { @@ -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/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/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/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/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/null.ts b/src/schemas/objs/null.ts index 7d31bd1..748a09a 100644 --- a/src/schemas/objs/null.ts +++ b/src/schemas/objs/null.ts @@ -1,17 +1,14 @@ 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, }) + +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 a009262..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, @@ -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,34 @@ export const objSchema = Schema.suspend( nullObjSchema, returnObjSchema, stringObjSchema, + identObjSchema, + 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/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/cli.ts b/src/services/cli.ts index 42f4331..85bb357 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', {}, () => @@ -35,8 +36,8 @@ const replCommand = Command.make( const parser = yield* Parser yield* parser.init(input) const program = yield* parser.parseProgram - const evaluation = yield* Eval(program)(env) - yield* Console.log(evaluation.inspect()) + const evaluation = yield* Eval(program)(env, undefined) + yield* Console.log(Pretty.make(objSchema)(evaluation)) } }), // Console.log(input), 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/diff/obj.ts b/src/services/diff/obj.ts index 987d592..6e7b07e 100644 --- a/src/services/diff/obj.ts +++ b/src/services/diff/obj.ts @@ -1,46 +1,38 @@ 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 { IdentObj, identObjSchema } from '@/schemas/objs/ident' +import { IntObj, intObjSchema } from '@/schemas/objs/int' +import { 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( 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,16 +51,12 @@ 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), + identObjSchema.make({identExp: x}), power.value - 1, ) }), @@ -79,16 +67,16 @@ 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), + newTerm(value, identObjSchema.make({identExp: x}), value - 1), ) }), ), @@ -108,36 +96,34 @@ 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), - '*', - right, - ), - '+', - createInfixObj( - left, - '*', - 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 }), @@ -145,31 +131,35 @@ 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), - '*', - right, - ), - '-', - createInfixObj( - left, - '*', - yield* diffPolynomial(right, x), - ), - ), - '/', - createInfixObj(right, '**', 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 }), @@ -177,91 +167,32 @@ 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( + 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), - '+', - yield* diffPolynomial(right, x), - ), + infixObjSchema.make({ + left: yield* diffPolynomial(left, x), + operator: TokenType.PLUS, + 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, - ) - }) + ) \ No newline at end of file diff --git a/src/services/evaluator/index.ts b/src/services/evaluator/index.ts index f5f3b9e..6a7415b 100644 --- a/src/services/evaluator/index.ts +++ b/src/services/evaluator/index.ts @@ -1,30 +1,28 @@ -import { - createIntegerObj, - FALSE, - type IntegerObj, - isIntegerObj, - nativeBoolToObjectBool, - NULL, - TRUE, - type Obj, - 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, 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' @@ -37,17 +35,27 @@ 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, 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 { 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! @@ -57,18 +65,18 @@ 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* () { const val = yield* Eval(value)(env, identExp) - env.set(name.value, val) + set(env)(name.value, val) 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) @@ -86,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) @@ -96,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* () { @@ -160,30 +168,34 @@ 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) => fn.fn(...args)), - 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* () { 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')) 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[], @@ -195,7 +207,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, @@ -259,7 +271,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) @@ -287,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 = ( @@ -327,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( @@ -389,4 +401,4 @@ export class Evaluator extends Effect.Service()('Evaluator', { } }), dependencies: [Parser.Default], -}) {} +}) { } 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/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/services/object/builtins.ts b/src/services/object/builtins.ts index 1e18207..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,17 +48,20 @@ const diff2 = (...args: Obj[]) => ], }) - return createFunctionObj(params, newBody, env as Environment) + return functionObjSchema.make({params, body: newBody, env}) }) export const builtins = { - len: createBuiltInObj((...args: Obj[]) => + len: builtInObjSchema.make({fn: 'len'}), + diff: builtInObjSchema.make({fn: 'diff'}), +} as const + +export const builtInFnMap = { + len: (...args: Obj[]) => 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`}) ) } @@ -68,19 +69,16 @@ export const builtins = { 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: createBuiltInObj(diff2), + diff: diff2 } as const const builtinKeys = Object.keys(builtins) as (keyof typeof builtins)[] // hack diff --git a/src/services/object/environment.ts b/src/services/object/environment.ts index 0c0a56f..0278039 100644 --- a/src/services/object/environment.ts +++ b/src/services/object/environment.ts @@ -1,44 +1,58 @@ 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 type Environment = { - store: Map - outer: Environment | undefined - get: (key: string) => Effect.Effect - set: (key: string, value: Obj) => Obj +export const get = (env: Environment) => (key: string): Effect.Effect => Effect.gen(function* () { + return ( + env.store.get(key) ?? + (env.outer + ? yield* get(env.outer)(key) + : builtins[yield* Schema.decodeUnknown(builtinsKeySchema)(key)]) + ) + }) + +export const set = (env: Environment) => (key: string, value: Obj) => { + env.store.set(key, value) + return value +} + + +export interface Environment { + readonly outer: Environment | undefined + readonly store: Map +} + +export interface EnvironmentEncoded { + 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.Union(Schema.suspend((): Schema.Schema => environmentSchema), Schema.Undefined) +}) + +// 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 => { let output = 'Environment Store:\n' env.store.forEach((value, key) => { - output += `${key}: ${value.inspect()}\n` + output += `${key}: ${value}\n` }) if (env.outer) { diff --git a/src/services/object/index.ts b/src/services/object/index.ts deleted file mode 100644 index 4a86f07..0000000 --- a/src/services/object/index.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { Data, type Effect } 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 & { - readonly value: number - } - BooleanObj: InternalObj & { readonly value: boolean } - NullObj: InternalObj - ReturnObj: InternalObj & { readonly value: Obj } - ErrorObj: InternalObj & { readonly message: string } - FunctionObj: InternalObj & { - readonly params: readonly IdentExp[] - readonly body: BlockStmt - readonly env: Environment - } - StringObj: InternalObj & { - readonly value: string - } - BuiltInObj: InternalObj & { - readonly fn: ( - ...args: Obj[] - ) => Effect.Effect - } - IdentObj: InternalObj & { - readonly identExp: IdentExp - } - InfixObj: InternalObj & { - 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, - inspect: () => String(value), - }) - -const createBooleanObj = (value: boolean) => - BooleanObj({ - value, - inspect: () => String(value), - }) - -export const FALSE = createBooleanObj(false) -export const TRUE = createBooleanObj(true) - -export const nativeBoolToObjectBool = (input: boolean) => (input ? TRUE : FALSE) - -const createNullObj = () => - NullObj({ - inspect: () => 'null', - }) - -export const NULL = createNullObj() - -export const createReturnObj = (value: Obj) => - ReturnObj({ value, inspect: value.inspect }) - -export const createErrorObj = (message: string) => - ErrorObj({ - message, - inspect: () => `ERROR: ${message}`, - }) - -export const createFunctionObj = ( - params: readonly IdentExp[], - body: BlockStmt, - env: Environment, -) => - FunctionObj({ - 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, -) => - BuiltInObj({ - fn, - inspect: () => 'builtin function', - }) - -export const createIdentObj = (identExp: IdentExp) => - IdentObj({ - identExp, - inspect: () => identExp.value, - }) - -export const createInfixObj = ( - left: Obj, - operator: InfixOperator, - right: Obj, -) => - InfixObj({ - left, - operator, - right, - inspect: () => 'infix obj', - }) 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( diff --git a/src/tests/evaluator/evaluator.test.ts b/src/tests/evaluator/evaluator.test.ts index 71f6a2b..1cd9b3f 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,18 +37,20 @@ 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' +import { KennethParseError } from '@/errors/kenneth/parse' +import { errorObjSchema } from '@/schemas/objs/error' 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 +125,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) }), }, { @@ -607,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) @@ -686,7 +688,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 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])