Comparing version 0.0.11 to 0.0.12
@@ -1,2 +0,2 @@ | ||
import { SxParserState, SxSymbol, SxToken, SxScope } from './types'; | ||
import { SxParserState, SxSymbol, SxToken, SxScope, CapturedScopes } from './types'; | ||
export declare function toNumber(x: any): number; | ||
@@ -7,3 +7,5 @@ export declare function resolveMacro(state: SxParserState, x: SxSymbol): ((list: SxToken[]) => SxToken) | false; | ||
export declare function resolveValueSymbol(state: SxParserState, x: SxSymbol): any; | ||
export declare function installScope(state: SxParserState, scope: any, isBlockLocal: boolean): any; | ||
export declare function collectCapturedVariables(state: SxParserState, names: SxSymbol[]): CapturedScopes; | ||
export declare function getCapturedScopes(state: SxParserState): CapturedScopes | undefined; | ||
export declare function installScope(state: SxParserState, scope: any, isBlockLocal: boolean, capturedScopes?: CapturedScopes): any; | ||
export declare function uninstallScope(state: SxParserState): any; | ||
@@ -10,0 +12,0 @@ export declare function getScope(state: SxParserState): SxScope; |
@@ -58,2 +58,6 @@ "use strict"; | ||
} | ||
if (localScope.capturedScopes && | ||
Object.prototype.hasOwnProperty.call(localScope.capturedScopes, x.symbol)) { | ||
return localScope.capturedScopes[x.symbol]; | ||
} | ||
if (!localScope.isBlockLocal) { | ||
@@ -90,5 +94,31 @@ break; | ||
exports.resolveValueSymbol = resolveValueSymbol; | ||
function installScope(state, scope, isBlockLocal) { | ||
state.scopes.push({ isBlockLocal, scope }); | ||
function collectCapturedVariables(state, names) { | ||
const capturedScopes = {}; | ||
for (const n of names) { | ||
const scope = resolveValueSymbolScope(state, n, true); | ||
if (scope === null) { | ||
throw new Error(`[SX] collectCapturedVariables: Unresolved symbols ${n}`); | ||
} | ||
capturedScopes[n.symbol] = scope; | ||
} | ||
return capturedScopes; | ||
} | ||
exports.collectCapturedVariables = collectCapturedVariables; | ||
function getCapturedScopes(state) { | ||
const a = []; | ||
for (let i = state.scopes.length - 1; i > 0; i--) { | ||
const localScope = state.scopes[i]; | ||
if (localScope.capturedScopes) { | ||
a.unshift(localScope.capturedScopes); | ||
} | ||
if (!localScope.isBlockLocal) { | ||
break; | ||
} | ||
} | ||
return a.length > 0 ? Object.assign({}, ...a) : void 0; | ||
} | ||
exports.getCapturedScopes = getCapturedScopes; | ||
function installScope(state, scope, isBlockLocal, capturedScopes) { | ||
state.scopes.push({ isBlockLocal, scope, capturedScopes }); | ||
} | ||
exports.installScope = installScope; | ||
@@ -95,0 +125,0 @@ function uninstallScope(state) { |
@@ -1,2 +0,2 @@ | ||
import { SxParserState, SxToken } from '../../types'; | ||
import { SxParserState, SxToken, CapturedScopes } from '../../types'; | ||
export declare const $car: (state: SxParserState, name: string) => (...args: any[]) => any; | ||
@@ -38,6 +38,8 @@ export declare const $$car: (...args: any[]) => any; | ||
export declare const $$list: (...args: any[]) => any[]; | ||
export declare const $__scope: (state: SxParserState, name: string) => (...args: any[]) => SxToken; | ||
export declare const $__scope: (state: SxParserState, name: string, capturedScopes?: CapturedScopes | undefined) => (...args: any[]) => SxToken; | ||
export declare const $__globalScope: (state: SxParserState, name: string) => (...args: any[]) => SxToken; | ||
export declare const $__capture: (state: SxParserState, name: string) => (...args: any[]) => SxToken; | ||
export declare const $__lambda: (state: SxParserState, name: string) => (...args: any[]) => (...actualArgs: any[]) => SxToken; | ||
export declare const $__defun: (state: SxParserState, name: string) => (...args: any[]) => (...actualArgs: any[]) => SxToken; | ||
export declare const $__refun: (state: SxParserState, name: string) => (...args: any[]) => (...args: any[]) => any; | ||
export declare const $apply: (state: SxParserState, name: string) => (...args: any[]) => (...p: any[]) => any; | ||
@@ -44,0 +46,0 @@ export declare const $$apply: (...args: any[]) => (...p: any[]) => any; |
@@ -158,3 +158,3 @@ "use strict"; | ||
// tslint:disable-next-line:variable-name | ||
exports.$__scope = (state, name) => (...args) => { | ||
exports.$__scope = (state, name, capturedScopes) => (...args) => { | ||
// S expression: ($__scope isBlockLocal returnMultiple '((name value) | name ...) 'expr1 ... 'exprN) | ||
@@ -168,20 +168,18 @@ // -> (if returnMultiple) S expr : [expr1 ... exprN] | ||
let r = null; | ||
let scopeInstalled = false; | ||
try { | ||
const scope = {}; | ||
if (Array.isArray(car)) { | ||
for (const x of car) { | ||
if (Array.isArray(x)) { | ||
const kv = exports.$$firstAndSecond(...x); | ||
const kvSym = types_1.isSymbol(kv.car); | ||
scope[kvSym ? kvSym.symbol : String(kv.car)] = evaluate_1.evaluate(state, kv.cdr); | ||
} | ||
else { | ||
const xSym = types_1.isSymbol(x); | ||
scope[xSym ? xSym.symbol : String(x)] = null; | ||
} | ||
const scope = {}; | ||
if (Array.isArray(car)) { | ||
for (const x of car) { | ||
if (Array.isArray(x)) { | ||
const kv = exports.$$firstAndSecond(...x); | ||
const kvSym = types_1.isSymbol(kv.car); | ||
scope[kvSym ? kvSym.symbol : String(kv.car)] = evaluate_1.evaluate(state, kv.cdr); | ||
} | ||
else { | ||
const xSym = types_1.isSymbol(x); | ||
scope[xSym ? xSym.symbol : String(x)] = null; | ||
} | ||
} | ||
evaluate_1.installScope(state, scope, isBlockLocal); | ||
scopeInstalled = true; | ||
} | ||
evaluate_1.installScope(state, scope, isBlockLocal, capturedScopes); | ||
try { | ||
if (4 < args.length) { | ||
@@ -205,5 +203,3 @@ if (returnMultiple) { | ||
finally { | ||
if (scopeInstalled) { | ||
evaluate_1.uninstallScope(state); | ||
} | ||
evaluate_1.uninstallScope(state); | ||
} | ||
@@ -221,4 +217,4 @@ return r; | ||
let r = null; | ||
evaluate_1.installScope(state, evaluate_1.getGlobalScope(state).scope, true); | ||
try { | ||
evaluate_1.installScope(state, evaluate_1.getGlobalScope(state).scope, true); | ||
if (2 < args.length) { | ||
@@ -247,2 +243,24 @@ if (returnMultiple) { | ||
// tslint:disable-next-line:variable-name | ||
exports.$__capture = (state, name) => (...args) => { | ||
// S expression: ($__capture '(sym1 ... symN) 'expr1 ... 'exprN) | ||
// -> S expr : exprN | ||
errors_1.checkParamsLength('$__capture', args, 1); | ||
const formalArgs = args[0]; | ||
if (!Array.isArray(formalArgs)) { | ||
throw new Error(`[SX] $__lambda: Invalid argument(s): args[0] is not array.`); | ||
} | ||
let r = null; | ||
const capturedScopes = evaluate_1.collectCapturedVariables(state, formalArgs); | ||
evaluate_1.installScope(state, {}, true, capturedScopes); | ||
try { | ||
for (const x of args.slice(1)) { | ||
r = evaluate_1.evaluate(state, x); | ||
} | ||
} | ||
finally { | ||
evaluate_1.uninstallScope(state); | ||
} | ||
return r; | ||
}; | ||
// tslint:disable-next-line:variable-name | ||
exports.$__lambda = (state, name) => (...args) => { | ||
@@ -275,2 +293,3 @@ // S expression: ($__lambda '(sym1 ... symN) 'expr1 ... 'exprN) | ||
} | ||
const capturedScopes = evaluate_1.getCapturedScopes(state); | ||
const fn = (...actualArgs) => { | ||
@@ -280,3 +299,3 @@ if ((actualArgs.length + (lastIsSpread ? 1 : 0)) < formalArgs.length) { | ||
} | ||
return exports.$__scope(state, name)(false, false, [ | ||
return exports.$__scope(state, name, capturedScopes)(false, false, [ | ||
[state.config.reservedNames.self, fn], | ||
@@ -305,2 +324,14 @@ ...(formalArgs.map((x, index) => [ | ||
}; | ||
// tslint:disable-next-line:variable-name | ||
exports.$__refun = (state, name) => (...args) => { | ||
// S expression: ($refun 'name) | ||
// -> S expr : fn | ||
errors_1.checkParamsLength('$__refun', args, 1, 1); | ||
const car = exports.$$first(...args); | ||
const info = state.funcMap.get(car.symbol); | ||
if (!info) { | ||
throw new Error(`[SX] $__refun: function ${car.symbol} is not defined.`); | ||
} | ||
return info.fn(state, car.symbol); | ||
}; | ||
exports.$apply = (state, name) => (...args) => { | ||
@@ -307,0 +338,0 @@ // S expression: ($apply fn arg1 ... argN) |
@@ -52,2 +52,34 @@ "use strict"; | ||
}, { | ||
name: '$capture', | ||
fn: (state, name) => (list) => { | ||
// S expression: ($capture (sym1 ... symN) expr ... expr) | ||
// -> S expr : ($__capture '(sym1 ... symN) 'expr ... 'expr) | ||
return [{ symbol: '$__capture' }, | ||
...(list.slice(1).map(x => types_1.quote(state, x))), | ||
]; | ||
}, | ||
}, { | ||
name: '$closure', | ||
fn: (state, name) => (list) => { | ||
// S expression: ($closure (sym1 ... symN) use (u-sym1 ... u-symM) expr ... expr) | ||
// -> S expr : ($__capture '(u-sym1 ... u-symM) ($__lambda '(sym1 ... symN) 'expr ... 'expr) ) | ||
const symUse = types_1.isSymbol(list[2], 'use'); | ||
if (!symUse) { | ||
throw new Error(`[SX] $closure: Invalid syntax: missing 'use' keyword.`); | ||
} | ||
return [{ symbol: '$__capture' }, types_1.quote(state, list[3]), types_1.quote(state, [{ symbol: '$__lambda' }, | ||
types_1.quote(state, list[1]), | ||
...(list.slice(4).map(x => types_1.quote(state, x))), | ||
])]; | ||
}, | ||
}, { | ||
name: '|->', | ||
fn: (state, name) => (list) => { | ||
// S expression: (|-> (sym1 ... symN) use (u-sym1 ... u-symM) expr ... expr) | ||
// -> S expr : ($closure (sym1 ... symN) use (u-sym1 ... u-symM) expr ... expr) | ||
return [{ symbol: '$closure' }, | ||
...list.slice(1), | ||
]; | ||
}, | ||
}, { | ||
name: '$lambda', | ||
@@ -80,2 +112,20 @@ fn: (state, name) => (list) => { | ||
}, { | ||
name: '$refun', | ||
fn: (state, name) => (list) => { | ||
// S expression: ($refun name) | ||
// -> S expr : ($__refun 'name) | ||
return [{ symbol: '$__refun' }, | ||
...(list.slice(1).map(x => types_1.quote(state, x))), | ||
]; | ||
}, | ||
}, { | ||
name: '<-', | ||
fn: (state, name) => (list) => { | ||
// S expression: (<- name) | ||
// -> S expr : ($__refun 'name) | ||
return [{ symbol: '$__refun' }, | ||
...(list.slice(1).map(x => types_1.quote(state, x))), | ||
]; | ||
}, | ||
}, { | ||
name: '$call', | ||
@@ -82,0 +132,0 @@ fn: (state, name) => (list) => { |
@@ -59,2 +59,5 @@ "use strict"; | ||
}, { | ||
name: '$__capture', | ||
fn: ops.$__capture, | ||
}, { | ||
name: '$__lambda', | ||
@@ -66,2 +69,5 @@ fn: ops.$__lambda, | ||
}, { | ||
name: '$__refun', | ||
fn: ops.$__refun, | ||
}, { | ||
name: '$apply', | ||
@@ -68,0 +74,0 @@ fn: ops.$apply, |
@@ -16,5 +16,11 @@ export declare type SxMacro = (state: SxParserState, name: string) => (list: SxToken[]) => SxToken; | ||
} | ||
export interface CapturedScopes { | ||
[s: string]: { | ||
[s: string]: any; | ||
}; | ||
} | ||
export interface SxScope { | ||
isBlockLocal: boolean; | ||
scope: any; | ||
capturedScopes?: CapturedScopes; | ||
} | ||
@@ -21,0 +27,0 @@ export interface SxReservedNames { |
@@ -1,2 +0,2 @@ | ||
import { SxParserState, SxSymbol, SxToken, SxScope } from './types'; | ||
import { SxParserState, SxSymbol, SxToken, SxScope, CapturedScopes } from './types'; | ||
export declare function toNumber(x: any): number; | ||
@@ -7,3 +7,5 @@ export declare function resolveMacro(state: SxParserState, x: SxSymbol): ((list: SxToken[]) => SxToken) | false; | ||
export declare function resolveValueSymbol(state: SxParserState, x: SxSymbol): any; | ||
export declare function installScope(state: SxParserState, scope: any, isBlockLocal: boolean): any; | ||
export declare function collectCapturedVariables(state: SxParserState, names: SxSymbol[]): CapturedScopes; | ||
export declare function getCapturedScopes(state: SxParserState): CapturedScopes | undefined; | ||
export declare function installScope(state: SxParserState, scope: any, isBlockLocal: boolean, capturedScopes?: CapturedScopes): any; | ||
export declare function uninstallScope(state: SxParserState): any; | ||
@@ -10,0 +12,0 @@ export declare function getScope(state: SxParserState): SxScope; |
@@ -53,2 +53,6 @@ // Copyright (c) 2018, Shellyl_N and Authors | ||
} | ||
if (localScope.capturedScopes && | ||
Object.prototype.hasOwnProperty.call(localScope.capturedScopes, x.symbol)) { | ||
return localScope.capturedScopes[x.symbol]; | ||
} | ||
if (!localScope.isBlockLocal) { | ||
@@ -83,5 +87,29 @@ break; | ||
} | ||
export function installScope(state, scope, isBlockLocal) { | ||
state.scopes.push({ isBlockLocal, scope }); | ||
export function collectCapturedVariables(state, names) { | ||
const capturedScopes = {}; | ||
for (const n of names) { | ||
const scope = resolveValueSymbolScope(state, n, true); | ||
if (scope === null) { | ||
throw new Error(`[SX] collectCapturedVariables: Unresolved symbols ${n}`); | ||
} | ||
capturedScopes[n.symbol] = scope; | ||
} | ||
return capturedScopes; | ||
} | ||
export function getCapturedScopes(state) { | ||
const a = []; | ||
for (let i = state.scopes.length - 1; i > 0; i--) { | ||
const localScope = state.scopes[i]; | ||
if (localScope.capturedScopes) { | ||
a.unshift(localScope.capturedScopes); | ||
} | ||
if (!localScope.isBlockLocal) { | ||
break; | ||
} | ||
} | ||
return a.length > 0 ? Object.assign({}, ...a) : void 0; | ||
} | ||
export function installScope(state, scope, isBlockLocal, capturedScopes) { | ||
state.scopes.push({ isBlockLocal, scope, capturedScopes }); | ||
} | ||
export function uninstallScope(state) { | ||
@@ -88,0 +116,0 @@ if (state.scopes.length < 2) { |
@@ -1,2 +0,2 @@ | ||
import { SxParserState, SxToken } from '../../types'; | ||
import { SxParserState, SxToken, CapturedScopes } from '../../types'; | ||
export declare const $car: (state: SxParserState, name: string) => (...args: any[]) => any; | ||
@@ -38,6 +38,8 @@ export declare const $$car: (...args: any[]) => any; | ||
export declare const $$list: (...args: any[]) => any[]; | ||
export declare const $__scope: (state: SxParserState, name: string) => (...args: any[]) => SxToken; | ||
export declare const $__scope: (state: SxParserState, name: string, capturedScopes?: CapturedScopes | undefined) => (...args: any[]) => SxToken; | ||
export declare const $__globalScope: (state: SxParserState, name: string) => (...args: any[]) => SxToken; | ||
export declare const $__capture: (state: SxParserState, name: string) => (...args: any[]) => SxToken; | ||
export declare const $__lambda: (state: SxParserState, name: string) => (...args: any[]) => (...actualArgs: any[]) => SxToken; | ||
export declare const $__defun: (state: SxParserState, name: string) => (...args: any[]) => (...actualArgs: any[]) => SxToken; | ||
export declare const $__refun: (state: SxParserState, name: string) => (...args: any[]) => (...args: any[]) => any; | ||
export declare const $apply: (state: SxParserState, name: string) => (...args: any[]) => (...p: any[]) => any; | ||
@@ -44,0 +46,0 @@ export declare const $$apply: (...args: any[]) => (...p: any[]) => any; |
@@ -5,3 +5,3 @@ // Copyright (c) 2018, Shellyl_N and Authors | ||
import { isSymbol, quote, FatalError } from '../../types'; | ||
import { evaluate, resolveValueSymbolScope, getScope, getGlobalScope, installScope, uninstallScope, optimizeTailCall, toNumber } from '../../evaluate'; | ||
import { evaluate, resolveValueSymbolScope, collectCapturedVariables, getCapturedScopes, getScope, getGlobalScope, installScope, uninstallScope, optimizeTailCall, toNumber } from '../../evaluate'; | ||
import { checkParamsLength } from '../../errors'; | ||
@@ -157,3 +157,3 @@ export const $car = (state, name) => (...args) => { | ||
// tslint:disable-next-line:variable-name | ||
export const $__scope = (state, name) => (...args) => { | ||
export const $__scope = (state, name, capturedScopes) => (...args) => { | ||
// S expression: ($__scope isBlockLocal returnMultiple '((name value) | name ...) 'expr1 ... 'exprN) | ||
@@ -167,20 +167,18 @@ // -> (if returnMultiple) S expr : [expr1 ... exprN] | ||
let r = null; | ||
let scopeInstalled = false; | ||
try { | ||
const scope = {}; | ||
if (Array.isArray(car)) { | ||
for (const x of car) { | ||
if (Array.isArray(x)) { | ||
const kv = $$firstAndSecond(...x); | ||
const kvSym = isSymbol(kv.car); | ||
scope[kvSym ? kvSym.symbol : String(kv.car)] = evaluate(state, kv.cdr); | ||
} | ||
else { | ||
const xSym = isSymbol(x); | ||
scope[xSym ? xSym.symbol : String(x)] = null; | ||
} | ||
const scope = {}; | ||
if (Array.isArray(car)) { | ||
for (const x of car) { | ||
if (Array.isArray(x)) { | ||
const kv = $$firstAndSecond(...x); | ||
const kvSym = isSymbol(kv.car); | ||
scope[kvSym ? kvSym.symbol : String(kv.car)] = evaluate(state, kv.cdr); | ||
} | ||
else { | ||
const xSym = isSymbol(x); | ||
scope[xSym ? xSym.symbol : String(x)] = null; | ||
} | ||
} | ||
installScope(state, scope, isBlockLocal); | ||
scopeInstalled = true; | ||
} | ||
installScope(state, scope, isBlockLocal, capturedScopes); | ||
try { | ||
if (4 < args.length) { | ||
@@ -204,5 +202,3 @@ if (returnMultiple) { | ||
finally { | ||
if (scopeInstalled) { | ||
uninstallScope(state); | ||
} | ||
uninstallScope(state); | ||
} | ||
@@ -220,4 +216,4 @@ return r; | ||
let r = null; | ||
installScope(state, getGlobalScope(state).scope, true); | ||
try { | ||
installScope(state, getGlobalScope(state).scope, true); | ||
if (2 < args.length) { | ||
@@ -246,2 +242,24 @@ if (returnMultiple) { | ||
// tslint:disable-next-line:variable-name | ||
export const $__capture = (state, name) => (...args) => { | ||
// S expression: ($__capture '(sym1 ... symN) 'expr1 ... 'exprN) | ||
// -> S expr : exprN | ||
checkParamsLength('$__capture', args, 1); | ||
const formalArgs = args[0]; | ||
if (!Array.isArray(formalArgs)) { | ||
throw new Error(`[SX] $__lambda: Invalid argument(s): args[0] is not array.`); | ||
} | ||
let r = null; | ||
const capturedScopes = collectCapturedVariables(state, formalArgs); | ||
installScope(state, {}, true, capturedScopes); | ||
try { | ||
for (const x of args.slice(1)) { | ||
r = evaluate(state, x); | ||
} | ||
} | ||
finally { | ||
uninstallScope(state); | ||
} | ||
return r; | ||
}; | ||
// tslint:disable-next-line:variable-name | ||
export const $__lambda = (state, name) => (...args) => { | ||
@@ -274,2 +292,3 @@ // S expression: ($__lambda '(sym1 ... symN) 'expr1 ... 'exprN) | ||
} | ||
const capturedScopes = getCapturedScopes(state); | ||
const fn = (...actualArgs) => { | ||
@@ -279,3 +298,3 @@ if ((actualArgs.length + (lastIsSpread ? 1 : 0)) < formalArgs.length) { | ||
} | ||
return $__scope(state, name)(false, false, [ | ||
return $__scope(state, name, capturedScopes)(false, false, [ | ||
[state.config.reservedNames.self, fn], | ||
@@ -304,2 +323,14 @@ ...(formalArgs.map((x, index) => [ | ||
}; | ||
// tslint:disable-next-line:variable-name | ||
export const $__refun = (state, name) => (...args) => { | ||
// S expression: ($refun 'name) | ||
// -> S expr : fn | ||
checkParamsLength('$__refun', args, 1, 1); | ||
const car = $$first(...args); | ||
const info = state.funcMap.get(car.symbol); | ||
if (!info) { | ||
throw new Error(`[SX] $__refun: function ${car.symbol} is not defined.`); | ||
} | ||
return info.fn(state, car.symbol); | ||
}; | ||
export const $apply = (state, name) => (...args) => { | ||
@@ -306,0 +337,0 @@ // S expression: ($apply fn arg1 ... argN) |
@@ -50,2 +50,34 @@ // Copyright (c) 2018, Shellyl_N and Authors | ||
}, { | ||
name: '$capture', | ||
fn: (state, name) => (list) => { | ||
// S expression: ($capture (sym1 ... symN) expr ... expr) | ||
// -> S expr : ($__capture '(sym1 ... symN) 'expr ... 'expr) | ||
return [{ symbol: '$__capture' }, | ||
...(list.slice(1).map(x => quote(state, x))), | ||
]; | ||
}, | ||
}, { | ||
name: '$closure', | ||
fn: (state, name) => (list) => { | ||
// S expression: ($closure (sym1 ... symN) use (u-sym1 ... u-symM) expr ... expr) | ||
// -> S expr : ($__capture '(u-sym1 ... u-symM) ($__lambda '(sym1 ... symN) 'expr ... 'expr) ) | ||
const symUse = isSymbol(list[2], 'use'); | ||
if (!symUse) { | ||
throw new Error(`[SX] $closure: Invalid syntax: missing 'use' keyword.`); | ||
} | ||
return [{ symbol: '$__capture' }, quote(state, list[3]), quote(state, [{ symbol: '$__lambda' }, | ||
quote(state, list[1]), | ||
...(list.slice(4).map(x => quote(state, x))), | ||
])]; | ||
}, | ||
}, { | ||
name: '|->', | ||
fn: (state, name) => (list) => { | ||
// S expression: (|-> (sym1 ... symN) use (u-sym1 ... u-symM) expr ... expr) | ||
// -> S expr : ($closure (sym1 ... symN) use (u-sym1 ... u-symM) expr ... expr) | ||
return [{ symbol: '$closure' }, | ||
...list.slice(1), | ||
]; | ||
}, | ||
}, { | ||
name: '$lambda', | ||
@@ -78,2 +110,20 @@ fn: (state, name) => (list) => { | ||
}, { | ||
name: '$refun', | ||
fn: (state, name) => (list) => { | ||
// S expression: ($refun name) | ||
// -> S expr : ($__refun 'name) | ||
return [{ symbol: '$__refun' }, | ||
...(list.slice(1).map(x => quote(state, x))), | ||
]; | ||
}, | ||
}, { | ||
name: '<-', | ||
fn: (state, name) => (list) => { | ||
// S expression: (<- name) | ||
// -> S expr : ($__refun 'name) | ||
return [{ symbol: '$__refun' }, | ||
...(list.slice(1).map(x => quote(state, x))), | ||
]; | ||
}, | ||
}, { | ||
name: '$call', | ||
@@ -80,0 +130,0 @@ fn: (state, name) => (list) => { |
@@ -57,2 +57,5 @@ // Copyright (c) 2018, Shellyl_N and Authors | ||
}, { | ||
name: '$__capture', | ||
fn: ops.$__capture, | ||
}, { | ||
name: '$__lambda', | ||
@@ -64,2 +67,5 @@ fn: ops.$__lambda, | ||
}, { | ||
name: '$__refun', | ||
fn: ops.$__refun, | ||
}, { | ||
name: '$apply', | ||
@@ -66,0 +72,0 @@ fn: ops.$apply, |
@@ -16,5 +16,11 @@ export declare type SxMacro = (state: SxParserState, name: string) => (list: SxToken[]) => SxToken; | ||
} | ||
export interface CapturedScopes { | ||
[s: string]: { | ||
[s: string]: any; | ||
}; | ||
} | ||
export interface SxScope { | ||
isBlockLocal: boolean; | ||
scope: any; | ||
capturedScopes?: CapturedScopes; | ||
} | ||
@@ -21,0 +27,0 @@ export interface SxReservedNames { |
{ | ||
"name": "liyad", | ||
"private": false, | ||
"version": "0.0.11", | ||
"version": "0.0.12", | ||
"description": "Liyad (Lisp yet another DSL interpreter) is very small Lisp interpreter written in JavaScript.", | ||
@@ -6,0 +6,0 @@ "keywords": [ |
@@ -13,3 +13,4 @@ // Copyright (c) 2018, Shellyl_N and Authors | ||
SxScope, | ||
isSymbol } from './types'; | ||
isSymbol, | ||
CapturedScopes } from './types'; | ||
import { setEvaluationCount } from './errors'; | ||
@@ -68,2 +69,6 @@ | ||
} | ||
if (localScope.capturedScopes && | ||
Object.prototype.hasOwnProperty.call(localScope.capturedScopes, x.symbol)) { | ||
return localScope.capturedScopes[x.symbol]; | ||
} | ||
if (! localScope.isBlockLocal) { | ||
@@ -101,7 +106,35 @@ break; | ||
export function installScope(state: SxParserState, scope: any, isBlockLocal: boolean): any { | ||
state.scopes.push({isBlockLocal, scope}); | ||
export function collectCapturedVariables(state: SxParserState, names: SxSymbol[]): CapturedScopes { | ||
const capturedScopes: CapturedScopes = {}; | ||
for (const n of names) { | ||
const scope = resolveValueSymbolScope(state, n, true); | ||
if (scope === null) { | ||
throw new Error(`[SX] collectCapturedVariables: Unresolved symbols ${n}`); | ||
} | ||
capturedScopes[n.symbol] = scope; | ||
} | ||
return capturedScopes; | ||
} | ||
export function getCapturedScopes(state: SxParserState): CapturedScopes | undefined { | ||
const a: CapturedScopes[] = []; | ||
for (let i = state.scopes.length - 1; i > 0; i--) { | ||
const localScope: SxScope = state.scopes[i]; | ||
if (localScope.capturedScopes) { | ||
a.unshift(localScope.capturedScopes); | ||
} | ||
if (! localScope.isBlockLocal) { | ||
break; | ||
} | ||
} | ||
return a.length > 0 ? Object.assign({}, ...a) : void 0; | ||
} | ||
export function installScope(state: SxParserState, scope: any, isBlockLocal: boolean, capturedScopes?: CapturedScopes): any { | ||
state.scopes.push({isBlockLocal, scope, capturedScopes}); | ||
} | ||
export function uninstallScope(state: SxParserState): any { | ||
@@ -108,0 +141,0 @@ if (state.scopes.length < 2) { |
@@ -11,5 +11,8 @@ // Copyright (c) 2018, Shellyl_N and Authors | ||
quote, | ||
FatalError } from '../../types'; | ||
FatalError, | ||
CapturedScopes } from '../../types'; | ||
import { evaluate, | ||
resolveValueSymbolScope, | ||
collectCapturedVariables, | ||
getCapturedScopes, | ||
getScope, | ||
@@ -203,3 +206,3 @@ getGlobalScope, | ||
// tslint:disable-next-line:variable-name | ||
export const $__scope = (state: SxParserState, name: string) => (...args: any[]) => { | ||
export const $__scope = (state: SxParserState, name: string, capturedScopes?: CapturedScopes) => (...args: any[]) => { | ||
// S expression: ($__scope isBlockLocal returnMultiple '((name value) | name ...) 'expr1 ... 'exprN) | ||
@@ -214,21 +217,19 @@ // -> (if returnMultiple) S expr : [expr1 ... exprN] | ||
let r: SxToken = null; | ||
let scopeInstalled = false; | ||
try { | ||
const scope: any = {}; | ||
if (Array.isArray(car)) { | ||
for (const x of car) { | ||
if (Array.isArray(x)) { | ||
const kv = $$firstAndSecond(...x); | ||
const kvSym = isSymbol(kv.car); | ||
scope[kvSym ? kvSym.symbol : String(kv.car)] = evaluate(state, kv.cdr); | ||
} else { | ||
const xSym = isSymbol(x); | ||
scope[xSym ? xSym.symbol : String(x)] = null; | ||
} | ||
const scope: any = {}; | ||
if (Array.isArray(car)) { | ||
for (const x of car) { | ||
if (Array.isArray(x)) { | ||
const kv = $$firstAndSecond(...x); | ||
const kvSym = isSymbol(kv.car); | ||
scope[kvSym ? kvSym.symbol : String(kv.car)] = evaluate(state, kv.cdr); | ||
} else { | ||
const xSym = isSymbol(x); | ||
scope[xSym ? xSym.symbol : String(x)] = null; | ||
} | ||
} | ||
installScope(state, scope, isBlockLocal); | ||
scopeInstalled = true; | ||
} | ||
installScope(state, scope, isBlockLocal, capturedScopes); | ||
try { | ||
if (4 < args.length) { | ||
@@ -249,5 +250,3 @@ if (returnMultiple) { | ||
} finally { | ||
if (scopeInstalled) { | ||
uninstallScope(state); | ||
} | ||
uninstallScope(state); | ||
} | ||
@@ -270,5 +269,4 @@ | ||
installScope(state, getGlobalScope(state).scope, true); | ||
try { | ||
installScope(state, getGlobalScope(state).scope, true); | ||
if (2 < args.length) { | ||
@@ -297,2 +295,29 @@ if (returnMultiple) { | ||
// tslint:disable-next-line:variable-name | ||
export const $__capture = (state: SxParserState, name: string) => (...args: any[]) => { | ||
// S expression: ($__capture '(sym1 ... symN) 'expr1 ... 'exprN) | ||
// -> S expr : exprN | ||
checkParamsLength('$__capture', args, 1); | ||
const formalArgs: SxSymbol[] = args[0]; | ||
if (! Array.isArray(formalArgs)) { | ||
throw new Error(`[SX] $__lambda: Invalid argument(s): args[0] is not array.`); | ||
} | ||
let r: SxToken = null; | ||
const capturedScopes = collectCapturedVariables(state, formalArgs); | ||
installScope(state, {}, true, capturedScopes); | ||
try { | ||
for (const x of args.slice(1)) { | ||
r = evaluate(state, x); | ||
} | ||
} finally { | ||
uninstallScope(state); | ||
} | ||
return r; | ||
}; | ||
// tslint:disable-next-line:variable-name | ||
export const $__lambda = (state: SxParserState, name: string) => (...args: any[]) => { | ||
@@ -328,2 +353,4 @@ // S expression: ($__lambda '(sym1 ... symN) 'expr1 ... 'exprN) | ||
const capturedScopes = getCapturedScopes(state); | ||
const fn = (...actualArgs: any[]) => { | ||
@@ -334,3 +361,3 @@ if ((actualArgs.length + (lastIsSpread ? 1 : 0)) < formalArgs.length) { | ||
} | ||
return $__scope(state, name)(false, false, [ | ||
return $__scope(state, name, capturedScopes)(false, false, [ | ||
[state.config.reservedNames.self, fn], | ||
@@ -366,2 +393,17 @@ ...(formalArgs.map((x: SxSymbol, index) => [ | ||
// tslint:disable-next-line:variable-name | ||
export const $__refun = (state: SxParserState, name: string) => (...args: any[]) => { | ||
// S expression: ($refun 'name) | ||
// -> S expr : fn | ||
checkParamsLength('$__refun', args, 1, 1); | ||
const car: SxSymbol = $$first(...args); | ||
const info = state.funcMap.get(car.symbol); | ||
if (!info) { | ||
throw new Error(`[SX] $__refun: function ${car.symbol} is not defined.`); | ||
} | ||
return info.fn(state, car.symbol); | ||
}; | ||
export const $apply = (state: SxParserState, name: string) => (...args: any[]) => { | ||
@@ -368,0 +410,0 @@ // S expression: ($apply fn arg1 ... argN) |
@@ -58,2 +58,34 @@ // Copyright (c) 2018, Shellyl_N and Authors | ||
}, { | ||
name: '$capture', | ||
fn: (state: SxParserState, name: string) => (list) => { | ||
// S expression: ($capture (sym1 ... symN) expr ... expr) | ||
// -> S expr : ($__capture '(sym1 ... symN) 'expr ... 'expr) | ||
return [{symbol: '$__capture'}, | ||
...(list.slice(1).map(x => quote(state, x))), | ||
]; | ||
}, | ||
}, { | ||
name: '$closure', | ||
fn: (state: SxParserState, name: string) => (list) => { | ||
// S expression: ($closure (sym1 ... symN) use (u-sym1 ... u-symM) expr ... expr) | ||
// -> S expr : ($__capture '(u-sym1 ... u-symM) ($__lambda '(sym1 ... symN) 'expr ... 'expr) ) | ||
const symUse = isSymbol(list[2], 'use'); | ||
if (! symUse) { | ||
throw new Error(`[SX] $closure: Invalid syntax: missing 'use' keyword.`); | ||
} | ||
return [{symbol: '$__capture'}, quote(state, list[3]), quote(state, [{symbol: '$__lambda'}, | ||
quote(state, list[1]), | ||
...(list.slice(4).map(x => quote(state, x))), | ||
])]; | ||
}, | ||
}, { | ||
name: '|->', | ||
fn: (state: SxParserState, name: string) => (list) => { | ||
// S expression: (|-> (sym1 ... symN) use (u-sym1 ... u-symM) expr ... expr) | ||
// -> S expr : ($closure (sym1 ... symN) use (u-sym1 ... u-symM) expr ... expr) | ||
return [{symbol: '$closure'}, | ||
...list.slice(1), | ||
]; | ||
}, | ||
}, { | ||
name: '$lambda', | ||
@@ -86,2 +118,20 @@ fn: (state: SxParserState, name: string) => (list) => { | ||
}, { | ||
name: '$refun', | ||
fn: (state: SxParserState, name: string) => (list) => { | ||
// S expression: ($refun name) | ||
// -> S expr : ($__refun 'name) | ||
return [{symbol: '$__refun'}, | ||
...(list.slice(1).map(x => quote(state, x))), | ||
]; | ||
}, | ||
}, { | ||
name: '<-', | ||
fn: (state: SxParserState, name: string) => (list) => { | ||
// S expression: (<- name) | ||
// -> S expr : ($__refun 'name) | ||
return [{symbol: '$__refun'}, | ||
...(list.slice(1).map(x => quote(state, x))), | ||
]; | ||
}, | ||
}, { | ||
name: '$call', | ||
@@ -88,0 +138,0 @@ fn: (state: SxParserState, name: string) => (list) => { |
@@ -63,2 +63,5 @@ // Copyright (c) 2018, Shellyl_N and Authors | ||
}, { | ||
name: '$__capture', | ||
fn: ops.$__capture, | ||
}, { | ||
name: '$__lambda', | ||
@@ -70,2 +73,5 @@ fn: ops.$__lambda, | ||
}, { | ||
name: '$__refun', | ||
fn: ops.$__refun, | ||
}, { | ||
name: '$apply', | ||
@@ -72,0 +78,0 @@ fn: ops.$apply, |
@@ -28,5 +28,8 @@ | ||
export interface CapturedScopes { [s: string]: { [s: string]: any; }; } | ||
export interface SxScope { | ||
isBlockLocal: boolean; | ||
scope: any; | ||
scope: any; // { [s: string]: any; }; | ||
capturedScopes?: CapturedScopes; | ||
} | ||
@@ -33,0 +36,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
1152289
22836