Skip to content

Commit 2d3247c

Browse files
committed
create apply metaprocedure
1 parent a979a68 commit 2d3247c

File tree

4 files changed

+80
-38
lines changed

4 files changed

+80
-38
lines changed

src/createContext.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import { makeWrapper } from './utils/makeWrapper'
3838
import * as operators from './utils/operators'
3939
import { stringify } from './utils/stringify'
4040
import { schemeVisualise } from './alt-langs/scheme/scheme-mapper'
41-
import { cset_eval } from './cse-machine/scheme-macros'
41+
import { cset_apply, cset_eval } from './cse-machine/scheme-macros'
4242
import { Transformers } from './cse-machine/interpreter'
4343

4444
export class LazyBuiltIn {
@@ -619,11 +619,16 @@ export const importBuiltins = (context: Context, externalBuiltIns: CustomBuiltIn
619619
defineBuiltin(context, 'list$45$$62$string(xs)', scheme_libs.list$45$$62$string)
620620

621621
// Scheme apply is needed here to help in the definition of the Scheme Prelude.
622-
defineBuiltin(context, 'apply(f, ...args)', scheme_libs.apply, 2)
622+
defineBuiltin(context, 'apply(f, ...args)', cset_apply, 2)
623623

624624
case Chapter.SCHEME_1:
625625
// Display
626-
defineBuiltin(context, 'display(val)', (val: any) => display(schemeVisualise(val)))
626+
defineBuiltin(
627+
context,
628+
'display(val, prepend = undefined)',
629+
(val: any, ...str: string[]) => display(schemeVisualise(val), ...str),
630+
1
631+
)
627632
defineBuiltin(context, 'newline()', () => display(''))
628633

629634
// I/O

src/cse-machine/interpreter.ts

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,10 @@ import {
7979
setVariable,
8080
valueProducing
8181
} from './utils'
82-
import { isEval, schemeEval } from './scheme-macros'
82+
import { isApply, isEval, schemeEval } from './scheme-macros'
8383
import { Transformer } from './patterns'
8484
import { isSchemeLanguage } from '../alt-langs/mapper'
85+
import { flattenList, isList } from './macro-utils'
8586

8687
type CmdEvaluator = (
8788
command: ControlItem,
@@ -998,6 +999,37 @@ const cmdEvaluators: { [type: string]: CmdEvaluator } = {
998999
handleRuntimeError(context, new errors.CallingNonFunctionValue(func, command.srcNode))
9991000
}
10001001

1002+
if (isApply(func)) {
1003+
// Check for number of arguments mismatch error
1004+
checkNumberOfArguments(context, func, args, command.srcNode)
1005+
1006+
// get the procedure from the arguments
1007+
const proc = args[0]
1008+
// get the last list from the arguments
1009+
// (and it should be a list)
1010+
const last = args[args.length - 1]
1011+
if (!isList(last)) {
1012+
handleRuntimeError(
1013+
context,
1014+
new errors.ExceptionError(new Error('Last argument of apply must be a list'))
1015+
)
1016+
}
1017+
// get the rest of the arguments between the procedure and the last list
1018+
const rest = args.slice(1, args.length - 1)
1019+
// convert the last list to an array
1020+
const lastAsArray = flattenList(last)
1021+
// combine the rest and the last list
1022+
const combined = [...rest, ...lastAsArray]
1023+
1024+
// push the items back onto the stash
1025+
stash.push(proc)
1026+
stash.push(...combined)
1027+
1028+
// prepare a function call for the procedure
1029+
control.push(instr.appInstr(combined.length, command.srcNode))
1030+
return
1031+
}
1032+
10011033
if (isEval(func)) {
10021034
// Check for number of arguments mismatch error
10031035
checkNumberOfArguments(context, func, args, command.srcNode)
@@ -1057,17 +1089,6 @@ const cmdEvaluators: { [type: string]: CmdEvaluator } = {
10571089
// Check for number of arguments mismatch error
10581090
checkNumberOfArguments(context, func, args, command.srcNode)
10591091

1060-
// const dummyContCallExpression = makeDummyContCallExpression('f', 'cont')
1061-
1062-
// // Restore the state of the stash,
1063-
// // but replace the function application instruction with
1064-
// // a resume continuation instruction
1065-
// stash.push(func)
1066-
// // we need to push the arguments back onto the stash
1067-
// // as well
1068-
// stash.push(...args)
1069-
// control.push(instr.resumeContInstr(command.numOfArgs, dummyContCallExpression))
1070-
10711092
// get the C, S, E from the continuation
10721093
const contControl = func.getControl()
10731094
const contStash = func.getStash()

src/cse-machine/scheme-macros.ts

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,38 @@ import { flattenList, isList } from './macro-utils'
2121
// this needs to be better but for now it's fine
2222
export type SchemeControlItems = List | _Symbol | SchemeNumber | boolean | string
2323

24+
/**
25+
* A metaprocedure used to detect for the apply function object.
26+
* If the interpreter sees this specific function,
27+
* it will take all of the operands, and apply the second to second last operands as well as the last operand (must be a list)
28+
* to the first operand (which must be a function).
29+
*/
30+
export class Apply extends Function {
31+
private static instance: Apply = new Apply()
32+
33+
private constructor() {
34+
super()
35+
}
36+
37+
public static get(): Apply {
38+
return Apply.instance
39+
}
40+
41+
public toString(): string {
42+
return 'apply'
43+
}
44+
}
45+
46+
export const cset_apply = Apply.get()
47+
48+
export function isApply(value: any): boolean {
49+
return value === cset_apply
50+
}
51+
2452
/**
2553
* A metaprocedure used to detect for the eval function object.
2654
* If the interpreter sees this specific function,
55+
* it will transfer its operand to the control component.
2756
*/
2857
export class Eval extends Function {
2958
private static instance: Eval = new Eval()
@@ -508,28 +537,6 @@ export function makeDummyIdentifierNode(name: string): es.Identifier {
508537
}
509538
}
510539

511-
/**
512-
* Provides an adequate representation of what calling
513-
* eval looks like, to give to the
514-
* APPLICATION instruction.
515-
*/
516-
export function makeDummyEvalExpression(callee: string, argument: string): es.CallExpression {
517-
return {
518-
type: 'CallExpression',
519-
optional: false,
520-
callee: {
521-
type: 'Identifier',
522-
name: callee
523-
},
524-
arguments: [
525-
{
526-
type: 'Identifier',
527-
name: argument
528-
}
529-
]
530-
}
531-
}
532-
533540
/**
534541
* Convert a scheme expression (that is meant to be evaluated)
535542
* into an estree expression, using eval.

src/cse-machine/utils.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
} from './types'
2222
import Closure from './closure'
2323
import { Continuation, isCallWithCurrentContinuation } from './continuations'
24-
import { isEval } from './scheme-macros'
24+
import { isApply, isEval } from './scheme-macros'
2525
import { _Symbol } from '../alt-langs/scheme/scm-slang/src/stdlib/base'
2626
import { is_number } from '../alt-langs/scheme/scm-slang/src/stdlib/core-math'
2727

@@ -581,6 +581,15 @@ export const checkNumberOfArguments = (
581581
)
582582
}
583583
return undefined
584+
} else if (isApply(callee)) {
585+
// apply should have at least two arguments
586+
if (args.length < 2) {
587+
return handleRuntimeError(
588+
context,
589+
new errors.InvalidNumberOfArguments(exp, 2, args.length, false)
590+
)
591+
}
592+
return undefined
584593
} else if (callee instanceof Continuation) {
585594
// Continuations have variadic arguments,
586595
// and so we can let it pass

0 commit comments

Comments
 (0)