Skip to content

Commit

Permalink
Lazy lists support for lazy source 2 (source-academy#564)
Browse files Browse the repository at this point in the history
* added option for lazy evaluation to interpreter

* tests for lazy evaluation

* finished transpiler

* additional test case

* updated REPL to have nice command line args

* updated REPL to have nice command line args

* added html test reporter

* fixed function printing bug in REPL

* formatting

* fixed REPL printing bug for null and undefined

* changed REPL bug fix

* changed REPL bug fix

* implement lazy pairs

* add lazy pairs

* add tests for lists, fix bugs for lazy lists

* previous tests fixes

* remove console.log calls

* minor style fix for lazy context creation

* untracked coverage

* formatting

* Updated last references to evaluationMethod

Co-authored-by: Ian Duncan <[email protected]>
  • Loading branch information
Ahmedjjj and Ian Duncan authored Apr 27, 2020
1 parent bffc40d commit eb5fd65
Show file tree
Hide file tree
Showing 11 changed files with 2,916 additions and 27 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ node_modules
*.map
dist/
.idea/
coverage/

# emacs backup files
*~
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
"/src/typings/",
"/src/utils/testing.ts"
],
"reporters": [
"reporters": [
"default",
[
"./node_modules/jest-html-reporter",
Expand Down
43 changes: 34 additions & 9 deletions src/createContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,15 @@ import { streamPrelude } from './stdlib/stream.prelude'
import { Context, CustomBuiltIns, Value, Variant } from './types'
import * as operators from './utils/operators'
import { stringify } from './utils/stringify'

import { lazyListPrelude } from './stdlib/lazyList.prelude'
export class LazyBuiltIn {
func: (...arg0: any) => any
evaluateArgs: boolean
constructor(func: (...arg0: any) => any, evaluateArgs: boolean) {
this.func = func
this.evaluateArgs = evaluateArgs
}
}
const createEmptyRuntime = () => ({
break: false,
debuggerOn: true,
Expand Down Expand Up @@ -103,6 +111,12 @@ export const defineBuiltin = (context: Context, name: string, value: Value) => {
wrapped.toString = () => repr

defineSymbol(context, funName, wrapped)
} else if (value instanceof LazyBuiltIn) {
const wrapped = (...args: any) => value.func(...args)
const funName = name.split('(')[0].trim()
const repr = `function ${name} {\n\t[implementation hidden]\n}`
wrapped.toString = () => repr
defineSymbol(context, funName, new LazyBuiltIn(wrapped, value.evaluateArgs))
} else {
defineSymbol(context, name, value)
}
Expand Down Expand Up @@ -154,13 +168,24 @@ export const importBuiltins = (context: Context, externalBuiltIns: CustomBuiltIn

if (context.chapter >= 2) {
// List library
defineBuiltin(context, 'pair(left, right)', list.pair)
defineBuiltin(context, 'is_pair(val)', list.is_pair)
defineBuiltin(context, 'head(xs)', list.head)
defineBuiltin(context, 'tail(xs)', list.tail)
defineBuiltin(context, 'is_null(val)', list.is_null)
defineBuiltin(context, 'list(...values)', list.list)
defineBuiltin(context, 'draw_data(xs)', visualiseList)

if (context.variant === 'lazy') {
defineBuiltin(context, 'pair(left, right)', new LazyBuiltIn(list.pair, false))
defineBuiltin(context, 'list(...values)', new LazyBuiltIn(list.list, false))
defineBuiltin(context, 'is_pair(val)', new LazyBuiltIn(list.is_pair, true))
defineBuiltin(context, 'head(xs)', new LazyBuiltIn(list.head, true))
defineBuiltin(context, 'tail(xs)', new LazyBuiltIn(list.tail, true))
defineBuiltin(context, 'is_null(val)', new LazyBuiltIn(list.is_null, true))
defineBuiltin(context, 'draw_data(xs)', new LazyBuiltIn(visualiseList, true))
} else {
defineBuiltin(context, 'pair(left, right)', list.pair)
defineBuiltin(context, 'is_pair(val)', list.is_pair)
defineBuiltin(context, 'head(xs)', list.head)
defineBuiltin(context, 'tail(xs)', list.tail)
defineBuiltin(context, 'is_null(val)', list.is_null)
defineBuiltin(context, 'list(...values)', list.list)
defineBuiltin(context, 'draw_data(xs)', visualiseList)
}
}

if (context.chapter >= 3) {
Expand Down Expand Up @@ -202,7 +227,7 @@ export const importBuiltins = (context: Context, externalBuiltIns: CustomBuiltIn
function importPrelude(context: Context) {
let prelude = ''
if (context.chapter >= 2) {
prelude += listPrelude
prelude += context.variant === 'lazy' ? lazyListPrelude : listPrelude
}
if (context.chapter >= 3) {
prelude += streamPrelude
Expand Down
28 changes: 28 additions & 0 deletions src/interpreter/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { conditionalExpression, literal, primitive } from '../utils/astCreator'
import { evaluateBinaryExpression, evaluateUnaryExpression } from '../utils/operators'
import * as rttc from '../utils/rttc'
import Closure from './closure'
import { LazyBuiltIn } from '../createContext'
import { loadIIFEModule } from '../modules/moduleLoader'

class BreakValue {}
Expand Down Expand Up @@ -662,6 +663,33 @@ export function* apply(
// No Return Value, set it as undefined
result = new ReturnValue(undefined)
}
} else if (fun instanceof LazyBuiltIn) {
try {
let finalArgs = args
if (fun.evaluateArgs) {
finalArgs = []
for (const arg of args) {
finalArgs.push(yield* forceIt(arg, context))
}
}
result = fun.func.apply(thisContext, finalArgs)
break
} catch (e) {
// Recover from exception
context.runtime.environments = context.runtime.environments.slice(
-context.numberOfOuterEnvironments
)

const loc = node ? node.loc! : constants.UNKNOWN_LOCATION
if (!(e instanceof RuntimeSourceError || e instanceof errors.ExceptionError)) {
// The error could've arisen when the builtin called a source function which errored.
// If the cause was a source error, we don't want to include the error.
// However if the error came from the builtin itself, we need to handle it.
return handleRuntimeError(context, new errors.ExceptionError(e, loc))
}
result = undefined
throw e
}
} else if (typeof fun === 'function') {
try {
const forcedArgs = []
Expand Down
2 changes: 1 addition & 1 deletion src/repl/repl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function startRepl(
prelude = ''
) {
// use defaults for everything
const context = createContext(chapter)
const context = createContext(chapter, undefined, undefined, undefined)
const options: Partial<IOptions> = {
scheduler: 'preemptive',
executionMethod,
Expand Down
Loading

0 comments on commit eb5fd65

Please sign in to comment.