Skip to content

Commit

Permalink
Implement the CSET machine, along with macros (#1728)
Browse files Browse the repository at this point in the history
* add prelimiary framework for macro system

* Allow Scheme data types as part of control

* add functionality to scheme eval metaprocedure

* update macro skeleton

* add pattern matching system prototype

* complete first iteration of pattern matching functions

* finish first implementation of pattern matcher

* update tests for call/cc

* complete CSEP machine behaviour for standard syntax forms

* add tags to modified estree nodes, and deparser

* fix pattern matching logic

* allow let, cond, etc... to be defined in the prelude

* allow runtime checks for basic forms

* remove hack of storing scheme lists in estree nodes

* fix behaviour of ... in macros

* fix tests with scheme

* undo fix tests with scheme

* reduce strictness of scm-slang parser for FULL_SCHEME

* add more error-checking for CSEP machine

* add tests for CSEP machine and macros

* rename csep machine to cset machine

* implement the macro environment

* improve behaviour of CSET machine

* fix formatting issues

* Clean up helper functions

* create apply metaprocedure

* add tests for base cset machine and apply

* add named let to scheme prelude

---------

Co-authored-by: Kyriel Abad <[email protected]>
  • Loading branch information
s-kybound and Kyriel Abad authored Dec 4, 2024
1 parent 6652336 commit 61199f7
Show file tree
Hide file tree
Showing 28 changed files with 3,137 additions and 63 deletions.
9 changes: 8 additions & 1 deletion src/alt-langs/mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,11 @@ export function mapResult(context: Context): (x: Result) => Result {
// there is no need for a mapper in this case.
return x => x
}
}
}

export const isSchemeLanguage = (context: Context) =>
context.chapter === Chapter.SCHEME_1 ||
context.chapter === Chapter.SCHEME_2 ||
context.chapter === Chapter.SCHEME_3 ||
context.chapter === Chapter.SCHEME_4 ||
context.chapter === Chapter.FULL_SCHEME
26 changes: 14 additions & 12 deletions src/createContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import { makeWrapper } from './utils/makeWrapper'
import * as operators from './utils/operators'
import { stringify } from './utils/stringify'
import { schemeVisualise } from './alt-langs/scheme/scheme-mapper'
import { cset_apply, cset_eval } from './cse-machine/scheme-macros'
import { Transformers } from './cse-machine/interpreter'

export class LazyBuiltIn {
func: (...arg0: any) => any
Expand Down Expand Up @@ -117,6 +119,7 @@ const createEmptyRuntime = () => ({
nodes: [],
control: null,
stash: null,
transformers: new Transformers(),
objectCount: 0,
envSteps: -1,
envStepsTotal: 0,
Expand Down Expand Up @@ -450,16 +453,14 @@ export const importBuiltins = (context: Context, externalBuiltIns: CustomBuiltIn
if (context.chapter <= +Chapter.SCHEME_1 && context.chapter >= +Chapter.FULL_SCHEME) {
switch (context.chapter) {
case Chapter.FULL_SCHEME:
// Introduction to eval
// eval metaprocedure
defineBuiltin(context, '$scheme_ZXZhbA$61$$61$(xs)', cset_eval)

case Chapter.SCHEME_4:
// Introduction to call/cc
defineBuiltin(context, 'call$47$cc(f)', call_with_current_continuation)

// Introduction to eval

// Scheme apply
// ^ is needed in Schemes 2 and 3 to apply to call functions with rest parameters,
// so we move it there.

case Chapter.SCHEME_3:
// Introduction to mutable values, streams

Expand Down Expand Up @@ -497,10 +498,6 @@ export const importBuiltins = (context: Context, externalBuiltIns: CustomBuiltIn
defineBuiltin(context, 'list$45$$62$vector(xs)', scheme_libs.list$45$$62$vector)

case Chapter.SCHEME_2:
// Splicing builtin resolvers
// defineBuiltin(context, '$36$make$45$splice(expr)', scheme_libs.make_splice)
// defineBuiltin(context, '$36$resolve$45$splice(xs)', scheme_libs.resolve_splice)

// Scheme pairs
defineBuiltin(context, 'cons(left, right)', scheme_libs.cons)
defineBuiltin(context, 'xcons(right, left)', scheme_libs.xcons)
Expand Down Expand Up @@ -622,11 +619,16 @@ export const importBuiltins = (context: Context, externalBuiltIns: CustomBuiltIn
defineBuiltin(context, 'list$45$$62$string(xs)', scheme_libs.list$45$$62$string)

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

case Chapter.SCHEME_1:
// Display
defineBuiltin(context, 'display(val)', (val: any) => display(schemeVisualise(val)))
defineBuiltin(
context,
'display(val, prepend = undefined)',
(val: any, ...str: string[]) => display(schemeVisualise(val), ...str),
1
)
defineBuiltin(context, 'newline()', () => display(''))

// I/O
Expand Down
84 changes: 84 additions & 0 deletions src/cse-machine/__tests__/__snapshots__/cset-machine-apply.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`eval of strings: expectResult 1`] = `
Object {
"alertResult": Array [],
"code": "
(eval \\"hello\\")
",
"displayResult": Array [],
"numErrors": 0,
"parsedErrors": "",
"result": "hello",
"resultStatus": "finished",
"visualiseListResult": Array [],
}
`;

exports[`incorrect use of apply throws error (insufficient arguments): expectParsedError 1`] = `
Object {
"alertResult": Array [],
"code": "
(apply)
",
"displayResult": Array [],
"numErrors": 1,
"parsedErrors": "Expected 2 arguments, but got 0.",
"result": undefined,
"resultStatus": "error",
"visualiseListResult": Array [],
}
`;

exports[`incorrect use of apply throws error (last argument not a list): expectParsedError 1`] = `
Object {
"alertResult": Array [],
"code": "
(apply + 1 2 3)
",
"displayResult": Array [],
"numErrors": 1,
"parsedErrors": "Error: Last argument of apply must be a list",
"result": undefined,
"resultStatus": "error",
"visualiseListResult": Array [],
}
`;

exports[`multi-operand apply: expectResult 1`] = `
Object {
"alertResult": Array [],
"code": "
(define args '(1 2 3 4 5))
(apply + 6 7 8 9 10 args)
",
"displayResult": Array [],
"numErrors": 0,
"parsedErrors": "",
"result": SchemeInteger {
"numberType": 1,
"value": 55n,
},
"resultStatus": "finished",
"visualiseListResult": Array [],
}
`;

exports[`two-operand apply: expectResult 1`] = `
Object {
"alertResult": Array [],
"code": "
(define args '(1 2))
(apply + args)
",
"displayResult": Array [],
"numErrors": 0,
"parsedErrors": "",
"result": SchemeInteger {
"numberType": 1,
"value": 3n,
},
"resultStatus": "finished",
"visualiseListResult": Array [],
}
`;
224 changes: 224 additions & 0 deletions src/cse-machine/__tests__/__snapshots__/cset-machine-eval.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`eval of application: expectResult 1`] = `
Object {
"alertResult": Array [],
"code": "
(eval '(+ 1 2))
",
"displayResult": Array [],
"numErrors": 0,
"parsedErrors": "",
"result": SchemeInteger {
"numberType": 1,
"value": 3n,
},
"resultStatus": "finished",
"visualiseListResult": Array [],
}
`;

exports[`eval of begin: expectResult 1`] = `
Object {
"alertResult": Array [],
"code": "
(eval '(begin 1 2 3))
",
"displayResult": Array [],
"numErrors": 0,
"parsedErrors": "",
"result": SchemeInteger {
"numberType": 1,
"value": 3n,
},
"resultStatus": "finished",
"visualiseListResult": Array [],
}
`;

exports[`eval of booleans: expectResult 1`] = `
Object {
"alertResult": Array [],
"code": "
(eval #t)
",
"displayResult": Array [],
"numErrors": 0,
"parsedErrors": "",
"result": true,
"resultStatus": "finished",
"visualiseListResult": Array [],
}
`;

exports[`eval of define: expectResult 1`] = `
Object {
"alertResult": Array [],
"code": "
(eval '(define x 1))
x
",
"displayResult": Array [],
"numErrors": 0,
"parsedErrors": "",
"result": SchemeInteger {
"numberType": 1,
"value": 1n,
},
"resultStatus": "finished",
"visualiseListResult": Array [],
}
`;

exports[`eval of empty list: expectParsedError 1`] = `
Object {
"alertResult": Array [],
"code": "
(eval '())
",
"displayResult": Array [],
"numErrors": 1,
"parsedErrors": "Error: Cannot evaluate null",
"result": undefined,
"resultStatus": "error",
"visualiseListResult": Array [],
}
`;

exports[`eval of if: expectResult 1`] = `
Object {
"alertResult": Array [],
"code": "
(eval '(if #t 1 2))
",
"displayResult": Array [],
"numErrors": 0,
"parsedErrors": "",
"result": SchemeInteger {
"numberType": 1,
"value": 1n,
},
"resultStatus": "finished",
"visualiseListResult": Array [],
}
`;

exports[`eval of lambda: expectResult 1`] = `
Object {
"alertResult": Array [],
"code": "
(eval '(lambda (x) x))
",
"displayResult": Array [],
"numErrors": 0,
"parsedErrors": "",
"result": [Function],
"resultStatus": "finished",
"visualiseListResult": Array [],
}
`;

exports[`eval of numbers: expectResult 1`] = `
Object {
"alertResult": Array [],
"code": "
(eval 1)
",
"displayResult": Array [],
"numErrors": 0,
"parsedErrors": "",
"result": SchemeInteger {
"numberType": 1,
"value": 1n,
},
"resultStatus": "finished",
"visualiseListResult": Array [],
}
`;

exports[`eval of quote: expectResult 1`] = `
Object {
"alertResult": Array [],
"code": "
(eval '(quote (1 2 3)))
",
"displayResult": Array [],
"numErrors": 0,
"parsedErrors": "",
"result": Array [
SchemeInteger {
"numberType": 1,
"value": 1n,
},
Array [
SchemeInteger {
"numberType": 1,
"value": 2n,
},
Array [
SchemeInteger {
"numberType": 1,
"value": 3n,
},
null,
],
],
],
"resultStatus": "finished",
"visualiseListResult": Array [],
}
`;

exports[`eval of set!: expectResult 1`] = `
Object {
"alertResult": Array [],
"code": "
(define x 2)
(eval '(set! x 1))
x
",
"displayResult": Array [],
"numErrors": 0,
"parsedErrors": "",
"result": SchemeInteger {
"numberType": 1,
"value": 1n,
},
"resultStatus": "finished",
"visualiseListResult": Array [],
}
`;
exports[`eval of strings: expectResult 1`] = `
Object {
"alertResult": Array [],
"code": "
(eval \\"hello\\")
",
"displayResult": Array [],
"numErrors": 0,
"parsedErrors": "",
"result": "hello",
"resultStatus": "finished",
"visualiseListResult": Array [],
}
`;
exports[`eval of symbols: expectResult 1`] = `
Object {
"alertResult": Array [],
"code": "
(define hello 1)
(eval 'hello)
",
"displayResult": Array [],
"numErrors": 0,
"parsedErrors": "",
"result": SchemeInteger {
"numberType": 1,
"value": 1n,
},
"resultStatus": "finished",
"visualiseListResult": Array [],
}
`;
Loading

0 comments on commit 61199f7

Please sign in to comment.