@bramas/simulant-lang 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/builder.js CHANGED
@@ -208,7 +208,7 @@ export function task(spec) {
208
208
  return dropUndefined({ name: spec.name, goal: spec.goal, verdict: ir(spec.verdict(v('C'))), equivariance: spec.equivariance, defs: spec.defs });
209
209
  }
210
210
  export function experiment(spec) {
211
- const params = (spec.params ?? []).map(p => dropUndefined({ name: p.name, type: p.type, domain: p.domain === undefined ? undefined : ir(p.domain) }));
211
+ const params = (spec.params ?? []).map(p => dropUndefined({ name: p.name, type: p.type, domain: p.domain === undefined ? undefined : ir(p.domain), accepts: p.accepts === undefined ? undefined : ir(p.accepts) }));
212
212
  return dropUndefined({
213
213
  irVersion: IR_VERSION, name: spec.name, model: spec.model, algorithm: spec.algorithm, task: spec.task, params,
214
214
  agents: { count: ir(spec.agents.count), pos: ir(spec.agents.pos(v('i'))) },
package/dist/check.js CHANGED
@@ -405,6 +405,8 @@ export class Checker {
405
405
  this.report(`parameter ${p.name}: ${showType(p.type)} cannot be stored`);
406
406
  if (p.domain)
407
407
  this.root(p.domain, {}, unit('domain', `domain of ${p.name}`), T.list(p.type));
408
+ if (p.accepts)
409
+ this.root(p.accepts, {}, unit('domain', `acceptance of ${p.name}`), T.bool);
408
410
  }
409
411
  this.root(model.topology.size, {}, unit('domain', 'topology size'), T.nat);
410
412
  this.root(exp.agents.count, {}, unit('init', 'agent count'), T.nat);
@@ -197,8 +197,13 @@ export class Elaborator {
197
197
  case 'ascribe':
198
198
  return this.meval(e.e, env, e.type);
199
199
  case 'name':
200
+ if (e.name === 'none' && expected?.t === 'name' && expected.name === 'Option')
201
+ return { m: 'option' };
200
202
  return env.get(e.name) ?? this.metaCall(e.name, [], e.span, env);
201
203
  case 'app': {
204
+ if (e.fn.t === 'name' && e.fn.name === 'some' && expected?.t === 'name' && expected.name === 'Option' && e.args.length === 1) {
205
+ return { m: 'option', value: this.meval(e.args[0].value, env, expected.args[0]) };
206
+ }
202
207
  if (e.fn.t !== 'name')
203
208
  fail('expected the name of a definition', e.fn.span);
204
209
  return this.metaCall(e.fn.name, e.args, e.span, env);
@@ -280,6 +285,7 @@ export class Elaborator {
280
285
  case 'str': return v.v;
281
286
  case 'enum': return v.ctor;
282
287
  case 'list': return v.items.map(i => this.json(i, span));
288
+ case 'option': return v.value ? this.json(v.value, span) : null;
283
289
  default: return fail('expected a value', span);
284
290
  }
285
291
  }
@@ -611,6 +617,8 @@ export class Elaborator {
611
617
  if (i.value.t !== 'nothing')
612
618
  p.domain = this.tr(i.value, { scope: new Map(), params: new Set(paramNames), owner: decl.name });
613
619
  paramNames.add(i.name);
620
+ if (i.param.accepts)
621
+ p.accepts = this.tr(i.param.accepts, { scope: new Map(), params: new Set(paramNames), owner: decl.name });
614
622
  return p;
615
623
  });
616
624
  const ictx = (extra = []) => ({ scope: new Map(extra), params: paramNames, owner: decl.name });
@@ -5,7 +5,7 @@
5
5
  export const KEYWORDS = new Set([
6
6
  'import', 'def', 'structure', 'inductive', 'sensor', 'task', 'goal', 'algorithm', 'experiment', 'where', 'let',
7
7
  'if', 'then', 'else', 'match', 'with', 'fun', 'choose', 'such', 'that', 'labels', 'param', 'flag', 'uses',
8
- 'true', 'false', 'raise', 'nothing', 'action',
8
+ 'true', 'false', 'raise', 'nothing', 'action', 'in',
9
9
  ]);
10
10
  /** Operators, longest first. Unicode and ASCII spellings are both accepted. */
11
11
  const OPERATORS = [
@@ -256,13 +256,15 @@ export class Parser {
256
256
  if (output)
257
257
  this.next();
258
258
  if (this.is('param')) {
259
- // `param N : Nat := domain`
259
+ // `param N : Nat in domain where predicate` (both clauses optional).
260
+ // Keep `:=` as an alias for the original finite-domain syntax.
260
261
  this.next();
261
262
  const name = this.ident();
262
263
  this.expect(':');
263
264
  const type = this.type();
264
- const value = this.is(':=') ? (this.next(), this.expr()) : { t: 'nothing', span: this.span(start) };
265
- items.push({ name, args: [], value, span: this.span(start), param: { type } });
265
+ const value = this.is('in') || this.is(':=') ? (this.next(), this.expr()) : { t: 'nothing', span: this.span(start) };
266
+ const accepts = this.is('where') ? (this.next(), this.expr()) : undefined;
267
+ items.push({ name, args: [], value, span: this.span(start), param: { type, ...(accepts ? { accepts } : {}) } });
266
268
  return;
267
269
  }
268
270
  // Item names may be keywords: `algorithm := ...`, `task := ...`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bramas/simulant-lang",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "license": "CECILL-B",
6
6
  "type": "module",
@@ -20,7 +20,7 @@
20
20
  "LICENCE.txt"
21
21
  ],
22
22
  "dependencies": {
23
- "@bramas/simulant-stdlib": "0.2.0"
23
+ "@bramas/simulant-stdlib": "0.3.0"
24
24
  },
25
25
  "repository": {
26
26
  "type": "git",
package/src/builder.ts CHANGED
@@ -340,11 +340,11 @@ export function task(spec: { name: string; goal: Goal; verdict: (C: X) => Arg; e
340
340
 
341
341
  export function experiment(spec: {
342
342
  name: string; model: Model; algorithm: Algorithm; task: Task;
343
- params?: { name: string; type: Type; domain?: Arg }[];
343
+ params?: { name: string; type: Type; domain?: Arg; accepts?: Arg }[];
344
344
  agents: { count: Arg; pos: (i: X) => Arg };
345
345
  limits?: Experiment['limits'];
346
346
  }): Experiment {
347
- const params: Param[] = (spec.params ?? []).map(p => dropUndefined({ name: p.name, type: p.type, domain: p.domain === undefined ? undefined : ir(p.domain) }));
347
+ const params: Param[] = (spec.params ?? []).map(p => dropUndefined({ name: p.name, type: p.type, domain: p.domain === undefined ? undefined : ir(p.domain), accepts: p.accepts === undefined ? undefined : ir(p.accepts) }));
348
348
  return dropUndefined({
349
349
  irVersion: IR_VERSION, name: spec.name, model: spec.model, algorithm: spec.algorithm, task: spec.task, params,
350
350
  agents: { count: ir(spec.agents.count), pos: ir(spec.agents.pos(v('i'))) },
package/src/check.ts CHANGED
@@ -388,6 +388,7 @@ export class Checker {
388
388
  for (const p of exp.params) {
389
389
  if (!isStorable(p.type)) this.report(`parameter ${p.name}: ${showType(p.type)} cannot be stored`);
390
390
  if (p.domain) this.root(p.domain, {}, unit('domain', `domain of ${p.name}`), T.list(p.type));
391
+ if (p.accepts) this.root(p.accepts, {}, unit('domain', `acceptance of ${p.name}`), T.bool);
391
392
  }
392
393
  this.root(model.topology.size, {}, unit('domain', 'topology size'), T.nat);
393
394
  this.root(exp.agents.count, {}, unit('init', 'agent count'), T.nat);
@@ -46,6 +46,7 @@ type MV =
46
46
  | { m: 'str'; v: string }
47
47
  | { m: 'enum'; type: Type & { k: 'enum' }; ctor: string }
48
48
  | { m: 'list'; items: MV[] }
49
+ | { m: 'option'; value?: MV }
49
50
  | { m: 'component'; ir: Component }
50
51
  | { m: 'model'; ir: Model }
51
52
  | { m: 'sensor'; ir: Sensor }
@@ -236,8 +237,12 @@ export class Elaborator {
236
237
  case 'ascribe':
237
238
  return this.meval(e.e, env, e.type);
238
239
  case 'name':
240
+ if (e.name === 'none' && expected?.t === 'name' && expected.name === 'Option') return { m: 'option' };
239
241
  return env.get(e.name) ?? this.metaCall(e.name, [], e.span, env);
240
242
  case 'app': {
243
+ if (e.fn.t === 'name' && e.fn.name === 'some' && expected?.t === 'name' && expected.name === 'Option' && e.args.length === 1) {
244
+ return { m: 'option', value: this.meval(e.args[0].value, env, expected.args[0]) };
245
+ }
241
246
  if (e.fn.t !== 'name') fail('expected the name of a definition', e.fn.span);
242
247
  return this.metaCall((e.fn as { name: string }).name, e.args, e.span, env);
243
248
  }
@@ -307,6 +312,7 @@ export class Elaborator {
307
312
  case 'nat': case 'bool': case 'str': return v.v;
308
313
  case 'enum': return v.ctor;
309
314
  case 'list': return v.items.map(i => this.json(i, span));
315
+ case 'option': return v.value ? this.json(v.value, span) : null;
310
316
  default: return fail('expected a value', span);
311
317
  }
312
318
  }
@@ -618,6 +624,7 @@ export class Elaborator {
618
624
  const p: Param = { name: i.name, type: this.type(i.param!.type) };
619
625
  if (i.value.t !== 'nothing') p.domain = this.tr(i.value, { scope: new Map(), params: new Set(paramNames), owner: decl.name });
620
626
  paramNames.add(i.name);
627
+ if (i.param!.accepts) p.accepts = this.tr(i.param!.accepts, { scope: new Map(), params: new Set(paramNames), owner: decl.name });
621
628
  return p;
622
629
  });
623
630
  const ictx = (extra: [string, string][] = []): OCtx => ({ scope: new Map(extra), params: paramNames, owner: decl.name });
package/src/ir.ts CHANGED
@@ -196,8 +196,10 @@ export type Goal =
196
196
  /** A verdict on configurations (`C` in scope) and a goal over executions. */
197
197
  export type Task = { name: string; verdict: Expr; goal: Goal; equivariance?: Equivariance; defs?: Def[] };
198
198
 
199
- /** A parameter, with the list of its values for `check-all` (earlier parameters in scope). */
200
- export type Param = { name: string; type: Type; domain?: Expr };
199
+ /** A parameter's domain: an optional exact finite list and/or a Boolean refinement.
200
+ * Both constraints apply when present. Earlier parameters are in scope in the
201
+ * list; the refinement also sees the parameter itself. No simulator defaults. */
202
+ export type Param = { name: string; type: Type; domain?: Expr; accepts?: Expr };
201
203
 
202
204
  /** Where a declaration is written (display only). */
203
205
  export type DeclSource = {
package/src/parse/ast.ts CHANGED
@@ -63,11 +63,11 @@ export type SStmt = { span: Span } & (
63
63
  | { s: 'call'; name: string; args: SExpr[] }
64
64
  );
65
65
 
66
- /** An item of a `where` block: `name args := value`, or `param N : T := domain` in experiments. */
66
+ /** An item of a `where` block: `name args := value`, or a parameter domain in experiments. */
67
67
  export type SWhereItem = {
68
68
  name: string; args: string[]; value: SExpr; span: Span;
69
- /** `param N : T := domain`, in experiments. */
70
- param?: { type: SType };
69
+ /** `param N : T in domain where predicate`, in experiments (both clauses optional). */
70
+ param?: { type: SType; accepts?: SExpr };
71
71
  /** `def p (x : T) := statements` and `main := statements`, in algorithms. */
72
72
  proc?: { params: SBinder[]; body: SStmt[] };
73
73
  };
@@ -30,7 +30,7 @@ export type Token = {
30
30
  export const KEYWORDS = new Set([
31
31
  'import', 'def', 'structure', 'inductive', 'sensor', 'task', 'goal', 'algorithm', 'experiment', 'where', 'let',
32
32
  'if', 'then', 'else', 'match', 'with', 'fun', 'choose', 'such', 'that', 'labels', 'param', 'flag', 'uses',
33
- 'true', 'false', 'raise', 'nothing', 'action',
33
+ 'true', 'false', 'raise', 'nothing', 'action', 'in',
34
34
  ]);
35
35
 
36
36
  /** Operators, longest first. Unicode and ASCII spellings are both accepted. */
@@ -252,13 +252,15 @@ export class Parser {
252
252
  const output = algorithm && this.peek().text === 'output' && this.peek(1).kind === 'ident';
253
253
  if (output) this.next();
254
254
  if (this.is('param')) {
255
- // `param N : Nat := domain`
255
+ // `param N : Nat in domain where predicate` (both clauses optional).
256
+ // Keep `:=` as an alias for the original finite-domain syntax.
256
257
  this.next();
257
258
  const name = this.ident();
258
259
  this.expect(':');
259
260
  const type = this.type();
260
- const value: SExpr = this.is(':=') ? (this.next(), this.expr()) : { t: 'nothing', span: this.span(start) };
261
- items.push({ name, args: [], value, span: this.span(start), param: { type } });
261
+ const value: SExpr = this.is('in') || this.is(':=') ? (this.next(), this.expr()) : { t: 'nothing', span: this.span(start) };
262
+ const accepts = this.is('where') ? (this.next(), this.expr()) : undefined;
263
+ items.push({ name, args: [], value, span: this.span(start), param: { type, ...(accepts ? { accepts } : {}) } });
262
264
  return;
263
265
  }
264
266
  // Item names may be keywords: `algorithm := ...`, `task := ...`.