Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/compiler/src/ast-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,10 @@ export function extractExpressionValue(value: unknown): unknown {
if (typeof value === 'object') {
const obj = value as { __kind?: string; elements?: unknown[] };

if (obj.__kind === 'NoneLiteral') {
return null;
}

if (obj.__kind === 'DictLiteral') {
return extractDictExpression(value as Expression);
}
Expand Down
12 changes: 11 additions & 1 deletion packages/compiler/test/ast-helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ import {
AtIdentifier,
Identifier,
MemberExpression,
NoneLiteral,
} from '@agentscript/language';
import { resolveAtReference } from '../src/ast-helpers.js';
import {
extractExpressionValue,
resolveAtReference,
} from '../src/ast-helpers.js';
import { CompilerContext } from '../src/compiler-context.js';

let ctx: CompilerContext;
Expand All @@ -22,6 +26,12 @@ beforeEach(() => {
ctx = new CompilerContext();
});

describe('extractExpressionValue', () => {
it('should extract NoneLiteral as JSON null', () => {
expect(extractExpressionValue(new NoneLiteral())).toBeNull();
});
});

describe('resolveAtReference', () => {
it('should resolve @namespace.property to the property name', () => {
const expr = new MemberExpression(new AtIdentifier('actions'), 'do_thing');
Expand Down
45 changes: 45 additions & 0 deletions packages/compiler/test/state-variables.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,51 @@ variables:
// ---------------------------------------------------------------------------

describe('state variables: dict and object defaults', () => {
it.each([
{
name: 'object properties containing None',
type: 'object',
literal: '{"missing": None, "kept": 1}',
expected: { missing: null, kept: 1 },
},
{
name: 'list positions containing None',
type: 'list[object]',
literal: '[None, {"n": 1}, None]',
expected: [null, { n: 1 }, null],
},
{
name: 'None inside an object containing a list and another object',
type: 'object',
literal:
'{"outer": {"items": [None, {"missing": None, "kept": 1}, None]}}',
expected: { outer: { items: [null, { missing: null, kept: 1 }, null] } },
},
{
name: 'None inside a list containing another list and an object',
type: 'list[object]',
literal: '[None, [None, {"missing": None, "kept": 1}], None]',
expected: [null, [null, { missing: null, kept: 1 }], null],
},
{
name: 'falsy values inside objects and lists',
type: 'object',
literal: '{"no": False, "zero": 0, "empty": "", "items": [False, 0, ""]}',
expected: { no: false, zero: 0, empty: '', items: [false, 0, ''] },
},
])('should preserve $name in defaults', ({ type, literal, expected }) => {
const result = compileSource(
agentSource(`
variables:
value: mutable ${type} = ${literal}`)
);

expect(result.diagnostics).toEqual([]);
const sv = findStateVar(result, 'value');
expect(sv).toBeDefined();
expect(sv!.default).toStrictEqual(expected);
});

it('should compile variable with empty dict default', () => {
const result = compileSource(
agentSource(`
Expand Down
Loading