UNPKG

bali-component-framework

Version:

This library provides a JavaScript based implementation of the Bali Nebula™ Component Framework.

781 lines (718 loc) 28.5 kB
/************************************************************************ * Copyright (c) Crater Dog Technologies(TM). All Rights Reserved. * ************************************************************************ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * * * This code is free software; you can redistribute it and/or modify it * * under the terms of The MIT License (MIT), as published by the Open * * Source Initiative. (See http://opensource.org/licenses/MIT) * ************************************************************************/ 'use strict'; /* * This package provides a robust and simple to use interface into the Bali Nebula™ * component framework. */ const EOL = '\n'; const utilities = require('./src/utilities'); const abstractions = require('./src/abstractions'); const agents = require('./src/agents'); const elements = require('./src/elements'); // depends on agents const collections = require('./src/collections'); const strings = require('./src/strings'); // depends on collections const trees = require('./src/trees'); agents.BDNParser = require('./src/agents/BDNParser').BDNParser; // must be last // AVOIDING CIRCULAR DEPENDENCIES /* * This function defines the canonical toString() method for all Component classes including * the Exception class which attempts to inherit from both Component and Error. The function * must be declared and assigned to these classes here instead of in their class definitions * to avoid circular dependencies. */ const toString = function() { const formatter = new agents.BDNFormatter(this.debug); return formatter.asSource(this); }; abstractions.Component.prototype.toString = toString; abstractions.Exception.prototype.toString = toString; /* * This function is used to convert most JavaScript values into their corresponding * Bali Nebula™ component values. It is needed by the Component class and depends on * everything else so it must be injected into them after everything has been imported. * Just to be safe, this function does not depend on any functions defined later * in this file, even though that should not matter. When possible circular dependencies * are involved we can't be too careful! Also, no exceptions are thrown by this function * since the Exception class calls the componentize function on its attributes and again * we want to avoid circular dependencies. */ const componentize = function(value) { if (value === null) value = undefined; // normalize to undefined var component; switch (typeof value) { case 'undefined': component = new elements.Pattern(undefined, undefined, this.debug); // none break; case 'boolean': component = new elements.Boolean(value, undefined, this.debug); break; case 'number': // NOTE: doesn't handle probabilities, they must be parsed as a string component = new elements.Number([value, undefined], undefined, this.debug); break; case 'string': try { // first try to parse it as a Bali Document Notation™ source string const parser = new agents.BDNParser(); // don't log parsing exceptions here component = parser.parseSource(value); } catch (cause) { // otherwise convert it to a text element component = new strings.Text(value, undefined, this.debug); } break; case 'object': if (Array.isArray(value)) { // convert the array to a list component = new collections.List(undefined, this.debug); value.forEach(function(item) { component.addItem(item); // item converted in addItem() }); } else if (value.isComponent) { // leave it since it is already a component component = value; } else { // convert the object to a catalog component = new collections.Catalog(undefined, this.debug); const keys = Object.keys(value); keys.forEach(function(key) { component.setAttribute(key, value[key]); // key and value are converted in setAttribute() }); } break; default: // punt, convert whatever it is to a multi-line text element component = new strings.Text('"' + EOL + value + EOL + '"', undefined, this.debug); } return component; }; abstractions.Component.prototype.componentize = componentize; abstractions.Exception.prototype.componentize = componentize; // PUBLIC INTERFACE /** * This function returns the public interace for this framework. * * An optional debug argument may be specified that controls the level of debugging that * should be applied during execution. The allowed levels are as follows: * <pre> * 0: no debugging is applied (this is the default value and has the best performance) * 1: log any exceptions to console.error before throwing them * 2: perform argument validation checks on each call (poor performance) * 3: log interesting arguments, states and results to console.log * </pre> * * Each function exposed by the interface also supports an optional debug argument as its last * argument. If specified, it will override the value specified for the entire interface. */ exports.api = function(debug) { const defaultDebug = debug || 0; // default is off // ANGLE const angle = function(value, parameters, debug) { debug = debug || defaultDebug; return new elements.Angle(value, parameters, debug); }; angle.inverse = function(angle, debug) { debug = debug || defaultDebug; return elements.Angle.inverse(angle, debug); }; angle.complement = function(angle, debug) { debug = debug || defaultDebug; return elements.Angle.complement(angle, debug); }; angle.supplement = function(angle, debug) { debug = debug || defaultDebug; return elements.Angle.supplement(angle, debug); }; angle.conjugate = function(angle, debug) { debug = debug || defaultDebug; return elements.Angle.conjugate(angle, debug); }; angle.sum = function(first, second, debug) { debug = debug || defaultDebug; return elements.Angle.sum(first, second, debug); }; angle.difference = function(first, second, debug) { debug = debug || defaultDebug; return elements.Angle.difference(first, second, debug); }; angle.scaled = function(angle, factor, debug) { debug = debug || defaultDebug; return elements.Angle.scaled(angle, factor, debug); }; angle.sine = function(angle, debug) { debug = debug || defaultDebug; return elements.Angle.sine(angle, debug); }; angle.cosine = function(angle, debug) { debug = debug || defaultDebug; return elements.Angle.cosine(angle, debug); }; angle.tangent = function(angle, debug) { debug = debug || defaultDebug; return elements.Angle.tangent(angle, debug); }; angle.arcsine = function(ratio, debug) { debug = debug || defaultDebug; return elements.Angle.arcsine(ratio, debug); }; angle.arccosine = function(ratio, debug) { debug = debug || defaultDebug; return elements.Angle.arccosine(ratio, debug); }; angle.arctangent = function(opposite, adjacent, debug) { debug = debug || defaultDebug; return elements.Angle.arctangent(opposite, adjacent, debug); }; // ARE EQUAL const areEqual = function(first, second, debug) { debug = debug || defaultDebug; const comparator = new agents.CanonicalComparator(debug); return comparator.areEqual(first, second); }; // ASSOCIATION const association = function(key, value, debug) { debug = debug || defaultDebug; return new collections.Association(key, value, debug); }; // BINARY const binary = function(value, parameters, debug) { debug = debug || defaultDebug; return new strings.Binary(value, parameters, debug); }; binary.not = function(binary, debug) { debug = debug || defaultDebug; return strings.Binary.not(binary, debug); }; binary.and = function(first, second, debug) { debug = debug || defaultDebug; return strings.Binary.and(first, second, debug); }; binary.sans = function(first, second, debug) { debug = debug || defaultDebug; return strings.Binary.sans(first, second, debug); }; binary.or = function(first, second, debug) { debug = debug || defaultDebug; return strings.Binary.or(first, second, debug); }; binary.xor = function(first, second, debug) { debug = debug || defaultDebug; return strings.Binary.xor(first, second, debug); }; binary.chain = function(first, second, debug) { debug = debug || defaultDebug; return strings.Binary.chain(first, second, debug); }; // BOOLEAN const boolean = function(value, parameters, debug) { debug = debug || defaultDebug; return new elements.Boolean(value, parameters, debug); }; boolean.not = function(boolean, debug) { debug = debug || defaultDebug; return elements.Boolean.not(boolean, debug); }; boolean.and = function(first, second, debug) { debug = debug || defaultDebug; return elements.Boolean.and(first, second, debug); }; boolean.sans = function(first, second, debug) { debug = debug || defaultDebug; return elements.Boolean.sans(first, second, debug); }; boolean.or = function(first, second, debug) { debug = debug || defaultDebug; return elements.Boolean.or(first, second, debug); }; boolean.xor = function(first, second, debug) { debug = debug || defaultDebug; return elements.Boolean.xor(first, second, debug); }; // CALCULATOR const calculator = function(debug) { debug = debug || defaultDebug; return new utilities.Calculator(debug); }; // CATALOG const catalog = function(associations, parameters, debug) { debug = debug || defaultDebug; const collection = new collections.Catalog(parameters, debug); if (associations) collection.addItems(associations); return collection; }; catalog.chain = function(first, second, debug) { debug = debug || defaultDebug; return collections.Catalog.chain(first, second, debug); }; catalog.extraction = function(catalog, keys, debug) { debug = debug || defaultDebug; return collections.Catalog.extraction(catalog, keys, debug); }; // COMPARATOR const comparator = function(debug) { debug = debug || defaultDebug; return new agents.CanonicalComparator(debug); }; // COMPONENT const component = function(bdn, debug) { debug = debug || defaultDebug; const parser = new agents.BDNParser(debug); if (bdn.slice(-1) === EOL) { return parser.parseDocument(bdn); } return parser.parseSource(bdn); }; component.canonicalType = function(component, debug) { debug = debug || defaultDebug; return abstractions.Component.canonicalType(component, debug); }; component.normalizedIndex = function(sequence, index, debug) { debug = debug || defaultDebug; return abstractions.Component.normalizedIndex(sequence, index, debug); }; component.validateArgument = function(moduleName, procedureName, argumentName, argumentValue, allowedTypes, debug) { debug = debug || defaultDebug; return abstractions.Component.validateArgument(moduleName, procedureName, argumentName, argumentValue, allowedTypes, debug); }; // CONFIGURATOR const configurator = function(filename, directory, debug) { debug = debug || defaultDebug; return new utilities.Configurator(filename, directory, debug); }; // CONTROLLER const controller = function(eventTypes, nextStates, currentState, debug) { debug = debug || defaultDebug; return new utilities.Controller(eventTypes, nextStates, currentState, debug); }; // DECODER const decoder = function(debug) { debug = debug || defaultDebug; return new utilities.Decoder(debug); }; // DOCUMENT const document = function(component, debug) { debug = debug || defaultDebug; const formatter = new agents.BDNFormatter(debug); return formatter.asDocument(component); }; // DOES MATCH const doesMatch = function(component, pattern, debug) { debug = debug || defaultDebug; const comparator = new agents.CanonicalComparator(debug); return comparator.doesMatch(component, pattern); }; // DUPLICATE const duplicate = function(component, shallow, debug) { shallow = shallow || false; debug = debug || defaultDebug; if (shallow) return new component.constructor(); const duplicator = new agents.DeepDuplicator(debug); return duplicator.duplicateComponent(component); }; // DURATION const duration = function(value, parameters, debug) { value = value || 'P0D'; // default is zero days debug = debug || defaultDebug; return new elements.Duration(value, parameters, debug); }; duration.inverse = function(duration, debug) { debug = debug || defaultDebug; return elements.Duration.inverse(duration, debug); }; duration.sum = function(first, second, debug) { debug = debug || defaultDebug; return elements.Duration.sum(first, second, debug); }; duration.difference = function(first, second, debug) { debug = debug || defaultDebug; return elements.Duration.difference(first, second, debug); }; duration.scaled = function(duration, factor, debug) { debug = debug || defaultDebug; return elements.Duration.scaled(duration, factor, debug); }; // EXCEPTION const exception = function(attributes, cause, debug) { debug = debug || defaultDebug; return new abstractions.Exception(attributes, cause, debug); }; // GENERATOR const generator = function(debug) { debug = debug || defaultDebug; return new utilities.Generator(debug); }; // HTML const html = function(component, title, style, debug) { debug = debug || defaultDebug; const formatter = new agents.HTMLFormatter(debug); return formatter.asDocument(component, title, style); }; // INSTANCE const instance = function(type, attributes, debug) { const catalog = new collections.Catalog({ $type: type, $tag: new elements.Tag(), $version: 'v1', $permissions: '/nebula/permissions/public/v1', $previous: 'none' }, debug); catalog.addItems(attributes); return catalog; }; // LIST const list = function(items, parameters, debug) { debug = debug || defaultDebug; const collection = new collections.List(parameters, debug); if (items) collection.addItems(items); return collection; }; list.chain = function(first, second, debug) { debug = debug || defaultDebug; return collections.List.chain(first, second, debug); }; // MOMENT const moment = function(value, parameters, debug) { debug = debug || defaultDebug; return new elements.Moment(value, parameters, debug); }; moment.duration = function(first, second, debug) { debug = debug || defaultDebug; return elements.Moment.duration(first, second, debug); }; moment.earlier = function(moment, duration, debug) { debug = debug || defaultDebug; return elements.Moment.earlier(moment, duration, debug); }; moment.later = function(moment, duration, debug) { debug = debug || defaultDebug; return elements.Moment.later(moment, duration, debug); }; // NAME const name = function(value, parameters, debug) { debug = debug || defaultDebug; return new strings.Name(value, parameters, debug); }; name.chain = function(first, second, debug) { debug = debug || defaultDebug; return strings.Name.chain(first, second, debug); }; // NODE const node = function(type, debug) { debug = debug || defaultDebug; const node = new trees.Node(type, debug); return node; }; // NUMBER const number = function(real, imaginary, parameters, debug) { debug = debug || defaultDebug; return new elements.Number([real, imaginary], parameters, debug); }; number.conjugate = function(number, debug) { debug = debug || defaultDebug; return elements.Number.conjugate(number, debug); }; number.difference = function(first, second, debug) { debug = debug || defaultDebug; return elements.Number.difference(first, second, debug); }; number.exponential = function(base, exponent, debug) { debug = debug || defaultDebug; return elements.Number.exponential(base, exponent, debug); }; number.factorial = function(number, debug) { debug = debug || defaultDebug; return elements.Number.factorial(number, debug); }; number.inverse = function(number, debug) { debug = debug || defaultDebug; return elements.Number.inverse(number, debug); }; number.logarithm = function(base, value, debug) { debug = debug || defaultDebug; return elements.Number.logarithm(base, value, debug); }; number.product = function(first, second, debug) { debug = debug || defaultDebug; return elements.Number.product(first, second, debug); }; number.quotient = function(first, second, debug) { debug = debug || defaultDebug; return elements.Number.quotient(first, second, debug); }; number.reciprocal = function(number, debug) { debug = debug || defaultDebug; return elements.Number.reciprocal(number, debug); }; number.remainder = function(first, second, debug) { debug = debug || defaultDebug; return elements.Number.remainder(first, second, debug); }; number.scaled = function(number, factor, debug) { debug = debug || defaultDebug; return elements.Number.scaled(number, factor, debug); }; number.sum = function(first, second, debug) { debug = debug || defaultDebug; return elements.Number.sum(first, second, debug); }; // PATTERN const pattern = function(value, parameters, debug) { debug = debug || defaultDebug; return new elements.Pattern(value, parameters, debug); }; // PERCENTAGE const percentage = function(value, parameters, debug) { debug = debug || defaultDebug; return new elements.Percentage(value, parameters, debug); }; percentage.inverse = function(percentage, debug) { debug = debug || defaultDebug; return elements.Percentage.inverse(percentage, debug); }; percentage.sum = function(first, second, debug) { debug = debug || defaultDebug; return elements.Percentage.sum(first, second, debug); }; percentage.difference = function(first, second, debug) { debug = debug || defaultDebug; return elements.Percentage.difference(first, second, debug); }; percentage.scaled = function(percentage, factor, debug) { debug = debug || defaultDebug; return elements.Percentage.scaled(percentage, factor, debug); }; // PROBABILITY const probability = function(value, parameters, debug) { debug = debug || defaultDebug; return new elements.Probability(value, parameters, debug); }; probability.random = function(debug) { debug = debug || defaultDebug; return elements.Probability.random(debug); }; probability.not = function(probability, debug) { debug = debug || defaultDebug; return elements.Probability.not(probability, debug); }; probability.and = function(first, second, debug) { debug = debug || defaultDebug; return elements.Probability.and(first, second, debug); }; probability.sans = function(first, second, debug) { debug = debug || defaultDebug; return elements.Probability.sans(first, second, debug); }; probability.or = function(first, second, debug) { debug = debug || defaultDebug; return elements.Probability.or(first, second, debug); }; probability.xor = function(first, second, debug) { debug = debug || defaultDebug; return elements.Probability.xor(first, second, debug); }; // PROCEDURE const procedure = function(code, parameters, debug) { debug = debug || defaultDebug; return new trees.Procedure(code, parameters, debug); }; // QUEUE const queue = function(items, parameters, debug) { debug = debug || defaultDebug; const collection = new collections.Queue(parameters, debug); if (items) collection.addItems(items); return collection; }; // RANGE const range = function(first, connector, last, parameters, debug) { debug = debug || defaultDebug; return new collections.Range(first, connector, last, parameters, debug); }; range.effective = function(range, debug) { debug = debug || defaultDebug; return collections.Range.effective(range, debug); }; // RANKING const ranking = function(first, second, debug) { debug = debug || defaultDebug; const comparator = new agents.CanonicalComparator(debug); return comparator.ranking(first, second); }; // RESOURCE const resource = function(value, parameters, debug) { debug = debug || defaultDebug; return new elements.Resource(value, parameters, debug); }; // SET const set = function(items, parameters, debug) { debug = debug || defaultDebug; const collection = new collections.Set(parameters, debug); if (items) collection.addItems(items); return collection; }; set.and = function(first, second, debug) { debug = debug || defaultDebug; return collections.Set.and(first, second, debug); }; set.sans = function(first, second, debug) { debug = debug || defaultDebug; return collections.Set.sans(first, second, debug); }; set.or = function(first, second, debug) { debug = debug || defaultDebug; return collections.Set.or(first, second, debug); }; set.xor = function(first, second, debug) { debug = debug || defaultDebug; return collections.Set.xor(first, second, debug); }; // SORTED const sorted = function(collection, comparator, debug) { debug = debug || defaultDebug; const sorter = new agents.MergeSorter(debug); return sorter.sortCollection(collection, comparator); }; // SORTER const sorter = function(debug) { debug = debug || defaultDebug; return new agents.MergeSorter(debug); }; // SOURCE const source = function(component, indentation, debug) { debug = debug || defaultDebug; const formatter = new agents.BDNFormatter(debug); return formatter.asSource(component, indentation); }; // STACK const stack = function(items, parameters, debug) { debug = debug || defaultDebug; const collection = new collections.Stack(parameters, debug); if (items) collection.addItems(items); return collection; }; // SYMBOL const symbol = function(value, parameters, debug) { debug = debug || defaultDebug; return new strings.Symbol(value, parameters, debug); }; symbol.chain = function(first, second, debug) { debug = debug || defaultDebug; return strings.Symbol.chain(first, second, debug); }; // TAG const tag = function(value, parameters, debug) { debug = debug || defaultDebug; return new elements.Tag(value, parameters, debug); }; // TEXT const text = function(value, parameters, debug) { debug = debug || defaultDebug; return new strings.Text(value, parameters, debug); }; text.chain = function(first, second, debug) { debug = debug || defaultDebug; return strings.Text.chain(first, second, debug); }; // VERSION const version = function(value, parameters, debug) { debug = debug || defaultDebug; return new strings.Version(value, parameters, debug); }; version.nextVersion = function(current, level, debug) { debug = debug || defaultDebug; return strings.Version.nextVersion(current, level, debug); }; version.validNext = function(current, next, debug) { debug = debug || defaultDebug; return strings.Version.validNext(current, next, debug); }; version.chain = function(first, second, debug) { debug = debug || defaultDebug; return strings.Version.chain(first, second, debug); }; /* * This section defines constants for common components */ angle.PI = component('~π', defaultDebug); angle.TAU = component('~τ', defaultDebug); angle.DEGREES = {$units: '$degrees'}; angle.RADIANS = {$units: '$radians'}; binary.BASE02 = {$encoding: '$base02'}; binary.BASE16 = {$encoding: '$base16'}; binary.BASE32 = {$encoding: '$base32'}; binary.BASE64 = {$encoding: '$base64'}; boolean.FALSE = boolean(false, undefined, defaultDebug); boolean.TRUE = boolean(true, undefined, defaultDebug); number.UNDEFINED = component('undefined', defaultDebug); number.ZERO = component('0', defaultDebug); number.ONE = component('1', defaultDebug); number.PHI = component('φ', defaultDebug); number.E = component('e', defaultDebug); number.INFINITY = component('∞', defaultDebug); number.I = component('1i', defaultDebug); pattern.ANY = component('any', defaultDebug); pattern.NONE = component('none', defaultDebug); probability.IMPOSSIBLE = probability(0, undefined, defaultDebug); probability.CERTAIN = probability(1, undefined, defaultDebug); return { // instance constructors angle: angle, areEqual: areEqual, association: association, binary: binary, boolean: boolean, calculator: calculator, catalog: catalog, comparator: comparator, component: component, controller: controller, configurator: configurator, decoder: decoder, document: document, doesMatch: doesMatch, duplicate: duplicate, duration: duration, exception: exception, generator: generator, html: html, instance: instance, list: list, moment: moment, name: name, node: node, number: number, pattern: pattern, percentage: percentage, probability: probability, procedure: procedure, queue: queue, range: range, ranking: ranking, resource: resource, set: set, sorted: sorted, sorter: sorter, source: source, stack: stack, symbol: symbol, tag: tag, text: text, version: version, // abstract classes from whence to inherit Collection: abstractions.Collection, Comparator: abstractions.Comparator, Component: abstractions.Component, Element: abstractions.Element, Iterator: abstractions.Iterator, String: abstractions.String, Sorter: abstractions.Sorter, Visitor: abstractions.Visitor }; };