Skip to content

Commit

Permalink
Define function seed_type.
Browse files Browse the repository at this point in the history
Basically a `let-multi` but where you don't include a namespace in the names, and it's clearer the intent of the user.

Part of #20.
  • Loading branch information
jkomoros committed Jul 29, 2023
1 parent 7acc233 commit fa0187c
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 4 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,16 @@ Required parameters:
- `values` - The object of name -> value pairs to set.
- `block` - The sub-seed that will be evaluated where the environment will have `name=value`.
#### function
Defines a procedure that other seeds can call with `call`.
Note that semantically it's basically equivalent to a `let-multi` with named arguments, but using `function` communicates more clearly the intent that this is a callable-sub-procedure, which allows tooling to understand it better.
Required parameters:
- `arguments` - The object of name -> value pairs to set. These variable names should not include a namespace. Internally they will be prefixed with the `arg:` namespace.
- `block` - The sub-seed that will be evaluated where the environment will have `name=value`.
#### store
Stores a value in the long-term key/val store.
Expand Down
78 changes: 78 additions & 0 deletions seed-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2879,6 +2879,84 @@
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"id": {
"$ref": "#/definitions/seedData/anyOf/0/properties/id"
},
"description": {
"$ref": "#/definitions/seedData/anyOf/0/properties/description"
},
"comment": {
"$ref": "#/definitions/seedData/anyOf/0/properties/comment"
},
"private": {
"$ref": "#/definitions/seedData/anyOf/0/properties/private"
},
"type": {
"type": "string",
"const": "function"
},
"arguments": {
"anyOf": [
{
"$ref": "#/definitions/seedData"
},
{
"$ref": "#/definitions/seedData/anyOf/0/properties/prompt/anyOf/1"
},
{
"type": "object",
"additionalProperties": {
"anyOf": [
{
"$ref": "#/definitions/seedData/anyOf/27/properties/properties/additionalProperties/anyOf/0"
},
{
"$ref": "#/definitions/seedData/anyOf/0/properties/prompt/anyOf/1"
},
{
"$ref": "#/definitions/seedData/anyOf/5/properties/value/anyOf/2/anyOf/1/additionalProperties/anyOf/2"
}
]
},
"propertyNames": {
"pattern": "^[a-zA-Z0-9-_.]*$"
},
"description": "The map of name -> variables to set"
}
]
},
"block": {
"anyOf": [
{
"$ref": "#/definitions/seedData"
},
{
"$ref": "#/definitions/seedData/anyOf/0/properties/prompt/anyOf/1"
},
{
"anyOf": [
{
"$ref": "#/definitions/seedData/anyOf/5/properties/value/anyOf/2/anyOf/0"
},
{
"$ref": "#/definitions/seedData/anyOf/5/properties/value/anyOf/2/anyOf/1/additionalProperties/anyOf/2/anyOf/2"
}
],
"description": "The sub-expression where name=value will be set in environment"
}
]
}
},
"required": [
"type",
"arguments",
"block"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
Expand Down
4 changes: 2 additions & 2 deletions src/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ import {

const CHANGE_ME_SENTINEL = 'CHANGE_ME';

const isNamespaced = (input : MemoryID | StoreID | VarName) : boolean => {
export const isNamespaced = (input : MemoryID | StoreID | VarName) : boolean => {
return input.includes(NAMESPACE_DELIMITER);
};

const getNamespacedID = <I extends MemoryID | StoreID | VarName>(input : I, namespace : Namespace) : I => {
export const getNamespacedID = <I extends MemoryID | StoreID | VarName>(input : I, namespace : Namespace) : I => {
if (isNamespaced(input)) return input;
if (!namespaceSchema.safeParse(namespace).success) throw new Error(`${namespace} was not a valid namespace`);
return (namespace + NAMESPACE_DELIMITER + input) as I;
Expand Down
27 changes: 25 additions & 2 deletions src/grow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ import {
SeedDataFetch,
fetchMethod,
fetchFormat,
SeedDataFilter
SeedDataFilter,
SeedDataFunction
} from './types.js';

import {
Expand All @@ -79,7 +80,7 @@ import {
} from './template.js';

import {
Environment
Environment, getNamespacedID, isNamespaced
} from './environment.js';

import {
Expand Down Expand Up @@ -686,6 +687,25 @@ const growLetMulti = async (seed : Seed<SeedDataLetMulti>, env : Environment) :
return await getProperty(seed, newEnv, data.block);
};

const FUNCTION_ARG_NAMESPACE = 'arg';

const growFunction = async (seed : Seed<SeedDataFunction>, env : Environment) : Promise<Value> => {
const data = seed.data;

const values = await getProperty(seed, env, data.arguments);
if (typeof values != 'object') throw new Error('Values must be an object');
if (Array.isArray(values)) throw new Error('Values must be an object');
if (!values) throw new Error('Values must be an object');
const vars : EnvironmentData = {};
for (const [key, val] of Object.entries(values)) {
if (isNamespaced(key)) throw new Error('arguments should not be namespaced');
const processedKey = getNamespacedID(key, FUNCTION_ARG_NAMESPACE);
vars[processedKey] = val;
}
const newEnv = env.clone(vars);
return await getProperty(seed, newEnv, data.block);
};

const growStore = async (seed : Seed<SeedDataStore>, env : Environment) : Promise<Value> => {
const data = seed.data;
const rawStoreID = extractString(await getProperty(seed, env, data.store, env.getKnownStringKey('store')));
Expand Down Expand Up @@ -844,6 +864,9 @@ export const grow = async (seed : Seed, env : Environment) : Promise<Value> => {
case 'let-multi':
result = await growLetMulti(seed as Seed<SeedDataLetMulti>, env);
break;
case 'function':
result = await growFunction(seed as Seed<SeedDataFunction>, env);
break;
case 'store':
result = await growStore(seed as Seed<SeedDataStore>, env);
break;
Expand Down
20 changes: 20 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,24 @@ const seedDataLetMulti = makeSeedData(seedDataConfigLetMulti);

export type SeedDataLetMulti = z.infer<typeof seedDataLetMulti>;

const seedDataConfigFunction = {
type: z.literal('function'),
properties: {
//We don't use varName because we want a non-namespaced ID
arguments: z.record(genericExtraID, z.union([
lazySeedData,
seedReference,
inputValue
])).describe('The map of name -> variables to set'),
block: inputNonObjectValue.describe('The sub-expression where name=value will be set in environment')
}
};

const nestedSeedDataFunction = makeNestedSeedData(seedDataConfigFunction);
const seedDataFunction = makeSeedData(seedDataConfigFunction);

export type SeedDataFunction = z.infer<typeof seedDataFunction>;

const seedDataConfigStore = {
type: z.literal('store'),
properties: {
Expand Down Expand Up @@ -1028,6 +1046,7 @@ export const expandedSeedData = z.discriminatedUnion('type', [
seedDataRandomSeed,
seedDataLet,
seedDataLetMulti,
seedDataFunction,
seedDataStore,
seedDataRetrieve,
seedDataDelete
Expand Down Expand Up @@ -1075,6 +1094,7 @@ export const seedData = z.discriminatedUnion('type', [
nestedSeedDataRandomSeed,
nestedSeedDataLet,
nestedSeedDataLetMulti,
nestedSeedDataFunction,
nestedSeedDataStore,
nestedSeedDataRetrieve,
nestedSeedDataDelete
Expand Down

0 comments on commit fa0187c

Please sign in to comment.