Home Reference Source

src/stdlib/JSON.js

'use strict';

const Value = require('../Value');
const EasyObjectValue = require('../values/EasyObjectValue');
const ObjectValue = require('../values/ObjectValue');
const PrimitiveValue = require('../values/PrimitiveValue');
const ArrayValue = require('../values/ArrayValue');
const CompletionRecord = require('../CompletionRecord');

class JSONUtils {
	static *genJSONTokens(arr, o, map, str, strincr) {
		let str2 = str !== undefined ? str + strincr : undefined;

		if ( o instanceof PrimitiveValue ) {
			return arr.push(JSON.stringify(o.native));
		}


		if ( map.has(o) ) {
			return arr.push('[Circular]');
		}
		map.set(o, true);

		if ( o instanceof ArrayValue ) {
			arr.push('[');
			let length = yield * (yield * o.get('length')).toIntNative();
			for ( let i = 0; i < length; ++i ) {
				if ( i > 0 ) arr.push(',');
				if ( str !== undefined  ) arr.push('\n');
				let m = yield * o.get(i);
				if ( str !== undefined ) arr.push(str2);
				if ( m ) {
					if ( m.jsTypeName == 'undefined' ) arr.push('null');
					else yield * JSONUtils.genJSONTokens(arr, m, map, str2, strincr);
				}
			}
			if ( str !== undefined  ) arr.push('\n');
			if ( str !== undefined  ) arr.push(str);
			arr.push(']');
			return;
		}

		arr.push('{');

		let first = true;
		for ( let p of Object.keys(o.properties)) {
			let po = o.properties[p];
			if ( !po.enumerable ) continue;
			let v = yield * o.get(p);
			if ( v.jsTypeName === 'function' ) continue;

			if ( first ) first = false;
			else arr.push(',');
			if ( str !== undefined ) arr.push('\n', str2);



			arr.push(JSON.stringify(p), ':');
			if ( str !== undefined ) arr.push(' ');
			yield * JSONUtils.genJSONTokens(arr, v, map, str2, strincr);


		}
		if ( str !== undefined  ) arr.push('\n');
		arr.push('}');
	}
}

class JSONObject extends EasyObjectValue {
	static *parse(thiz, args, s) {
		let str = Value.emptyString;
		if ( args.length > 0 ) str = yield * args[0].toStringNative();
		try {
			var out = JSON.parse(str, (k, o) => {
				if ( o === undefined ) return Value.undef;
				if ( o === null ) return Value.null;

				let prim = Value.fromPrimativeNative(o);
				if ( prim ) return prim;


				if ( Array.isArray(o) ) {
					return ArrayValue.make(o, s.realm);
				}

				let v = new ObjectValue(s.realm);
				for ( var p in o ) {
					v.setImmediate(p, o[p]);
				}
				return v;
			});
			return out;
		} catch ( e ) {
			yield new CompletionRecord(CompletionRecord.THROW, Value.fromNative(e, s.realm));
		}
	}

	static *stringify(thiz, args, s) {
		let arr = [];
		let v = Value.undef;
		let replacer = null;
		let str;
		let strincr;

		if ( args.length > 0 ) v = args[0];
		if ( args.length > 1 ) replacer = args[1];
		if ( args.length > 2 ) {
			str = '';
			if ( args[2].jsTypeName === 'number' ) {
				let len = yield * args[2].toIntNative();
				strincr = new Array(1 + len).join(' ');
			} else {
				strincr = yield * args[2].toStringNative();
			}

		}
		if ( v.jsTypeName === 'undefined' ) return Value.undef;

		yield * JSONUtils.genJSONTokens(arr, v, new WeakMap(), str, strincr);
		return Value.fromNative(arr.join(''));
	}


}

module.exports = JSONObject;