Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

refactor, add parsing of untyped lambdas #12

Merged
merged 1 commit into from
Jun 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions bin/ski.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@ import * as terminalKit from 'terminal-kit'
import { hrtime } from 'process'
import { create } from 'random-seed'
import { Terminal } from 'terminal-kit'
import { Expression, generate } from '../lib/expression'
import { TerminalSymbol } from '../lib/terminal'
import { stepOnce } from '../lib'
import { SKIExpression, generate } from '../lib/ski/expression'
import { SKITerminalSymbol } from '../lib/ski/terminal'
import { stepOnceSKI } from '../lib'

function colorizeSymbol (sym: TerminalSymbol): string {
function colorizeSymbol (sym: SKITerminalSymbol): string {
switch (sym) {
case TerminalSymbol.S:
case SKITerminalSymbol.S:
return ' ^[red]S^ '
case TerminalSymbol.K:
case SKITerminalSymbol.K:
return ' ^[green]K^ '
case TerminalSymbol.I:
case SKITerminalSymbol.I:
return ' ^[blue]I^ '
default:
return '?'
}
}

function colorizeExpression (expr: Expression): string {
function colorizeExpression (expr: SKIExpression): string {
switch (expr.kind) {
case 'terminal':
return colorizeSymbol(expr.sym)
Expand All @@ -36,7 +36,7 @@ function colorizeExpression (expr: Expression): string {
}
}

function formatted (expr: Expression): string {
function formatted (expr: SKIExpression): string {
return '> ' + colorizeExpression(expr) + '\n'
}

Expand All @@ -63,7 +63,7 @@ function runTUI (): number {
term.grabInput(false)
break
case 's': {
const stepResult = stepOnce(expression)
const stepResult = stepOnceSKI(expression)
expression = stepResult.expr
term(formatted(expression))
break
Expand All @@ -73,7 +73,7 @@ function runTUI (): number {
let iterations = 0

while (loop && iterations < MAX_ITER) {
const stepResult = stepOnce(expression)
const stepResult = stepOnceSKI(expression)
expression = stepResult.expr
if (stepResult.altered) {
term(formatted(expression))
Expand Down
21 changes: 15 additions & 6 deletions lib/combinators.ts → lib/consts/combinators.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { apply } from './expression'
import { parse } from './parser'
import { S, K, I } from './terminal'
import { convertLambda } from '../conversion/converter'
import { apply } from '../ski/expression'
import { predLambda } from './lambdas'
import { parseSKI } from '../parser/ski'

import { S, K, I } from '../ski/terminal'

/*
* Zero. apply a function to its arguments zero times.
Expand Down Expand Up @@ -60,7 +63,7 @@ export const One = I
*
* λnfx.n(fx) ≡ B
*/
export const B = parse('S(KS)K')
export const B = parseSKI('S(KS)K')

/*
* Successor function
Expand Down Expand Up @@ -166,7 +169,7 @@ export const V = apply(B, C, T)
*
* λa.aa ≡ M
*/
export const M = parse('SII')
export const M = parseSKI('SII')

/*
* Retrieve the first element in a Cons cell.
Expand Down Expand Up @@ -209,7 +212,7 @@ export const Cdr = apply(T, Snd)
*
* λxy.xyy ≡ W
*/
export const W = parse('SS(SK)')
export const W = parseSKI('SS(SK)')

// λabcd.a(bcd)
export const Blk = apply(B, B, B)
Expand All @@ -219,3 +222,9 @@ export const E = apply(B, apply(B, B, B))

// λabc.cba
export const F = apply(E, T, T, E, T)

// λf.(λx.f(x x))(λx.f(x x))
export const Y = parseSKI('S(K(SII))(S(S(KS)K)(K(SII)))')

// note: this is a crossover
export const pred = convertLambda(predLambda)
3 changes: 3 additions & 0 deletions lib/consts/lambdas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { parseLambda } from '../parser/untyped'

export const [, predLambda] = parseLambda('λn.λf.λx.n(λg.λh.h(gf))(λu.x)(λu.u)')
1 change: 1 addition & 0 deletions lib/conversion/conversionError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export class ConversionError extends Error { }
23 changes: 12 additions & 11 deletions lib/converter.ts → lib/conversion/converter.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { NonTerminal, nt } from './nonterminal'
import { S, K, I, Terminal } from './terminal'
import { Expression } from './expression'
import { B, C } from './combinators'
import { LambdaVar } from './lambda'
import { NonTerminal, nt } from '../nonterminal'
import { S, K, I, SKITerminal } from '../ski/terminal'
import { SKIExpression } from '../ski/expression'
import { B, C } from '../consts/combinators'
import { LambdaVar } from '../lambda/lambda'
import { ConversionError } from './conversionError'

type LambdaAbsMixed = {
kind: 'lambda-abs',
Expand All @@ -12,7 +13,7 @@ type LambdaAbsMixed = {
}

type LambdaMixed
= Terminal
= SKITerminal
| LambdaVar
| LambdaAbsMixed
| NonTerminal<LambdaMixed>
Expand All @@ -22,15 +23,13 @@ export type Lambda
| LambdaAbsMixed
| NonTerminal<Lambda>

export class ConversionError extends Error { }

const mkAbstractMixed = (name: string, body: LambdaMixed): LambdaMixed => ({
kind: 'lambda-abs',
name,
body
})

export const convertLambda = (lm: Lambda): Expression => {
export const convertLambda = (lm: Lambda): SKIExpression => {
const mixed = convert(lm)
return assertCombinator(mixed)
}
Expand Down Expand Up @@ -120,7 +119,7 @@ const convert = (lm: LambdaMixed): LambdaMixed => {
}
}

const assertCombinator = (lm: LambdaMixed): Expression => {
const assertCombinator = (lm: LambdaMixed): SKIExpression => {
switch (lm.kind) {
case 'terminal':
return lm
Expand All @@ -130,7 +129,9 @@ const assertCombinator = (lm: LambdaMixed): Expression => {
throw new ConversionError('lambda abstraction detected in nt')
}

return nt<Expression>(assertCombinator(lm.lft), assertCombinator(lm.rgt))
return nt<SKIExpression>(
assertCombinator(lm.lft), assertCombinator(lm.rgt)
)
default:
throw new ConversionError('lambda abstraction detected at top')
}
Expand Down
86 changes: 50 additions & 36 deletions lib/evaluator.ts → lib/evaluator/skiEvaluator.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { Expression, prettyPrint } from './expression'
import { nt } from './nonterminal'
import { TerminalSymbol } from './terminal'
import { SKIExpression, prettyPrint } from '../ski/expression'
import { nt } from '../nonterminal'
import { SKITerminalSymbol } from '../ski/terminal'

/**
* the shape of an evaluation result.
* altered is set if the evaluation step changed the input.
* expr is the evaluation output.
*/
export interface Result<E> {
export interface SKIResult<E> {
altered: boolean;
expr: E;
}

/**
* a computation step; takes an expression and returns a result.
*/
export type Step<E> = (expr: E) => Result<E>
export type SKIStep<E> = (expr: E) => SKIResult<E>

/**
* the SKI combinator reduction function.
Expand All @@ -24,9 +24,9 @@ export type Step<E> = (expr: E) => Result<E>
*
* NOTE: this function is not guaranteed to terminate
*/
export const stepMany: Step<Expression> =
(expr: Expression) => {
const result = stepOnce(expr)
export const stepMany: SKIStep<SKIExpression> =
(expr: SKIExpression) => {
const result = stepOnceSKI(expr)

if (result.altered) {
return stepMany(result.expr)
Expand All @@ -35,11 +35,11 @@ export const stepMany: Step<Expression> =
}
}

export const loggedStepMany: Step<Expression> =
(expr: Expression) => {
export const loggedStepMany: SKIStep<SKIExpression> =
(expr: SKIExpression) => {
console.log(prettyPrint(expr))
console.log('->')
const result = stepOnce(expr)
const result = stepOnceSKI(expr)

if (result.altered) {
return loggedStepMany(result.expr)
Expand All @@ -53,33 +53,38 @@ export const loggedStepMany: Step<Expression> =
* @param exp the input expression.
* @returns the evaluation result.
*/
export const reduce = (exp: Expression): Expression =>
export const reduceSKI = (
exp: SKIExpression
): SKIExpression =>
stepMany(exp).expr

/**
* Run β reduction on a SKI expression until it terminates.
* @param exp the input expression.
* @returns the evaluation result.
*/
export const loggedReduce = (exp: Expression): Expression =>
export const loggedReduceSKI = (
exp: SKIExpression
): SKIExpression =>
loggedStepMany(exp).expr

/**
* the SKI combinator single step reduction function.
* @param expr the input expression.
* @returns the evaluation result after one step.
*/
export const stepOnce: Step<Expression> =
(expr: Expression) => scanStep(expr, [stepOnceI, stepOnceK, stepOnceS])
export const stepOnceSKI: SKIStep<SKIExpression> =
(expr: SKIExpression) =>
scanStep(expr, [stepOnceI, stepOnceK, stepOnceS])

const stepOnceI: Step<Expression> =
(expr: Expression) => treeStep(expr, stepI)
const stepOnceI: SKIStep<SKIExpression> =
(expr: SKIExpression) => treeStep(expr, stepI)

const stepOnceK: Step<Expression> =
(expr: Expression) => treeStep(expr, stepK)
const stepOnceK: SKIStep<SKIExpression> =
(expr: SKIExpression) => treeStep(expr, stepK)

const stepOnceS: Step<Expression> =
(expr: Expression) => treeStep(expr, stepS)
const stepOnceS: SKIStep<SKIExpression> =
(expr: SKIExpression) => treeStep(expr, stepS)

/**
* @param expr the expression to scan with steppers.
Expand All @@ -88,8 +93,11 @@ const stepOnceS: Step<Expression> =
*
* NOTE: this is an eagerly returning fold
*/
const scanStep = (expr: Expression, steppers: Array<Step<Expression>>):
Result<Expression> => {
const scanStep = (
expr: SKIExpression,
steppers: Array<SKIStep<SKIExpression>>
):
SKIResult<SKIExpression> => {
for (const step of steppers) {
const result = step(expr)

Expand All @@ -114,8 +122,11 @@ const scanStep = (expr: Expression, steppers: Array<Step<Expression>>):
* is the input and a singular function that processes an expression
* and returns either nothing or some result, returning eagerly.
*/
function treeStep (expr: Expression, step: Step<Expression>):
Result<Expression> {
function treeStep (
expr: SKIExpression,
step: SKIStep<SKIExpression>
):
SKIResult<SKIExpression> {
switch (expr.kind) {
case 'terminal':
return ({
Expand Down Expand Up @@ -151,8 +162,11 @@ function treeStep (expr: Expression, step: Step<Expression>):

type ExtractStep<E> = (expr: E) => E | false

function extractStep (expr: Expression, extractStep: ExtractStep<Expression>):
Result<Expression> {
function extractStep (
expr: SKIExpression,
extractStep: ExtractStep<SKIExpression>
):
SKIResult<SKIExpression> {
const extractionResult = extractStep(expr)

if (extractionResult) {
Expand All @@ -166,42 +180,42 @@ function extractStep (expr: Expression, extractStep: ExtractStep<Expression>):
* identity
* Ix = x
*/
const stepI: Step<Expression> = (expr: Expression) =>
const stepI: SKIStep<SKIExpression> = (expr: SKIExpression) =>
extractStep(
expr, (expr: Expression) =>
expr, (expr: SKIExpression) =>
expr.kind === 'non-terminal' &&
expr.lft.kind === 'terminal' &&
expr.lft.sym === TerminalSymbol.I &&
expr.lft.sym === SKITerminalSymbol.I &&
expr.rgt
)

/*
* constant
* Kxy = x
*/
const stepK: Step<Expression> = (expr: Expression) =>
const stepK: SKIStep<SKIExpression> = (expr: SKIExpression) =>
extractStep(
expr, (expr: Expression) =>
expr, (expr: SKIExpression) =>
expr.kind === 'non-terminal' &&
expr.lft.kind === 'non-terminal' &&
expr.lft.lft.kind === 'terminal' &&
expr.lft.lft.sym === TerminalSymbol.K &&
expr.lft.lft.sym === SKITerminalSymbol.K &&
expr.lft.rgt
)

/*
* fusion
* Sxyz = xz(yz)
*/
const stepS: Step<Expression> = (expr: Expression) =>
const stepS: SKIStep<SKIExpression> = (expr: SKIExpression) =>
extractStep(
expr, (expr: Expression) => {
expr, (expr: SKIExpression) => {
if (
expr.kind === 'non-terminal' &&
expr.lft.kind === 'non-terminal' &&
expr.lft.lft.kind === 'non-terminal' &&
expr.lft.lft.lft.kind === 'terminal' &&
expr.lft.lft.lft.sym === TerminalSymbol.S
expr.lft.lft.lft.sym === SKITerminalSymbol.S
) {
const x = expr.lft.lft.rgt
const y = expr.lft.rgt
Expand Down
20 changes: 11 additions & 9 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
export * from './church'
export * from './combinators'
export * from './evaluator'
export * from './expression'
export * from './lambda'
export * from './packer'
export * from './parser'
export * from './typedLambda'
export * from './types'
export * from './ski/church'
export * from './consts/combinators'
export * from './consts/lambdas'
export * from './evaluator/skiEvaluator'
export * from './ski/expression'
export * from './lambda/lambda'
export * from './ski/packer'
export * from './parser/ski'
export * from './parser/typed'
export * from './typed/typedLambda'
export * from './typed/types'
Loading
Loading