xod-arduino
Advanced tools
Comparing version 0.1.2 to 0.1.3
@@ -21,2 +21,3 @@ 'use strict'; | ||
exports.default = _transpiler2.default; | ||
exports.default = _transpiler2.default; | ||
//# sourceMappingURL=index.js.map |
@@ -21,6 +21,9 @@ 'use strict'; | ||
var configTpl = '{{!-- Template for program configuration block --}}\n{{!-- Accepts the Config context --}}\n\n/*=============================================================================\n *\n *\n * Configuration\n *\n *\n =============================================================================*/\n\n#define NODE_COUNT {{ NODE_COUNT }}\n#define MAX_OUTPUT_COUNT {{ MAX_OUTPUT_COUNT }}\n\n// Uncomment to trace the program in the Serial Monitor\n{{#unless XOD_DEBUG}}//{{/unless}}#define XOD_DEBUG\n'; | ||
var patchContextTpl = '{{!-- Template for GENERATED_CODE token inside each patch implementation --}}\n{{!-- Accepts the Node context --}}\n\nstruct Storage {\n State state;\n {{#each inputs}}\n PinRef input_{{ pinKey }};\n {{/each}}\n {{#each outputs}}\n OutputPin<{{ type }}> output_{{ pinKey }};\n {{/each}}\n};\n\nenum Inputs : PinKey {\n {{#each inputs}}\n {{ pinKey }} = offsetof(Storage, input_{{ pinKey }}){{#unless @last}},{{/unless}}\n {{/each}}\n};\n\nenum Outputs : PinKey {\n {{#each outputs}}\n {{ pinKey }} = offsetof(Storage, output_{{ pinKey }}) | ({{@index}} << PIN_KEY_OFFSET_BITS){{#unless @last}},{{/unless}}\n {{/each}}\n};\n'; | ||
var implListTpl = '{{!-- Template for program graph --}}\n{{!-- Accepts the context with list of Nodes --}}\n/*=============================================================================\n *\n *\n * Native node implementations\n *\n *\n =============================================================================*/\nnamespace _program {\n {{#each this}}\n namespace {{ owner }} { namespace {{ libName }} { namespace {{ patchName }} {\n {{ implementation }}\n }}}\n {{/each}}\n}\n'; | ||
var programTpl = '{{!-- Template for program graph --}}\n{{!-- Accepts the context with list of Nodes --}}\n/*=============================================================================\n *\n *\n * Program graph\n *\n *\n =============================================================================*/\n\nnamespace _program {\n {{#each nodes}}\n {{mergePins }}\n {{#each outputs }}\n NodeId links_{{ ../id }}_{{ pinKey }}[] = { {{#each to }}{{ this }}, {{/each}}NO_NODE };\n {{/each}}\n {{ patch/owner }}::{{ patch/libName }}::{{ patch/patchName }}::Storage storage_{{ id }} = {\n { }, // state\n {{#each inputs }}\n {{#exists nodeId }}\n { NodeId({{ nodeId }}), {{ patch/owner }}::{{ patch/libName }}::{{ patch/patchName }}::Outputs::{{ fromPinKey }} }, // input_{{ pinKey }}\n {{else }}\n { NO_NODE, 0 }, // input_{{ pinKey }}\n {{/exists }}\n {{/each}}\n {{#each outputs }}\n { {{ value }}, links_{{ ../id }}_{{ pinKey }} }{{#unless @last }},{{/unless }} // output_{{ pinKey }}\n {{/each}}\n };\n {{/each}}\n\n void* storages[NODE_COUNT] = {\n {{#each nodes}}\n &storage_{{ id }}{{#unless @last }},{{/unless }}\n {{/each}}\n };\n\n EvalFuncPtr evaluationFuncs[NODE_COUNT] = {\n {{#each nodes}}\n (EvalFuncPtr)&{{ patch/owner }}::{{ patch/libName }}::{{ patch/patchName }}::evaluate{{#unless @last }},{{/unless }}\n {{/each}}\n };\n\n DirtyFlags dirtyFlags[NODE_COUNT] = {\n {{#each nodes}}DirtyFlags({{#if patch.isDirty }}-1{{ else }}0{{/if }}){{#unless @last}},\n {{/unless}}{{/each}}\n };\n\n NodeId topology[NODE_COUNT] = {\n {{#each topology}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}\n };\n\n TimeMs schedule[NODE_COUNT] = { 0 };\n}\n'; | ||
var runtime = '\n/*=============================================================================\n *\n *\n * Runtime\n *\n *\n =============================================================================*/\n\n#include <Arduino.h>\n#include <inttypes.h>\n\n//----------------------------------------------------------------------------\n// Debug routines\n//----------------------------------------------------------------------------\n#ifndef DEBUG_SERIAL\n# define DEBUG_SERIAL Serial\n#endif\n\n#ifdef XOD_DEBUG\n# define XOD_TRACE(x) DEBUG_SERIAL.print(x)\n# define XOD_TRACE_LN(x) DEBUG_SERIAL.println(x)\n# define XOD_TRACE_F(x) XOD_TRACE(F(x))\n# define XOD_TRACE_FLN(x) XOD_TRACE_LN(F(x))\n#else\n# define XOD_TRACE(x)\n# define XOD_TRACE_LN(x)\n# define XOD_TRACE_F(x)\n# define XOD_TRACE_FLN(x)\n#endif\n\n//----------------------------------------------------------------------------\n// Type definitions\n//----------------------------------------------------------------------------\n#define PIN_KEY_OFFSET_BITS (16 - MAX_OUTPUT_COUNT)\n#define NO_NODE ((NodeId)-1)\n\nnamespace _program {\n typedef double Number;\n typedef bool Logic;\n\n // OPTIMIZE: we should choose uint8_t if there are less than 255 nodes in total\n // and uint32_t if there are more than 65535\n typedef uint16_t NodeId;\n\n // OPTIMIZE: we should choose a proper type with a minimal enough capacity\n typedef uint16_t PinKey;\n\n // OPTIMIZE: we should choose a proper type with a minimal enough capacity\n typedef uint16_t DirtyFlags;\n\n typedef unsigned long TimeMs;\n typedef void (*EvalFuncPtr)(NodeId nid, void* state);\n}\n\n//----------------------------------------------------------------------------\n// Engine\n//----------------------------------------------------------------------------\nnamespace _program {\n extern void* storages[NODE_COUNT];\n extern EvalFuncPtr evaluationFuncs[NODE_COUNT];\n extern DirtyFlags dirtyFlags[NODE_COUNT];\n extern NodeId topology[NODE_COUNT];\n\n template<typename T>\n struct OutputPin {\n T value;\n // Keep outgoing link list with terminating `NO_NODE`\n NodeId* links;\n };\n\n struct PinRef {\n NodeId nodeId;\n PinKey pinKey;\n };\n\n\n // TODO: replace with a compact list\n extern TimeMs schedule[NODE_COUNT];\n\n inline void* pinPtr(void* storage, PinKey key) {\n const size_t offset = key & ~(PinKey(-1) << PIN_KEY_OFFSET_BITS);\n return (uint8_t*)storage + offset;\n }\n\n inline DirtyFlags dirtyPinBit(PinKey key) {\n const PinKey nbit = (key >> PIN_KEY_OFFSET_BITS) + 1;\n return 1 << nbit;\n }\n\n inline bool isOutputDirty(NodeId nid, PinKey key) {\n return dirtyFlags[nid] & dirtyPinBit(key);\n }\n\n inline bool isInputDirty(NodeId nid, PinKey key) {\n PinRef* ref = (PinRef*)pinPtr(storages[nid], key);\n if (ref->nodeId == NO_NODE)\n return false;\n\n return isOutputDirty(ref->nodeId, ref->pinKey);\n }\n\n inline void markPinDirty(NodeId nid, PinKey key) {\n dirtyFlags[nid] |= dirtyPinBit(key);\n }\n\n inline void markNodeDirty(NodeId nid) {\n dirtyFlags[nid] |= 0x1;\n }\n\n inline bool isNodeDirty(NodeId nid) {\n return dirtyFlags[nid] & 0x1;\n }\n\n TimeMs transactionTime() {\n return millis();\n }\n\n void setTimeout(NodeId nid, TimeMs timeout) {\n schedule[nid] = transactionTime() + timeout;\n }\n\n void clearTimeout(NodeId nid) {\n schedule[nid] = 0;\n }\n\n template<typename T>\n T getValue(NodeId nid, PinKey key) {\n PinRef* ref = (PinRef*)pinPtr(storages[nid], key);\n if (ref->nodeId == NO_NODE)\n return (T)0;\n\n return *(T*)pinPtr(storages[ref->nodeId], ref->pinKey);\n }\n\n Number getNumber(NodeId nid, PinKey key) {\n return getValue<Number>(nid, key);\n }\n\n Logic getLogic(NodeId nid, PinKey key) {\n return getValue<Logic>(nid, key);\n }\n\n template<typename T>\n void emitValue(NodeId nid, PinKey key, T value) {\n OutputPin<T>* outputPin = (OutputPin<T>*)pinPtr(storages[nid], key);\n\n outputPin->value = value;\n markPinDirty(nid, key);\n\n NodeId* linkedNode = outputPin->links;\n while (*linkedNode != NO_NODE) {\n markNodeDirty(*linkedNode++);\n }\n }\n\n void emitNumber(NodeId nid, PinKey key, Number value) {\n emitValue<Number>(nid, key, value);\n }\n\n void emitLogic(NodeId nid, PinKey key, Logic value) {\n emitValue<Logic>(nid, key, value);\n }\n\n template<typename T>\n void reemitValue(NodeId nid, PinKey key) {\n OutputPin<T>* outputPin = (OutputPin<T>*)pinPtr(storages[nid], key);\n emitValue<T>(nid, key, outputPin->value);\n }\n\n void reemitNumber(NodeId nid, PinKey key) {\n reemitValue<Number>(nid, key);\n }\n\n void reemitLogic(NodeId nid, PinKey key) {\n reemitValue<Logic>(nid, key);\n }\n\n void evaluateNode(NodeId nid) {\n XOD_TRACE_F("eval #");\n XOD_TRACE_LN(nid);\n EvalFuncPtr eval = evaluationFuncs[nid];\n eval(nid, storages[nid]);\n }\n\n void runTransaction() {\n XOD_TRACE_FLN("Transaction started");\n for (NodeId nid : topology) {\n if (isNodeDirty(nid))\n evaluateNode(nid);\n }\n\n memset(dirtyFlags, 0, sizeof(dirtyFlags));\n XOD_TRACE_FLN("Transaction completed");\n }\n\n void idle() {\n TimeMs now = millis();\n for (NodeId nid = 0; nid < NODE_COUNT; ++nid) {\n TimeMs t = schedule[nid];\n if (t && t <= now) {\n markNodeDirty(nid);\n clearTimeout(nid);\n return;\n }\n }\n }\n}\n\n//----------------------------------------------------------------------------\n// Entry point\n//----------------------------------------------------------------------------\nvoid setup() {\n // FIXME: looks like there is a rounding bug. Waiting for 1 second fights it\n delay(1000);\n#ifdef XOD_DEBUG\n DEBUG_SERIAL.begin(9600);\n#endif\n XOD_TRACE_FLN("Program started");\n\n XOD_TRACE_F("NODE_COUNT = ");\n XOD_TRACE_LN(NODE_COUNT);\n\n XOD_TRACE_F("sizeof(NodeId) = ");\n XOD_TRACE_LN(sizeof(NodeId));\n\n XOD_TRACE_F("sizeof(PinKey) = ");\n XOD_TRACE_LN(sizeof(PinKey));\n\n XOD_TRACE_F("sizeof(DirtyFlags) = ");\n XOD_TRACE_LN(sizeof(DirtyFlags));\n}\n\nvoid loop() {\n _program::idle();\n _program::runTransaction();\n}\n'; | ||
var patchContextTpl = '{{!-- Template for GENERATED_CODE token inside each patch implementation --}}\n{{!-- Accepts the Node context --}}\n\nstruct Storage {\n State state;\n {{#each inputs}}\n PinRef input_{{ pinKey }};\n {{/each}}\n {{#each outputs}}\n OutputPin<{{ type }}> output_{{ pinKey }};\n {{/each}}\n};\n\nnamespace Inputs {\n {{#each inputs}}\n using {{ pinKey }} = InputDescriptor<{{ type }}, offsetof(Storage, input_{{ pinKey }})>;\n {{/each}}\n}\n\nnamespace Outputs {\n {{#each outputs}}\n using {{ pinKey }} = OutputDescriptor<{{ type }}, offsetof(Storage, output_{{ pinKey }}), {{@index}}>;\n {{/each}}\n}\n'; | ||
var implListTpl = '{{!-- Template for program graph --}}\n{{!-- Accepts the context with list of Nodes --}}\n/*=============================================================================\n *\n *\n * Native node implementations\n *\n *\n =============================================================================*/\n\nnamespace _program {\n\n{{#each this}}\n//-----------------------------------------------------------------------------\n// {{ owner }}/{{ libName }}/{{ patchName }} implementation\n//-----------------------------------------------------------------------------\nnamespace {{ owner }}__{{ libName }}__{{ patchName }} {\n\n{{ implementation }}\n\n} // namespace {{ owner }}__{{ libName }}__{{ patchName }}\n\n{{/each}}\n} // namespace _program\n'; | ||
var programTpl = '{{!-- Template for program graph --}}\n{{!-- Accepts the context with list of Nodes --}}\n/*=============================================================================\n *\n *\n * Program graph\n *\n *\n =============================================================================*/\n\nnamespace _program {\n {{#each nodes}}\n {{mergePins }}\n {{#each outputs }}\n NodeId links_{{ ../id }}_{{ pinKey }}[] = { {{#each to }}{{ this }}, {{/each}}NO_NODE };\n {{/each}}\n {{ patch/owner }}__{{ patch/libName }}__{{ patch/patchName }}::Storage storage_{{ id }} = {\n { }, // state\n {{#each inputs }}\n {{#exists nodeId }}\n { NodeId({{ nodeId }}), {{ patch/owner }}__{{ patch/libName }}__{{ patch/patchName }}::Outputs::{{ fromPinKey }}::KEY }, // input_{{ pinKey }}\n {{else }}\n { NO_NODE, 0 }, // input_{{ pinKey }}\n {{/exists }}\n {{/each}}\n {{#each outputs }}\n { {{ value }}, links_{{ ../id }}_{{ pinKey }} }{{#unless @last }},{{/unless }} // output_{{ pinKey }}\n {{/each}}\n };\n {{/each}}\n\n void* storages[NODE_COUNT] = {\n {{#each nodes}}\n &storage_{{ id }}{{#unless @last }},{{/unless }}\n {{/each}}\n };\n\n EvalFuncPtr evaluationFuncs[NODE_COUNT] = {\n {{#each nodes}}\n (EvalFuncPtr)&{{ patch/owner }}__{{ patch/libName }}__{{ patch/patchName }}::evaluate{{#unless @last }},{{/unless }}\n {{/each}}\n };\n\n DirtyFlags dirtyFlags[NODE_COUNT] = {\n {{#each nodes}}DirtyFlags({{#if patch.isDirty }}-1{{ else }}0{{/if }}){{#unless @last}},\n {{/unless}}{{/each}}\n };\n\n NodeId topology[NODE_COUNT] = {\n {{#each topology}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}\n };\n\n TimeMs schedule[NODE_COUNT] = { 0 };\n}\n'; | ||
var preambleH = '// The sketch is auto-generated with XOD (https://xod.io).\n//\n// You can compile and upload it to an Arduino-compatible board with\n// Arduino IDE.\n//\n// Rough code overview:\n//\n// - Intrusive pointer (a smart pointer with ref counter)\n// - Immutable dynamic list data structure\n// - XOD runtime environment\n// - Native node implementation\n// - Program graph definition\n//\n// Search for comments fenced with \'====\' and \'----\' to navigate through\n// the major code blocks.\n\n#include <Arduino.h>\n#include <inttypes.h>\n\n'; | ||
var intrusivePtrH = '// ============================================================================\n//\n// Intrusive pointer\n//\n// ============================================================================\n\n// This is a stripped down version of Boost v1.63 intrusive pointer.\n//\n// Copyright (c) 2001, 2002 Peter Dimov\n//\n// Distributed under the Boost Software License, Version 1.0. (See\n// accompanying file LICENSE_1_0.txt or copy at\n// http://www.boost.org/LICENSE_1_0.txt)\n//\n// See http://www.boost.org/libs/smart_ptr/intrusive_ptr.html for\n// documentation.\n\n#ifndef XOD_INTRUSIVE_PTR_H\n#define XOD_INTRUSIVE_PTR_H\n\nnamespace boost {\n//\n// intrusive_ptr\n//\n// A smart pointer that uses intrusive reference counting.\n//\n// Relies on unqualified calls to\n//\n// void intrusive_ptr_add_ref(T * p);\n// void intrusive_ptr_release(T * p);\n//\n// (p != 0)\n//\n// The object is responsible for destroying itself.\n//\n\ntemplate <class T> class intrusive_ptr {\n private:\n typedef intrusive_ptr this_type;\n\n public:\n typedef T element_type;\n\n constexpr intrusive_ptr() : px(0) {}\n\n intrusive_ptr(T *p, bool add_ref = true) : px(p) {\n if (px != 0 && add_ref)\n intrusive_ptr_add_ref(px);\n }\n\n template <class U>\n intrusive_ptr(intrusive_ptr<U> const &rhs) : px(rhs.get()) {\n if (px != 0)\n intrusive_ptr_add_ref(px);\n }\n\n intrusive_ptr(intrusive_ptr const &rhs) : px(rhs.px) {\n if (px != 0)\n intrusive_ptr_add_ref(px);\n }\n\n ~intrusive_ptr() {\n if (px != 0)\n intrusive_ptr_release(px);\n }\n\n template <class U> intrusive_ptr &operator=(intrusive_ptr<U> const &rhs) {\n this_type(rhs).swap(*this);\n return *this;\n }\n\n intrusive_ptr(intrusive_ptr &&rhs) : px(rhs.px) { rhs.px = 0; }\n\n intrusive_ptr &operator=(intrusive_ptr &&rhs) {\n this_type(static_cast<intrusive_ptr &&>(rhs)).swap(*this);\n return *this;\n }\n\n template <class U> friend class intrusive_ptr;\n\n template <class U> intrusive_ptr(intrusive_ptr<U> &&rhs) : px(rhs.px) {\n rhs.px = 0;\n }\n\n template <class U> intrusive_ptr &operator=(intrusive_ptr<U> &&rhs) {\n this_type(static_cast<intrusive_ptr<U> &&>(rhs)).swap(*this);\n return *this;\n }\n\n intrusive_ptr &operator=(intrusive_ptr const &rhs) {\n this_type(rhs).swap(*this);\n return *this;\n }\n\n intrusive_ptr &operator=(T *rhs) {\n this_type(rhs).swap(*this);\n return *this;\n }\n\n void reset() { this_type().swap(*this); }\n\n void reset(T *rhs) { this_type(rhs).swap(*this); }\n\n void reset(T *rhs, bool add_ref) { this_type(rhs, add_ref).swap(*this); }\n\n T *get() const { return px; }\n\n T *detach() {\n T *ret = px;\n px = 0;\n return ret;\n }\n\n T &operator*() const { return *px; }\n\n T *operator->() const { return px; }\n\n operator bool() const { return px != 0; }\n\n void swap(intrusive_ptr &rhs) {\n T *tmp = px;\n px = rhs.px;\n rhs.px = tmp;\n }\n\n private:\n T *px;\n};\n\ntemplate <class T, class U>\ninline bool operator==(intrusive_ptr<T> const &a, intrusive_ptr<U> const &b) {\n return a.get() == b.get();\n}\n\ntemplate <class T, class U>\ninline bool operator!=(intrusive_ptr<T> const &a, intrusive_ptr<U> const &b) {\n return a.get() != b.get();\n}\n\ntemplate <class T, class U>\ninline bool operator==(intrusive_ptr<T> const &a, U *b) {\n return a.get() == b;\n}\n\ntemplate <class T, class U>\ninline bool operator!=(intrusive_ptr<T> const &a, U *b) {\n return a.get() != b;\n}\n\ntemplate <class T, class U>\ninline bool operator==(T *a, intrusive_ptr<U> const &b) {\n return a == b.get();\n}\n\ntemplate <class T, class U>\ninline bool operator!=(T *a, intrusive_ptr<U> const &b) {\n return a != b.get();\n}\n\n#if __GNUC__ == 2 && __GNUC_MINOR__ <= 96\n\n// Resolve the ambiguity between our op!= and the one in rel_ops\n\ntemplate <class T>\ninline bool operator!=(intrusive_ptr<T> const &a, intrusive_ptr<T> const &b) {\n return a.get() != b.get();\n}\n\n#endif\n\ntemplate <class T>\ninline bool operator==(intrusive_ptr<T> const &p, nullptr_t) {\n return p.get() == 0;\n}\n\ntemplate <class T>\ninline bool operator==(nullptr_t, intrusive_ptr<T> const &p) {\n return p.get() == 0;\n}\n\ntemplate <class T>\ninline bool operator!=(intrusive_ptr<T> const &p, nullptr_t) {\n return p.get() != 0;\n}\n\ntemplate <class T>\ninline bool operator!=(nullptr_t, intrusive_ptr<T> const &p) {\n return p.get() != 0;\n}\n\ntemplate <class T>\ninline bool operator<(intrusive_ptr<T> const &a, intrusive_ptr<T> const &b) {\n return a.get() < b.get();\n}\n\ntemplate <class T> void swap(intrusive_ptr<T> &lhs, intrusive_ptr<T> &rhs) {\n lhs.swap(rhs);\n}\n\n} // namespace boost\n\n#endif // #ifndef XOD_INTRUSIVE_PTR_H\n'; | ||
var listH = '// ============================================================================\n//\n// Immutable dynamic list\n//\n// ============================================================================\n\n#ifndef XOD_LIST_H\n#define XOD_LIST_H\n\n#ifndef XOD_LIST_CHUNK_SIZE\n#define XOD_LIST_CHUNK_SIZE 15\n#endif\n\nnamespace xod {\n// forward declaration\ntemplate <typename T> class List;\n}\n\nnamespace xod {\nnamespace detail {\n\n#if XOD_LIST_CHUNK_SIZE < 256\ntypedef uint8_t index_t;\n#else\ntypedef size_t index_t;\n#endif\n\ntypedef uint8_t refcount_t;\ntypedef uint8_t depth_t;\n\n/*\n * Bounds define a used range of data within Chunk\u2019s ring buffer. `first` is\n * an index (not byte offset) of the first element in range. `last` is an\n * index (not byte offset) of the last filled element. I.e. `last` points to\n * an existing element, *not* a slot past end.\n *\n * Value of `first` can be greater than `last`. It means that the range is\n * wrapped arround buffer\u2019s origin.\n *\n * Examples:\n *\n * - `first == 0 && last == 0`: chunk have 1 element and it is at buffer[0]\n * - `first == 0 && last == 15`: chunk have 16 elements spanning from buffer[0]\n * to buffer[15] inclusive\n * - `first == 14 && last == 2`: given the chunk size == 16 it has 5 elements:\n * buffer[14], buffer[15], buffer[0], buffer[1], buffer[2].\n */\nstruct Bounds {\n#if XOD_LIST_CHUNK_SIZE < 16\n index_t first : 4;\n index_t last : 4;\n#else\n index_t first;\n index_t last;\n#endif\n};\n\ntemplate <typename T> struct Traits {\n enum { N = XOD_LIST_CHUNK_SIZE / sizeof(T) };\n};\n\n/*\n * Ring buffer\n */\nstruct Chunk {\n char buffer[XOD_LIST_CHUNK_SIZE];\n Bounds bounds;\n refcount_t _refcount;\n\n Chunk() { memset(this, 0, sizeof(Chunk)); }\n\n /*\n * Returns number of elements occupied\n */\n template <typename T> index_t usage() {\n return (bounds.last - bounds.first + Traits<T>::N) % Traits<T>::N + 1;\n }\n\n template <typename T> bool isFull() { return usage<T>() == Traits<T>::N; }\n\n template <typename T> bool append(T val) {\n if (isFull<T>())\n return false;\n\n appendUnsafe(val);\n return true;\n }\n\n template <typename T> void appendUnsafe(T val) {\n auto idx = ++bounds.last;\n *((T *)buffer + idx) = val;\n }\n\n template <typename T> bool concat(T *val, index_t len) {\n if (usage<T>() > Traits<T>::N - len)\n return false;\n\n while (len--)\n appendUnsafe(*val++);\n\n return true;\n }\n};\n\nvoid intrusive_ptr_add_ref(Chunk *chunk) {\n // TODO: deal with possible overflow\n ++chunk->_refcount;\n}\n\nvoid intrusive_ptr_release(Chunk *chunk) {\n if (--chunk->_refcount == 0) {\n delete chunk;\n }\n}\n\ntemplate <typename T> class ListIterator {\n typedef List<T> ListT;\n typedef const ListT *ListRawPtr;\n\n public:\n ListIterator(ListRawPtr root) {\n _stackSize = 0;\n if (root->isEmpty()) {\n _stack = 0;\n } else {\n _stack = new ListRawPtr[root->maxDepth()];\n push(root);\n drillDownToLeftmost();\n }\n }\n\n ~ListIterator() {\n if (_stack)\n delete[] _stack;\n }\n\n /*\n * Returns false if iteration is done\n */\n operator bool() const { return _stackSize > 0; }\n\n const T &operator*() const { return chunk()->buffer[_indexInChunk]; }\n\n ListIterator &operator++() {\n if (!_stackSize)\n return *this;\n\n ++_indexInChunk;\n\n if (_indexInChunk > top()->_rightBounds.last) {\n // we\u2019ve runned over whole chunk, move to next one\n while (true) {\n auto branch = pop();\n\n if (!_stackSize)\n break;\n\n auto parent = top();\n if (parent->_left == branch) {\n // switch to right branch if we just completed with left one\n push(parent->_right.get());\n drillDownToLeftmost();\n break;\n }\n }\n }\n\n return *this;\n }\n\n private:\n ListRawPtr top() const { return _stack[_stackSize - 1]; }\n\n void push(ListRawPtr list) { _stack[_stackSize++] = list; }\n\n ListRawPtr pop() { return _stack[_stackSize-- - 1]; }\n\n void drillDownToLeftmost() {\n ListRawPtr left;\n while ((left = top()->_left.get()))\n push(left);\n _indexInChunk = top()->_rightBounds.first;\n }\n\n Chunk *chunk() const { return top()->_chunk.get(); }\n\n private:\n ListRawPtr *_stack;\n depth_t _stackSize;\n index_t _indexInChunk;\n};\n}\n} // namespace xod::detail\n\nnamespace xod {\n\ntemplate <typename T> void intrusive_ptr_add_ref(List<T> *list) {\n // TODO: deal with possible overflow\n ++list->_refcount;\n}\n\ntemplate <typename T> void intrusive_ptr_release(List<T> *list) {\n if (--list->_refcount == 0) {\n delete list;\n }\n}\n\ntemplate <typename T> class List {\n typedef boost::intrusive_ptr<detail::Chunk> ChunkPtr;\n\n public:\n typedef boost::intrusive_ptr<List> ListPtr;\n typedef detail::ListIterator<T> Iterator;\n\n static ListPtr empty() { return ListPtr(new List()); }\n\n static ListPtr of(T val) {\n auto list = empty();\n auto chunk = new detail::Chunk();\n chunk->buffer[0] = val;\n list->_chunk = chunk;\n return list;\n }\n\n static ListPtr fromPlainArray(const T *buf, size_t len) {\n auto list = empty();\n if (!len)\n return list;\n\n if (len <= detail::Traits<T>::N) {\n // whole buf can be contained within a single chunk\n auto chunk = new detail::Chunk();\n memcpy(chunk->buffer, buf, len);\n list->_chunk = chunk;\n list->_rightBounds.last = chunk->bounds.last = len - 1;\n } else {\n // split the buffer into two portions\n auto leftLen = len / 2;\n list->_left = fromPlainArray(buf, leftLen);\n list->_right = fromPlainArray(buf + leftLen, len - leftLen);\n }\n\n return list;\n }\n\n bool isEmpty() const { return length() == 0; }\n\n size_t length() const {\n if (_left == nullptr && _right == nullptr) {\n return 0;\n } else if (chunk()) {\n return _rightBounds.last - _rightBounds.first + 1;\n } else {\n return _left->length() + _right->length();\n }\n }\n\n size_t chunkCount() const {\n if (_left) {\n return _left->chunkCount() + _right->chunkCount();\n } else if (_chunk) {\n return 1;\n } else {\n return 0;\n }\n }\n\n detail::depth_t maxDepth() const {\n if (_left) {\n auto leftDepth = _left->maxDepth();\n auto rightDepth = _right->maxDepth();\n return 1 + (leftDepth > rightDepth ? leftDepth : rightDepth);\n } else {\n return 1;\n }\n }\n\n ListPtr append(T val) const {\n if (length() == 0) {\n return of(val);\n }\n\n auto chunk = this->chunk();\n\n if (chunk && isChunkTailFree()) {\n bool amend = chunk->append(val);\n if (amend) {\n auto list = empty();\n list->_chunk = chunk;\n list->_rightBounds.last = _rightBounds.last + 1;\n return list;\n }\n }\n\n auto list = empty();\n list->_left = const_cast<List *>(this);\n list->_right = of(val);\n return list;\n }\n\n ListPtr concat(ListPtr other) const {\n if (isEmpty()) {\n return other;\n }\n\n if (other->isEmpty()) {\n return ListPtr(const_cast<List *>(this));\n }\n\n auto thisChunk = this->chunk();\n auto otherChunk = other->chunk();\n auto otherLen = other->length();\n if (thisChunk && isChunkTailFree() && otherChunk) {\n bool amend = thisChunk->concat(otherChunk->buffer, otherLen);\n if (amend) {\n auto list = empty();\n list->_chunk = thisChunk;\n list->_rightBounds.first = _rightBounds.first;\n list->_rightBounds.last = thisChunk->bounds.last;\n return list;\n }\n }\n\n auto list = empty();\n list->_left = const_cast<List *>(this);\n list->_right = other;\n return list;\n }\n\n void toPlainArrayUnsafe(T *buf) const {\n auto chunk = this->chunk();\n if (chunk) {\n memcpy(buf, chunk->buffer, length() * sizeof(T));\n } else if (_left) {\n _left->toPlainArrayUnsafe(buf);\n _right->toPlainArrayUnsafe(buf + _left->length());\n }\n }\n\n Iterator iterate() const { return Iterator(this); }\n\n protected:\n ChunkPtr chunk() const {\n if (_left == nullptr) {\n return _chunk;\n } else {\n return nullptr;\n }\n }\n\n bool isChunkTailFree() const {\n return _chunk->bounds.last == _rightBounds.last;\n }\n\n private:\n List() { memset(this, 0, sizeof(List)); }\n\n ~List() {\n // _right branch is in union with _chunk. Call a proper destructor\n // explicitly\n if (_left) {\n _right.~ListPtr();\n } else {\n _chunk.~ChunkPtr();\n }\n }\n\n friend Iterator;\n\n // There are two possible conditions of a list. It either:\n //\n // - concatenation of two children, in that case `_left` and `_right` point\n // to branches\n // - a leaf with data, in that case `_left == nullptr` and `_chunk` contains\n // pointer to the data\n //\n // Use union to save one pointer size, consider `_left` nullness to\n // understand the condition.\n ListPtr _left;\n union {\n ListPtr _right;\n ChunkPtr _chunk;\n };\n\n // Branch bounds inside chunks. In case if this is a leaf, only _rightBounds\n // is used.\n //\n // Note that the bounds will not match bounds in chunks themselves. It\u2019s\n // because the chunks are reused across many List\u2019s.\n detail::Bounds _leftBounds;\n detail::Bounds _rightBounds;\n\n friend void intrusive_ptr_add_ref<T>(List *list);\n friend void intrusive_ptr_release<T>(List *list);\n detail::refcount_t _refcount;\n}; // class List<T>\n\n} // namespace xod\n\n#endif // #ifndef XOD_LIST_H\n'; | ||
var runtimeCpp = '\n/*=============================================================================\n *\n *\n * Runtime\n *\n *\n =============================================================================*/\n\n//----------------------------------------------------------------------------\n// Debug routines\n//----------------------------------------------------------------------------\n#ifndef DEBUG_SERIAL\n# define DEBUG_SERIAL Serial\n#endif\n\n#ifdef XOD_DEBUG\n# define XOD_TRACE(x) { DEBUG_SERIAL.print(x); DEBUG_SERIAL.flush(); }\n# define XOD_TRACE_LN(x) { DEBUG_SERIAL.println(x); DEBUG_SERIAL.flush(); }\n# define XOD_TRACE_F(x) XOD_TRACE(F(x))\n# define XOD_TRACE_FLN(x) XOD_TRACE_LN(F(x))\n#else\n# define XOD_TRACE(x)\n# define XOD_TRACE_LN(x)\n# define XOD_TRACE_F(x)\n# define XOD_TRACE_FLN(x)\n#endif\n\n//----------------------------------------------------------------------------\n// Type definitions\n//----------------------------------------------------------------------------\n#define PIN_KEY_OFFSET_BITS (16 - MAX_OUTPUT_COUNT)\n#define NO_NODE ((NodeId)-1)\n\nnamespace _program {\n typedef double Number;\n typedef bool Logic;\n\n // TODO: optimize, we should choose uint8_t if there are less than 255 nodes in total\n // and uint32_t if there are more than 65535\n typedef uint16_t NodeId;\n\n /*\n * PinKey is an address value used to find input\u2019s or output\u2019s data within\n * node\u2019s Storage.\n *\n * For inputs its value is simply an offset in bytes from the beginning of\n * Storage structure instance. There will be a PinRef pointing to an upstream\n * output at this address.\n *\n * For outputs the pin key consists of two parts ORed bitwise. Least\n * significant bits (count defined by `PIN_KEY_OFFSET_BITS`) define an offset\n * from the beginning of node\u2019s Storage where output data could be found. It\n * would be an OutputPin structure. Most significant bits define an index\n * number of that output among all outputs of the node. The index is used to\n * work with dirty flags bit-value.\n */\n // TODO: optimize, we should choose a proper type with a minimal enough capacity\n typedef uint16_t PinKey;\n\n // TODO: optimize, we should choose a proper type with a minimal enough capacity\n typedef uint16_t DirtyFlags;\n\n typedef unsigned long TimeMs;\n typedef void (*EvalFuncPtr)(NodeId nid, void* state);\n\n typedef xod::List<char>::ListPtr XString;\n}\n\n//----------------------------------------------------------------------------\n// Engine\n//----------------------------------------------------------------------------\nnamespace _program {\n extern void* storages[NODE_COUNT];\n extern EvalFuncPtr evaluationFuncs[NODE_COUNT];\n extern DirtyFlags dirtyFlags[NODE_COUNT];\n extern NodeId topology[NODE_COUNT];\n\n // TODO: replace with a compact list\n extern TimeMs schedule[NODE_COUNT];\n\n template<typename T>\n struct OutputPin {\n T value;\n // Keep outgoing link list with terminating `NO_NODE`\n NodeId* links;\n };\n\n struct PinRef {\n NodeId nodeId;\n PinKey pinKey;\n };\n\n /*\n * Input descriptor is a metaprogramming structure used to enforce an\n * input\u2019s type and store its PinKey as a zero-memory constant.\n *\n * A specialized descriptor is required by `getValue` function. Every\n * input of every type node gets its own descriptor in generated code that\n * can be accessed as Inputs::FOO. Where FOO is a pin identifier.\n */\n template<typename ValueT_, size_t offsetInStorage>\n struct InputDescriptor {\n typedef ValueT_ ValueT;\n enum Offset : PinKey {\n KEY = offsetInStorage\n };\n };\n\n /*\n * Output descriptor serve the same purpose as InputDescriptor but for\n * ouputs.\n */\n template<typename ValueT_, size_t offsetInStorage, int index>\n struct OutputDescriptor {\n typedef ValueT_ ValueT;\n enum Offset : PinKey {\n KEY = offsetInStorage | (index << PIN_KEY_OFFSET_BITS)\n };\n };\n\n void* pinPtr(void* storage, PinKey key) {\n const size_t offset = key & ~(PinKey(-1) << PIN_KEY_OFFSET_BITS);\n return (uint8_t*)storage + offset;\n }\n\n DirtyFlags dirtyPinBit(PinKey key) {\n const PinKey nbit = (key >> PIN_KEY_OFFSET_BITS) + 1;\n return 1 << nbit;\n }\n\n bool isOutputDirty(NodeId nid, PinKey key) {\n return dirtyFlags[nid] & dirtyPinBit(key);\n }\n\n bool isInputDirtyImpl(NodeId nid, PinKey key) {\n PinRef* ref = (PinRef*)pinPtr(storages[nid], key);\n if (ref->nodeId == NO_NODE)\n return false;\n\n return isOutputDirty(ref->nodeId, ref->pinKey);\n }\n\n template<typename InputT>\n bool isInputDirty(NodeId nid) {\n return isInputDirtyImpl(nid, InputT::KEY);\n }\n\n void markPinDirty(NodeId nid, PinKey key) {\n dirtyFlags[nid] |= dirtyPinBit(key);\n }\n\n void markNodeDirty(NodeId nid) {\n dirtyFlags[nid] |= 0x1;\n }\n\n bool isNodeDirty(NodeId nid) {\n return dirtyFlags[nid] & 0x1;\n }\n\n TimeMs transactionTime() {\n return millis();\n }\n\n void setTimeout(NodeId nid, TimeMs timeout) {\n schedule[nid] = transactionTime() + timeout;\n }\n\n void clearTimeout(NodeId nid) {\n schedule[nid] = 0;\n }\n\n template<typename T>\n T getValueImpl(NodeId nid, PinKey key) {\n PinRef* ref = (PinRef*)pinPtr(storages[nid], key);\n if (ref->nodeId == NO_NODE)\n return (T)0;\n\n return *(T*)pinPtr(storages[ref->nodeId], ref->pinKey);\n }\n\n template<typename InputT>\n typename InputT::ValueT getValue(NodeId nid) {\n return getValueImpl<typename InputT::ValueT>(nid, InputT::KEY);\n }\n\n template<typename T>\n void emitValueImpl(NodeId nid, PinKey key, T value) {\n OutputPin<T>* outputPin = (OutputPin<T>*)pinPtr(storages[nid], key);\n\n outputPin->value = value;\n markPinDirty(nid, key);\n\n NodeId* linkedNode = outputPin->links;\n while (*linkedNode != NO_NODE) {\n markNodeDirty(*linkedNode++);\n }\n }\n\n template<typename OutputT>\n void emitValue(NodeId nid, typename OutputT::ValueT value) {\n emitValueImpl(nid, OutputT::KEY, value);\n }\n\n template<typename T>\n void reemitValueImpl(NodeId nid, PinKey key) {\n OutputPin<T>* outputPin = (OutputPin<T>*)pinPtr(storages[nid], key);\n emitValueImpl<T>(nid, key, outputPin->value);\n }\n\n template<typename OutputT>\n void reemitValue(NodeId nid) {\n reemitValueImpl<typename OutputT::ValueT>(nid, OutputT::KEY);\n }\n\n void evaluateNode(NodeId nid) {\n XOD_TRACE_F("eval #");\n XOD_TRACE_LN(nid);\n EvalFuncPtr eval = evaluationFuncs[nid];\n eval(nid, storages[nid]);\n }\n\n void runTransaction() {\n XOD_TRACE_F("Transaction started, t=");\n XOD_TRACE_LN(millis());\n for (NodeId nid : topology) {\n if (isNodeDirty(nid))\n evaluateNode(nid);\n }\n\n memset(dirtyFlags, 0, sizeof(dirtyFlags));\n XOD_TRACE_F("Transaction completed, t=");\n XOD_TRACE_LN(millis());\n }\n\n void idle() {\n TimeMs now = millis();\n for (NodeId nid = 0; nid < NODE_COUNT; ++nid) {\n TimeMs t = schedule[nid];\n if (t && t <= now) {\n markNodeDirty(nid);\n clearTimeout(nid);\n return;\n }\n }\n }\n}\n\n//----------------------------------------------------------------------------\n// Entry point\n//----------------------------------------------------------------------------\nvoid setup() {\n // FIXME: looks like there is a rounding bug. Waiting for 1 second fights it\n delay(1000);\n#ifdef XOD_DEBUG\n DEBUG_SERIAL.begin(9600);\n#endif\n XOD_TRACE_FLN("Program started");\n}\n\nvoid loop() {\n _program::idle();\n _program::runTransaction();\n}\n'; | ||
@@ -33,2 +36,3 @@ // ============================================================================= | ||
var trimTrailingWhitespace = _ramda2.default.replace(/\s+$/gm, '\n'); | ||
var indexByPinKey = _ramda2.default.indexBy(_ramda2.default.prop('pinKey')); | ||
@@ -54,2 +58,6 @@ var getPatchPins = function getPatchPins(direction) { | ||
}); | ||
// Temporary switch to global C++ namespace | ||
_handlebars2.default.registerHelper('global', function global(options) { | ||
return ['// --- Enter global namespace ---', '}}', options.fn(this), 'namespace _program {', 'namespace ' + this.owner + '__' + this.libName + '__' + this.patchName + ' {', '// --- Back to local namespace ---'].join('\n'); | ||
}); | ||
@@ -82,7 +90,13 @@ // ============================================================================= | ||
var renderImpl = exports.renderImpl = (0, _types.def)('renderImpl :: TPatch -> String', function (data) { | ||
var ctx = _ramda2.default.applySpec({ | ||
owner: _ramda2.default.prop('owner'), | ||
libName: _ramda2.default.prop('libName'), | ||
patchName: _ramda2.default.prop('patchName'), | ||
GENERATED_CODE: renderPatchContext | ||
})(data); | ||
var patchImpl = _ramda2.default.prop('impl', data); | ||
var patchContext = renderPatchContext(data); | ||
return _handlebars2.default.compile(patchImpl, renderingOptions)({ GENERATED_CODE: patchContext }); | ||
return _handlebars2.default.compile(patchImpl, renderingOptions)(ctx); | ||
}); | ||
var renderImplList = exports.renderImplList = (0, _types.def)('renderImplList :: [TPatch] -> String', _ramda2.default.compose(templates.implList, _ramda2.default.map(_ramda2.default.applySpec({ | ||
var renderImplList = exports.renderImplList = (0, _types.def)('renderImplList :: [TPatch] -> String', _ramda2.default.compose(trimTrailingWhitespace, templates.implList, _ramda2.default.map(_ramda2.default.applySpec({ | ||
owner: _ramda2.default.prop('owner'), | ||
@@ -93,4 +107,5 @@ libName: _ramda2.default.prop('libName'), | ||
})))); | ||
var renderProgram = exports.renderProgram = (0, _types.def)('renderProgram :: [TNodeId] -> [TNode] -> String', function (topology, nodes) { | ||
return templates.program({ topology: topology, nodes: nodes }); | ||
return trimTrailingWhitespace(templates.program({ topology: topology, nodes: nodes })); | ||
}); | ||
@@ -102,3 +117,4 @@ var renderProject = exports.renderProject = (0, _types.def)('renderProject :: TProject -> String', function (project) { | ||
return _ramda2.default.join('\n')([config, runtime, impls, program]); | ||
}); | ||
return _ramda2.default.join('\n')([preambleH, intrusivePtrH, listH, config, runtimeCpp, impls, program]); | ||
}); | ||
//# sourceMappingURL=templates.js.map |
@@ -16,3 +16,3 @@ 'use strict'; | ||
var _xodProject2 = _interopRequireDefault(_xodProject); | ||
var Project = _interopRequireWildcard(_xodProject); | ||
@@ -23,2 +23,4 @@ var _types = require('./types'); | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -29,8 +31,7 @@ | ||
var ARDUINO_IMPLS = ['cpp', 'arduino']; | ||
var CONSTANT_VALUE_PINKEY = 'VAL'; // pinKey of output pin of constant patch, that curried and linked | ||
var CONST_NODETYPES = { | ||
string: 'xod/core/constant-string', | ||
number: 'xod/core/constant-number', | ||
boolean: 'xod/core/constant-logic', | ||
pulse: 'xod/core/constant-logic' | ||
boolean: 'xod/core/constant-boolean', | ||
pulse: 'xod/core/constant-boolean', | ||
string: 'xod/core/constant-string' | ||
}; | ||
@@ -40,3 +41,4 @@ var TYPES_MAP = { | ||
pulse: 'Logic', | ||
boolean: 'Logic' | ||
boolean: 'Logic', | ||
string: 'XString' | ||
}; | ||
@@ -53,3 +55,3 @@ | ||
var isNodeWithCurriedPins = (0, _types.def)('isNodeWithCurriedPins :: Node -> Boolean', _ramda2.default.compose(_ramda2.default.complement(_ramda2.default.isEmpty), _xodProject2.default.getCurriedPins)); | ||
var isNodeWithCurriedPins = (0, _types.def)('isNodeWithCurriedPins :: Node -> Boolean', _ramda2.default.compose(_ramda2.default.complement(_ramda2.default.isEmpty), Project.getAllBoundValues)); | ||
@@ -59,3 +61,3 @@ var nestedValues = (0, _types.def)('nestedValues :: Map String (Map String a) -> [a]', _ramda2.default.compose(_ramda2.default.unnest, _ramda2.default.map(_ramda2.default.values), _ramda2.default.values)); | ||
var isCurriedInNodePin = (0, _types.def)('isCurriedInNodePin :: Map NodeId (Map PinKey DataValue) -> NodeId -> Pin -> Boolean', function (nodePinValues, nodeId, pin) { | ||
var pinKey = _xodProject2.default.getPinKey(pin); | ||
var pinKey = Project.getPinKey(pin); | ||
var nodePins = _ramda2.default.prop(nodeId, nodePinValues); | ||
@@ -65,5 +67,5 @@ return _ramda2.default.has(pinKey, nodePins); | ||
var getNodeCount = (0, _types.def)('getNodeCount :: Patch -> Number', _ramda2.default.compose(_ramda2.default.length, _xodProject2.default.listNodes)); | ||
var getNodeCount = (0, _types.def)('getNodeCount :: Patch -> Number', _ramda2.default.compose(_ramda2.default.length, Project.listNodes)); | ||
var getOutputCount = (0, _types.def)('getOutputCount :: Project -> Number', _ramda2.default.compose(_ramda2.default.reduce(_ramda2.default.max, 0), _ramda2.default.map(_ramda2.default.compose(_ramda2.default.length, _xodProject2.default.listOutputPins)), _xodProject2.default.listPatches)); | ||
var getOutputCount = (0, _types.def)('getOutputCount :: Project -> Number', _ramda2.default.compose(_ramda2.default.reduce(_ramda2.default.max, 0), _ramda2.default.map(_ramda2.default.compose(_ramda2.default.length, Project.listOutputPins)), Project.listPatches)); | ||
@@ -74,2 +76,4 @@ var isConstPath = (0, _types.def)('isConstPath :: PatchPath -> Boolean', function (path) { | ||
var kebabToSnake = _ramda2.default.replace(/-/g, '_'); | ||
var createPatchNames = (0, _types.def)('createPatchNames :: PatchPath -> { owner :: String, libName :: String, patchName :: String }', function (path) { | ||
@@ -82,9 +86,9 @@ var _R$split = _ramda2.default.split('/', path), | ||
var patchName = patchNameParts.join('/').replace(/-/g, '_'); | ||
var patchName = patchNameParts.join('/'); | ||
return { | ||
return _ramda2.default.map(kebabToSnake, { | ||
owner: owner, | ||
libName: libName, | ||
patchName: patchName | ||
}; | ||
}); | ||
}); | ||
@@ -96,22 +100,38 @@ | ||
var getLinksInputNodeIds = (0, _types.def)('getLinksInputNodeIds :: [Link] -> [TNodeId]', _ramda2.default.compose(_ramda2.default.uniq, _ramda2.default.map(_ramda2.default.compose(toInt, _xodProject2.default.getLinkInputNodeId)))); | ||
var getLinksInputNodeIds = (0, _types.def)('getLinksInputNodeIds :: [Link] -> [TNodeId]', _ramda2.default.compose(_ramda2.default.uniq, _ramda2.default.map(_ramda2.default.compose(toInt, Project.getLinkInputNodeId)))); | ||
var getPatchByNodeId = (0, _types.def)('getPatchByNodeId :: Project -> PatchPath -> [TPatch] -> NodeId -> TPatch', function (project, entryPath, patches, nodeId) { | ||
return _ramda2.default.compose(findPatchByPath(_ramda2.default.__, patches), _xodProject2.default.getNodeType, _xodProject2.default.getNodeByIdUnsafe(nodeId), _xodProject2.default.getPatchByPathUnsafe)(entryPath, project); | ||
return _ramda2.default.compose(findPatchByPath(_ramda2.default.__, patches), Project.getNodeType, Project.getNodeByIdUnsafe(nodeId), Project.getPatchByPathUnsafe)(entryPath, project); | ||
}); | ||
var renumberProject = (0, _types.def)('renumberProject :: PatchPath -> Project -> Project', function (path, project) { | ||
return _ramda2.default.compose(_xodFuncTools.explode, _xodProject2.default.assocPatch(path, _ramda2.default.__, project), _xodProject2.default.renumberNodes, _xodProject2.default.getPatchByPathUnsafe)(path, project); | ||
return _ramda2.default.compose(_xodFuncTools.explodeEither, Project.assocPatch(path, _ramda2.default.__, project), Project.renumberNodes, Project.getPatchByPathUnsafe)(path, project); | ||
}); | ||
var getMapOfNodePinValues = (0, _types.def)('getMapOfNodePinValues :: [Node] -> Map NodeId (Map PinKey DataValue)', _ramda2.default.compose(_ramda2.default.reject(_ramda2.default.isEmpty), _ramda2.default.map(_xodProject2.default.getCurriedPins), _ramda2.default.indexBy(_xodProject2.default.getNodeId))); | ||
var getMapOfNodePinValues = (0, _types.def)('getMapOfNodePinValues :: [Node] -> Map NodeId (Map PinKey DataValue)', _ramda2.default.compose(_ramda2.default.reject(_ramda2.default.isEmpty), _ramda2.default.map(Project.getAllBoundValues), _ramda2.default.indexBy(Project.getNodeId))); | ||
var getMapOfNodeTypes = (0, _types.def)('getMapOfNodeTypes :: [Node] -> Map NodeId PatchPath', _ramda2.default.compose(_ramda2.default.map(_xodProject2.default.getNodeType), _ramda2.default.indexBy(_xodProject2.default.getNodeId))); | ||
var getMapOfNodePinsWithLinks = (0, _types.def)('getMapOfNodePinsWithLinks :: [Node] -> [Link] -> Map NodeId [PinKey]', function (nodes, links) { | ||
return _ramda2.default.compose(_ramda2.default.map(_ramda2.default.compose(_ramda2.default.map(Project.getLinkInputPinKey), _ramda2.default.filter(_ramda2.default.__, links), Project.isLinkInputNodeIdEquals, Project.getNodeId)), _ramda2.default.indexBy(Project.getNodeId))(nodes); | ||
}); | ||
var getMapOfNodeOutputPins = (0, _types.def)('getMapOfNodeOutputPins :: [Node] -> Project -> Map NodeId [PinKey]', function (nodes, project) { | ||
return _ramda2.default.compose(_ramda2.default.map(_ramda2.default.compose(_ramda2.default.map(Project.getPinKey), Project.listOutputPins, Project.getPatchByPathUnsafe(_ramda2.default.__, project), Project.getNodeType)), _ramda2.default.indexBy(Project.getNodeId))(nodes); | ||
}); | ||
var getMapOfNodeTypes = (0, _types.def)('getMapOfNodeTypes :: [Node] -> Map NodeId PatchPath', _ramda2.default.compose(_ramda2.default.map(Project.getNodeType), _ramda2.default.indexBy(Project.getNodeId))); | ||
var getMapOfNodePinTypes = (0, _types.def)('getMapOfNodePinTypes :: Map NodeId (Map PinKey DataValue) -> [Node] -> Project -> Map NodeId (Map PinKey DataType)', function (mapOfNodePinValues, curriedNodes, project) { | ||
return _ramda2.default.mapObjIndexed(function (patchPath, nodeId) { | ||
return _ramda2.default.compose(_ramda2.default.map(_xodProject2.default.getPinType), _ramda2.default.indexBy(_xodProject2.default.getPinKey), _ramda2.default.filter(isCurriedInNodePin(mapOfNodePinValues, nodeId)), _xodProject2.default.listPins, _xodProject2.default.getPatchByPathUnsafe(_ramda2.default.__, project))(patchPath); | ||
return _ramda2.default.compose(_ramda2.default.map(Project.getPinType), _ramda2.default.indexBy(Project.getPinKey), _ramda2.default.filter(isCurriedInNodePin(mapOfNodePinValues, nodeId)), Project.listPins, Project.getPatchByPathUnsafe(_ramda2.default.__, project))(patchPath); | ||
}, getMapOfNodeTypes(curriedNodes)); | ||
}); | ||
var getMapOfPathsToPinKeys = (0, _types.def)('getMapOfPathsToPinKeys :: Map DataType PatchPath -> Project -> Map PatchPath PinKey', function (constantPaths, project) { | ||
return _ramda2.default.compose(_ramda2.default.fromPairs, _ramda2.default.map(function (constPath) { | ||
return _ramda2.default.compose(function (constPinKey) { | ||
return [constPath, constPinKey]; | ||
}, Project.getPinKey, _ramda2.default.head, Project.listOutputPins, Project.getPatchByPathUnsafe(_ramda2.default.__, project))(constPath); | ||
}), _ramda2.default.uniq, _ramda2.default.values)(constantPaths); | ||
}); | ||
// :: { NodeId: { PinKey: PinType } } -> { NodeId: { PinKey: PatchPath } } | ||
@@ -126,3 +146,3 @@ var convertMapOfPinTypesIntoMapOfPinPaths = (0, _types.def)('convertMapOfPinTypesIntoMapOfPinPaths :: Map NodeId (Map PinKey DataType) -> Map NodeId (Map PinKey PatchPath)', _ramda2.default.map(_ramda2.default.map(_ramda2.default.prop(_ramda2.default.__, CONST_NODETYPES)))); | ||
return _ramda2.default.map(function (path) { | ||
return _xodProject2.default.getPatchByPathUnsafe(path, project); | ||
return Project.getPatchByPathUnsafe(path, project); | ||
}, paths); | ||
@@ -133,12 +153,13 @@ }); | ||
return _ramda2.default.reduce(function (proj, pair) { | ||
return (0, _xodFuncTools.explode)(_xodProject2.default.assocPatch(pair[0], pair[1], proj)); | ||
return (0, _xodFuncTools.explodeEither)(Project.assocPatch(pair[0], pair[1], proj)); | ||
}, project, patchPairs); | ||
}); | ||
var createNodesWithCurriedPins = (0, _types.def)('createNodesWithCurriedPins :: Map NodeId (Map PinKey DataValue) -> Map NodeId (Map PinKey PatchPath) -> Map NodeId (Map PinKey Node)', function (mapOfPinValues, mapOfPinPaths) { | ||
var createNodesWithBoundValues = (0, _types.def)('createNodesWithBoundValues :: Map NodeId (Map PinKey DataValue) -> Map NodeId (Map PinKey PatchPath) -> Map PatchPath PinKey -> Map NodeId (Map PinKey Node)', function (mapOfPinValues, mapOfPinPaths, mapOfPinKeys) { | ||
return _ramda2.default.mapObjIndexed(function (pinsData, nodeId) { | ||
return _ramda2.default.mapObjIndexed(function (pinValue, pinKey) { | ||
var type = _ramda2.default.path([nodeId, pinKey], mapOfPinPaths); | ||
var constPinKey = _ramda2.default.prop(type, mapOfPinKeys); | ||
return _ramda2.default.compose(_xodProject2.default.setPinCurriedValue(CONSTANT_VALUE_PINKEY, pinValue), _xodProject2.default.curryPin(CONSTANT_VALUE_PINKEY, true), _xodProject2.default.createNode({ x: 0, y: 0 }))(type); | ||
return _ramda2.default.compose(Project.setBoundValue(constPinKey, pinValue), Project.createNode({ x: 0, y: 0 }))(type); | ||
}, pinsData); | ||
@@ -148,8 +169,8 @@ }, mapOfPinValues); | ||
var uncurryPins = (0, _types.def)('uncurryPins :: Map NodeId (Map PinKey DataValue) -> Patch -> Map NodeId Node', function (mapOfPinValues, patch) { | ||
var removeBoundValues = (0, _types.def)('removeBoundValues :: Map NodeId (Map PinKey DataValue) -> Patch -> Map NodeId Node', function (mapOfPinValues, patch) { | ||
return _ramda2.default.mapObjIndexed(function (pinData, nodeId) { | ||
var pinKeys = _ramda2.default.keys(pinData); | ||
return _ramda2.default.compose(_ramda2.default.reduce(function (node, pinKey) { | ||
return _xodProject2.default.curryPin(pinKey, false, node); | ||
}, _ramda2.default.__, pinKeys), _xodProject2.default.getNodeByIdUnsafe(nodeId))(patch); | ||
return Project.removeBoundValue(pinKey, node); | ||
}, _ramda2.default.__, pinKeys), Project.getNodeByIdUnsafe(nodeId))(patch); | ||
}, mapOfPinValues); | ||
@@ -159,15 +180,19 @@ }); | ||
var assocUncurriedNodesToPatch = (0, _types.def)('assocUncurriedNodesToPatch :: Map NodeId Node -> Patch -> Patch', function (nodesMap, patch) { | ||
return _ramda2.default.reduce(_ramda2.default.flip(_xodProject2.default.assocNode), patch, _ramda2.default.values(nodesMap)); | ||
return _ramda2.default.reduce(_ramda2.default.flip(Project.assocNode), patch, _ramda2.default.values(nodesMap)); | ||
}); | ||
// :: { NodeId: { PinKey: Node } } -> { NodeId: { PinKey: Link } } | ||
var createLinksFromCurriedPins = (0, _types.def)('createLinksFromCurriedPins :: Map NodeId (Map PinKey Node) -> Map NodeId (Map PinKey Link)', _ramda2.default.mapObjIndexed(function (pinsData, nodeId) { | ||
return _ramda2.default.mapObjIndexed(function (node, pinKey) { | ||
var newNodeId = _xodProject2.default.getNodeId(node); | ||
return _xodProject2.default.createLink(pinKey, nodeId, CONSTANT_VALUE_PINKEY, newNodeId); | ||
}, pinsData); | ||
})); | ||
var createLinksFromCurriedPins = (0, _types.def)('createLinksFromCurriedPins :: Map NodeId (Map PinKey Node) -> Map PinKey PinLabel -> Map NodeId (Map PinKey Link)', function (mapOfPinNodes, mapOfPinKeys) { | ||
return _ramda2.default.mapObjIndexed(function (pinsData, nodeId) { | ||
return _ramda2.default.mapObjIndexed(function (node, pinKey) { | ||
var constNodeId = Project.getNodeId(node); | ||
var constNodeType = Project.getNodeType(node); | ||
return Project.createLink(pinKey, nodeId, mapOfPinKeys[constNodeType], constNodeId); | ||
}, pinsData); | ||
})(mapOfPinNodes); | ||
}); | ||
var assocNodesToPatch = (0, _types.def)('assocNodesToPatch :: Map NodeId (Map PinKey Node) -> Patch -> Patch', function (nodesMap, patch) { | ||
return _ramda2.default.reduce(_ramda2.default.flip(_xodProject2.default.assocNode), patch, nestedValues(nodesMap)); | ||
return _ramda2.default.reduce(_ramda2.default.flip(Project.assocNode), patch, nestedValues(nodesMap)); | ||
}); | ||
@@ -177,3 +202,3 @@ | ||
return _ramda2.default.reduce(function (p, link) { | ||
return (0, _xodFuncTools.explode)(_xodProject2.default.assocLink(link, p)); | ||
return (0, _xodFuncTools.explodeEither)(Project.assocLink(link, p)); | ||
}, patch, nestedValues(linksMap)); | ||
@@ -183,3 +208,3 @@ }); | ||
var uncurryAndAssocNodes = (0, _types.def)('uncurryAndAssocNodes :: Map NodeId (Map PinKey DataValue) -> Patch -> Patch', function (mapOfNodePinValues, patch) { | ||
return _ramda2.default.compose(assocUncurriedNodesToPatch(_ramda2.default.__, patch), uncurryPins(mapOfNodePinValues))(patch); | ||
return _ramda2.default.compose(assocUncurriedNodesToPatch(_ramda2.default.__, patch), removeBoundValues(mapOfNodePinValues))(patch); | ||
}); | ||
@@ -200,2 +225,10 @@ | ||
/** | ||
* Converts JS-typed data value to a string that is valid and expected | ||
* C++ literal representing that value | ||
*/ | ||
var formatValueLiteral = (0, _types.def)('formatValueLiteral :: DataValue -> String', _ramda2.default.cond([[_ramda2.default.equals(''), _ramda2.default.always('nullptr')], [_ramda2.default.is(String), function (x) { | ||
return '::xod::List<char>::fromPlainArray("' + x + '", ' + x.length + ')'; | ||
}], [_ramda2.default.T, _ramda2.default.toString]])); | ||
//----------------------------------------------------------------------------- | ||
@@ -212,14 +245,22 @@ // | ||
var placeConstNodesAndLinks = (0, _types.def)('placeConstNodesAndLinks :: Project -> PatchPath -> Project -> Project', function (flatProject, path, origProject) { | ||
var entryPointPatch = _xodProject2.default.getPatchByPathUnsafe(path, flatProject); | ||
var entryPointNodes = _xodProject2.default.listNodes(entryPointPatch); | ||
var entryPointPatch = Project.getPatchByPathUnsafe(path, flatProject); | ||
var entryPointNodes = Project.listNodes(entryPointPatch); | ||
var entryPointLinks = Project.listLinks(entryPointPatch); | ||
var nodesWithCurriedPins = _ramda2.default.filter(isNodeWithCurriedPins, entryPointNodes); | ||
var nodePinValues = getMapOfNodePinValues(entryPointNodes); | ||
var occupiedNodePins = getMapOfNodePinsWithLinks(entryPointNodes, entryPointLinks); | ||
var outputNodePins = getMapOfNodeOutputPins(entryPointNodes, origProject); | ||
var pinsToOmit = _ramda2.default.mergeWith(_ramda2.default.concat, occupiedNodePins, outputNodePins); | ||
var nodePinValues = _ramda2.default.compose(_ramda2.default.mapObjIndexed(function (pins, nodeId) { | ||
return _ramda2.default.omit(_ramda2.default.propOr([], nodeId, pinsToOmit), pins); | ||
}), getMapOfNodePinValues)(entryPointNodes); | ||
var pinPaths = getMapOfPinPaths(nodePinValues, nodesWithCurriedPins, flatProject); | ||
var constPinKeys = getMapOfPathsToPinKeys(CONST_NODETYPES, origProject); | ||
var newConstNodes = createNodesWithCurriedPins(nodePinValues, pinPaths); | ||
var newLinks = createLinksFromCurriedPins(newConstNodes); | ||
var newConstNodes = createNodesWithBoundValues(nodePinValues, pinPaths, constPinKeys); | ||
var newLinks = createLinksFromCurriedPins(newConstNodes, constPinKeys); | ||
var newPatch = updatePatch(newLinks, newConstNodes, nodePinValues, entryPointPatch); | ||
return _ramda2.default.compose(_xodFuncTools.explode, _xodProject2.default.assocPatch(path, newPatch), copyConstPatches(pinPaths, origProject))(flatProject); | ||
return _ramda2.default.compose(_xodFuncTools.explodeEither, Project.assocPatch(path, newPatch), copyConstPatches(pinPaths, origProject))(flatProject); | ||
}); | ||
@@ -230,3 +271,3 @@ | ||
return _ramda2.default.applySpec({ | ||
NODE_COUNT: _ramda2.default.compose(getNodeCount, _xodProject2.default.getPatchByPathUnsafe(path)), | ||
NODE_COUNT: _ramda2.default.compose(getNodeCount, Project.getPatchByPathUnsafe(path)), | ||
MAX_OUTPUT_COUNT: getOutputCount, | ||
@@ -237,3 +278,3 @@ XOD_DEBUG: _ramda2.default.F | ||
var createTTopology = (0, _types.def)('createTTopology :: PatchPath -> Project -> [Number]', _ramda2.default.compose(_ramda2.default.map(toInt), _xodProject2.default.getTopology, _xodProject2.default.getPatchByPathUnsafe)); | ||
var createTTopology = (0, _types.def)('createTTopology :: PatchPath -> Project -> [Number]', _ramda2.default.compose(_ramda2.default.map(toInt), Project.getTopology, Project.getPatchByPathUnsafe)); | ||
@@ -243,11 +284,15 @@ var createTPatches = (0, _types.def)('createTPatches :: PatchPath -> Project -> [TPatch]', function (entryPath, project) { | ||
var names = createPatchNames(path); | ||
var impl = (0, _xodFuncTools.explode)(_xodProject2.default.getImplByArray(ARDUINO_IMPLS, patch)); | ||
var isDirty = isConstPath(path); | ||
var impl = (0, _xodFuncTools.explodeMaybe)('Implementation for ' + path + ' not found', Project.getImplByArray(ARDUINO_IMPLS, patch)); | ||
var isDirty = _ramda2.default.either(isConstPath, _ramda2.default.equals('xod/core/boot'))(path); | ||
var outputs = _ramda2.default.compose(_ramda2.default.map(_ramda2.default.applySpec({ | ||
type: _ramda2.default.compose(_ramda2.default.prop(_ramda2.default.__, TYPES_MAP), _xodProject2.default.getPinType), | ||
pinKey: _xodProject2.default.getPinKey, | ||
value: _ramda2.default.compose(_xodProject2.default.defaultValueOfType, _xodProject2.default.getPinType) | ||
})), _xodProject2.default.listOutputPins)(patch); | ||
var inputs = _ramda2.default.compose(_ramda2.default.map(_ramda2.default.applySpec({ pinKey: _xodProject2.default.getPinKey })), _xodProject2.default.listInputPins)(patch); | ||
type: _ramda2.default.compose(_ramda2.default.prop(_ramda2.default.__, TYPES_MAP), Project.getPinType), | ||
pinKey: Project.getPinLabel, | ||
value: _ramda2.default.compose(formatValueLiteral, Project.defaultValueOfType, Project.getPinType) | ||
})), Project.normalizePinLabels, Project.listOutputPins)(patch); | ||
var inputs = _ramda2.default.compose(_ramda2.default.map(_ramda2.default.applySpec({ | ||
type: _ramda2.default.compose(_ramda2.default.prop(_ramda2.default.__, TYPES_MAP), Project.getPinType), | ||
pinKey: Project.getPinLabel | ||
})), Project.normalizePinLabels, Project.listInputPins)(patch); | ||
@@ -260,29 +305,49 @@ return _ramda2.default.merge(names, { | ||
}); | ||
}), _ramda2.default.omit([entryPath]), _ramda2.default.converge(_ramda2.default.zipObj, [_xodProject2.default.listPatchPaths, _xodProject2.default.listPatches]))(project); | ||
}), _ramda2.default.omit([entryPath]), _ramda2.default.indexBy(Project.getPatchPath), Project.listPatchesWithoutBuiltIns)(project); | ||
}); | ||
var getPinLabelsMap = (0, _types.def)('getPinLabelsMap :: [Pin] -> Map PinKey PinLabel', _ramda2.default.compose(_ramda2.default.map(Project.getPinLabel), _ramda2.default.indexBy(Project.getPinKey))); | ||
var getNodePinLabels = (0, _types.def)('getNodePinLabels :: Node -> Project -> Map PinKey PinLabel', function (node, project) { | ||
return _ramda2.default.compose(getPinLabelsMap, Project.normalizePinLabels, (0, _xodFuncTools.explodeMaybe)('Can\u2019t get node pins of node ' + node + '. Referred type missing?'), Project.getNodePins)(node, project); | ||
}); | ||
// TODO: Remove it when `Project.getBoundValue` will return default values | ||
/** | ||
* In case when `getBoundValue` doesn't contain a bound value | ||
* we have to fallback to default pin value. | ||
*/ | ||
var getDefaultPinValue = (0, _types.def)('getDefaultPinValue :: PinKey -> Node -> Project -> DataValue', function (pinKey, node, project) { | ||
return _ramda2.default.compose((0, _xodFuncTools.explodeMaybe)('Can\u2019t find pin with key ' + pinKey + ' for node ' + node + '"'), _ramda2.default.map(_ramda2.default.compose(Project.defaultValueOfType, Project.getPinType)), _ramda2.default.chain(Project.getPinByKey(pinKey)), Project.getPatchByNode(_ramda2.default.__, project))(node); | ||
}); | ||
var getTNodeOutputs = (0, _types.def)('getTNodeOutputs :: Project -> PatchPath -> Node -> [TNodeOutput]', function (project, entryPath, node) { | ||
var nodeId = _xodProject2.default.getNodeId(node); | ||
var nodeId = Project.getNodeId(node); | ||
var nodePins = getNodePinLabels(node, project); | ||
return _ramda2.default.compose(_ramda2.default.values, _ramda2.default.mapObjIndexed(function (links, pinKey) { | ||
var to = getLinksInputNodeIds(links); | ||
var value = _xodProject2.default.isPinCurried(pinKey, node) ? (0, _xodFuncTools.explode)(_xodProject2.default.getPinCurriedValue(pinKey, node)) : null; | ||
return { | ||
to: to, | ||
pinKey: pinKey, | ||
value: value | ||
to: getLinksInputNodeIds(links), | ||
pinKey: nodePins[pinKey], | ||
value: formatValueLiteral(Project.getBoundValue(pinKey, node).getOrElse(getDefaultPinValue(pinKey, node, project))) | ||
}; | ||
}), _ramda2.default.groupBy(_xodProject2.default.getLinkOutputPinKey), _ramda2.default.filter(_xodProject2.default.isLinkOutputNodeIdEquals(nodeId)), _xodProject2.default.listLinksByNode(node), _xodProject2.default.getPatchByPathUnsafe)(entryPath, project); | ||
}), _ramda2.default.groupBy(Project.getLinkOutputPinKey), _ramda2.default.filter(Project.isLinkOutputNodeIdEquals(nodeId)), Project.listLinksByNode(node), Project.getPatchByPathUnsafe)(entryPath, project); | ||
}); | ||
var getOutputPinLabelByLink = (0, _types.def)('getOutputPinLabelByLink :: Project -> Patch -> Link -> PinLabel', function (project, patch, link) { | ||
var pinKey = Project.getLinkOutputPinKey(link); | ||
return _ramda2.default.compose((0, _xodFuncTools.explodeMaybe)('Can\u2019t find pin with key ' + pinKey + ' for link ' + link + ' on patch ' + patch), _ramda2.default.map(_ramda2.default.compose(Project.getPinLabel, _ramda2.default.head, Project.normalizePinLabels, _ramda2.default.of)), _ramda2.default.chain(Project.getPinByKey(pinKey)), _ramda2.default.chain(Project.getPatchByNode(_ramda2.default.__, project)), Project.getNodeById(_ramda2.default.__, patch), Project.getLinkOutputNodeId)(link); | ||
}); | ||
var getTNodeInputs = (0, _types.def)('getTNodeInputs :: Project -> PatchPath -> [TPatch] -> Node -> [TNodeInput]', function (project, entryPath, patches, node) { | ||
var nodeId = _xodProject2.default.getNodeId(node); | ||
var patch = Project.getPatchByPathUnsafe(entryPath, project); | ||
var nodeId = Project.getNodeId(node); | ||
var nodePins = getNodePinLabels(node, project); | ||
return _ramda2.default.compose(_ramda2.default.map(_ramda2.default.applySpec({ | ||
nodeId: _ramda2.default.compose(toInt, _xodProject2.default.getLinkOutputNodeId), | ||
patch: _ramda2.default.compose(getPatchByNodeId(project, entryPath, patches), _xodProject2.default.getLinkOutputNodeId), | ||
pinKey: _xodProject2.default.getLinkInputPinKey, | ||
fromPinKey: _xodProject2.default.getLinkOutputPinKey | ||
})), _ramda2.default.filter(_xodProject2.default.isLinkInputNodeIdEquals(nodeId)), _xodProject2.default.listLinksByNode(node), _xodProject2.default.getPatchByPathUnsafe)(entryPath, project); | ||
nodeId: _ramda2.default.compose(toInt, Project.getLinkOutputNodeId), | ||
patch: _ramda2.default.compose(getPatchByNodeId(project, entryPath, patches), Project.getLinkOutputNodeId), | ||
pinKey: _ramda2.default.compose(_ramda2.default.prop(_ramda2.default.__, nodePins), Project.getLinkInputPinKey), | ||
fromPinKey: getOutputPinLabelByLink(project, patch) | ||
})), _ramda2.default.filter(Project.isLinkInputNodeIdEquals(nodeId)), Project.listLinksByNode(node))(patch); | ||
}); | ||
@@ -292,10 +357,14 @@ | ||
return _ramda2.default.compose(_ramda2.default.map(_ramda2.default.applySpec({ | ||
id: _ramda2.default.compose(toInt, _xodProject2.default.getNodeId), | ||
patch: _ramda2.default.compose(findPatchByPath(_ramda2.default.__, patches), _xodProject2.default.getNodeType), | ||
id: _ramda2.default.compose(toInt, Project.getNodeId), | ||
patch: _ramda2.default.compose(findPatchByPath(_ramda2.default.__, patches), Project.getNodeType), | ||
outputs: getTNodeOutputs(project, entryPath), | ||
inputs: getTNodeInputs(project, entryPath, patches) | ||
})), _xodProject2.default.listNodes, _xodProject2.default.getPatchByPathUnsafe)(entryPath, project); | ||
})), Project.listNodes, Project.getPatchByPathUnsafe)(entryPath, project); | ||
}); | ||
// Transforms Project into TProject | ||
/** | ||
* Transforms Project into TProject. | ||
* TProject is an object, that ready to be passed into renderer (handlebars) | ||
* and it has a ready-to-use values, nothing needed to compute anymore. | ||
*/ | ||
var transformProject = exports.transformProject = (0, _types.def)('transformProject :: [Source] -> Project -> PatchPath -> Either Error TProject', function (impls, project, path) { | ||
@@ -310,5 +379,6 @@ return _ramda2.default.compose(_ramda2.default.map(_ramda2.default.compose(function (proj) { | ||
})(proj); | ||
}, renumberProject(path), placeConstNodesAndLinks(_ramda2.default.__, path, project))), _xodProject2.default.flatten)(project, path, impls); | ||
}, renumberProject(path), placeConstNodesAndLinks(_ramda2.default.__, path, project))), Project.flatten)(project, path, impls); | ||
}); | ||
exports.default = (0, _types.def)('transpile :: Project -> PatchPath -> Either Error String', _ramda2.default.compose(_ramda2.default.map(_templates.renderProject), transformProject(ARDUINO_IMPLS))); | ||
exports.default = (0, _types.def)('transpile :: Project -> PatchPath -> Either Error String', _ramda2.default.compose(_ramda2.default.map(_templates.renderProject), transformProject(ARDUINO_IMPLS))); | ||
//# sourceMappingURL=transpiler.js.map |
@@ -43,2 +43,3 @@ 'use strict'; | ||
var AliasType = _xodFuncTools2.default.AliasType(packageName, docUrl); | ||
var OneOfType = _xodFuncTools2.default.OneOfType(packageName, docUrl); | ||
@@ -51,3 +52,3 @@ //----------------------------------------------------------------------------- | ||
var TNodeId = AliasType('TNodeId', _sanctuaryDef2.default.Number); | ||
var TPinKey = AliasType('TPinKey', _sanctuaryDef2.default.String); | ||
var TPinKey = OneOfType('TPinKey', [_xodProject.PinKey, _xodProject.PinLabel]); | ||
var DataValue = NullaryType('DataValue', _ramda2.default.complement(_ramda2.default.isNil)); | ||
@@ -119,2 +120,3 @@ | ||
}); | ||
exports.default = def; | ||
exports.default = def; | ||
//# sourceMappingURL=types.js.map |
{ | ||
"name": "xod-arduino", | ||
"version": "0.1.2", | ||
"version": "0.1.3", | ||
"description": "XOD project: Arduino transpiler", | ||
"scripts": { | ||
"build": "babel src/ -d dist/", | ||
"dev": "npm run build -- --watch", | ||
"build": "babel src/ -d dist/ --source-maps", | ||
"dev": "yarn run build -- --watch", | ||
"doc": "documentation build --format html --output doc --sort-order alpha src/", | ||
"test": "mocha test/**/*.spec.js" | ||
"test": "BABEL_DISABLE_CACHE=1 mocha test/**/*.spec.js", | ||
"test:cpp": "./tools/test-cpp.sh" | ||
}, | ||
@@ -18,7 +19,7 @@ "repository": {}, | ||
"handlebars": "^4.0.6", | ||
"hm-def": "^0.1.2", | ||
"hm-def": "^0.2.0", | ||
"ramda": "^0.23.0", | ||
"sanctuary-def": "^0.9.0", | ||
"xod-project": "^0.1.2", | ||
"xod-func-tools": "^0.1.2" | ||
"xod-func-tools": "^0.1.3", | ||
"xod-project": "^0.1.3" | ||
}, | ||
@@ -31,5 +32,5 @@ "engines": { | ||
"chai": "^3.5.0", | ||
"chai-string": "^1.3.0", | ||
"dirty-chai": "^1.2.2" | ||
"dirty-chai": "^1.2.2", | ||
"xod-fs": "^0.1.3" | ||
} | ||
} |
@@ -11,3 +11,6 @@ import R from 'ramda'; | ||
import runtime from '../platform/runtime.cpp'; | ||
import preambleH from '../platform/preamble.h'; | ||
import intrusivePtrH from '../platform/intrusive_ptr.h'; | ||
import listH from '../platform/list.h'; | ||
import runtimeCpp from '../platform/runtime.cpp'; | ||
@@ -19,2 +22,3 @@ // ============================================================================= | ||
// ============================================================================= | ||
const trimTrailingWhitespace = R.replace(/\s+$/gm, '\n'); | ||
const indexByPinKey = R.indexBy(R.prop('pinKey')); | ||
@@ -55,2 +59,13 @@ const getPatchPins = direction => R.compose( | ||
}); | ||
// Temporary switch to global C++ namespace | ||
Handlebars.registerHelper('global', function global(options) { | ||
return [ | ||
'// --- Enter global namespace ---', | ||
'}}', | ||
options.fn(this), | ||
'namespace _program {', | ||
`namespace ${this.owner}__${this.libName}__${this.patchName} {`, | ||
'// --- Back to local namespace ---', | ||
].join('\n'); | ||
}); | ||
@@ -91,5 +106,11 @@ // ============================================================================= | ||
(data) => { | ||
const ctx = R.applySpec({ | ||
owner: R.prop('owner'), | ||
libName: R.prop('libName'), | ||
patchName: R.prop('patchName'), | ||
GENERATED_CODE: renderPatchContext, | ||
})(data); | ||
const patchImpl = R.prop('impl', data); | ||
const patchContext = renderPatchContext(data); | ||
return Handlebars.compile(patchImpl, renderingOptions)({ GENERATED_CODE: patchContext }); | ||
return Handlebars.compile(patchImpl, renderingOptions)(ctx); | ||
} | ||
@@ -100,2 +121,3 @@ ); | ||
R.compose( | ||
trimTrailingWhitespace, | ||
templates.implList, | ||
@@ -112,5 +134,8 @@ R.map( | ||
); | ||
export const renderProgram = def( | ||
'renderProgram :: [TNodeId] -> [TNode] -> String', | ||
(topology, nodes) => templates.program({ topology, nodes }) | ||
(topology, nodes) => trimTrailingWhitespace( | ||
templates.program({ topology, nodes }) | ||
) | ||
); | ||
@@ -125,4 +150,7 @@ export const renderProject = def( | ||
return R.join('\n')([ | ||
preambleH, | ||
intrusivePtrH, | ||
listH, | ||
config, | ||
runtime, | ||
runtimeCpp, | ||
impls, | ||
@@ -129,0 +157,0 @@ program, |
import R from 'ramda'; | ||
import { explode } from 'xod-func-tools'; | ||
import Project from 'xod-project'; | ||
import { explodeMaybe, explodeEither } from 'xod-func-tools'; | ||
import * as Project from 'xod-project'; | ||
import { def } from './types'; | ||
@@ -10,8 +10,7 @@ | ||
const ARDUINO_IMPLS = ['cpp', 'arduino']; | ||
const CONSTANT_VALUE_PINKEY = 'VAL'; // pinKey of output pin of constant patch, that curried and linked | ||
const CONST_NODETYPES = { | ||
number: 'xod/core/constant-number', | ||
boolean: 'xod/core/constant-boolean', | ||
pulse: 'xod/core/constant-boolean', | ||
string: 'xod/core/constant-string', | ||
number: 'xod/core/constant-number', | ||
boolean: 'xod/core/constant-logic', | ||
pulse: 'xod/core/constant-logic', | ||
}; | ||
@@ -22,2 +21,3 @@ const TYPES_MAP = { | ||
boolean: 'Logic', | ||
string: 'XString', | ||
}; | ||
@@ -38,3 +38,3 @@ | ||
R.complement(R.isEmpty), | ||
Project.getCurriedPins | ||
Project.getAllBoundValues | ||
) | ||
@@ -83,2 +83,4 @@ ); | ||
const kebabToSnake = R.replace(/-/g, '_'); | ||
const createPatchNames = def( | ||
@@ -88,9 +90,9 @@ 'createPatchNames :: PatchPath -> { owner :: String, libName :: String, patchName :: String }', | ||
const [owner, libName, ...patchNameParts] = R.split('/', path); | ||
const patchName = patchNameParts.join('/').replace(/-/g, '_'); | ||
const patchName = patchNameParts.join('/'); | ||
return { | ||
return R.map(kebabToSnake, { | ||
owner, | ||
libName, | ||
patchName, | ||
}; | ||
}); | ||
} | ||
@@ -134,3 +136,3 @@ ); | ||
(path, project) => R.compose( | ||
explode, | ||
explodeEither, | ||
Project.assocPatch(path, R.__, project), | ||
@@ -146,3 +148,3 @@ Project.renumberNodes, | ||
R.reject(R.isEmpty), | ||
R.map(Project.getCurriedPins), | ||
R.map(Project.getAllBoundValues), | ||
R.indexBy(Project.getNodeId) | ||
@@ -152,2 +154,28 @@ ) | ||
const getMapOfNodePinsWithLinks = def( | ||
'getMapOfNodePinsWithLinks :: [Node] -> [Link] -> Map NodeId [PinKey]', | ||
(nodes, links) => R.compose( | ||
R.map(R.compose( | ||
R.map(Project.getLinkInputPinKey), | ||
R.filter(R.__, links), | ||
Project.isLinkInputNodeIdEquals, | ||
Project.getNodeId | ||
)), | ||
R.indexBy(Project.getNodeId) | ||
)(nodes) | ||
); | ||
const getMapOfNodeOutputPins = def( | ||
'getMapOfNodeOutputPins :: [Node] -> Project -> Map NodeId [PinKey]', | ||
(nodes, project) => R.compose( | ||
R.map(R.compose( | ||
R.map(Project.getPinKey), | ||
Project.listOutputPins, | ||
Project.getPatchByPathUnsafe(R.__, project), | ||
Project.getNodeType | ||
)), | ||
R.indexBy(Project.getNodeId) | ||
)(nodes) | ||
); | ||
const getMapOfNodeTypes = def( | ||
@@ -175,2 +203,19 @@ 'getMapOfNodeTypes :: [Node] -> Map NodeId PatchPath', | ||
const getMapOfPathsToPinKeys = def( | ||
'getMapOfPathsToPinKeys :: Map DataType PatchPath -> Project -> Map PatchPath PinKey', | ||
(constantPaths, project) => R.compose( | ||
R.fromPairs, | ||
R.map(constPath => R.compose( | ||
constPinKey => [constPath, constPinKey], | ||
Project.getPinKey, | ||
R.head, | ||
Project.listOutputPins, | ||
Project.getPatchByPathUnsafe(R.__, project) | ||
)(constPath) | ||
), | ||
R.uniq, | ||
R.values | ||
)(constantPaths) | ||
); | ||
// :: { NodeId: { PinKey: PinType } } -> { NodeId: { PinKey: PatchPath } } | ||
@@ -209,3 +254,3 @@ const convertMapOfPinTypesIntoMapOfPinPaths = def( | ||
(patchPairs, project) => R.reduce( | ||
(proj, pair) => explode(Project.assocPatch(pair[0], pair[1], proj)), | ||
(proj, pair) => explodeEither(Project.assocPatch(pair[0], pair[1], proj)), | ||
project, | ||
@@ -216,12 +261,12 @@ patchPairs | ||
const createNodesWithCurriedPins = def( | ||
'createNodesWithCurriedPins :: Map NodeId (Map PinKey DataValue) -> Map NodeId (Map PinKey PatchPath) -> Map NodeId (Map PinKey Node)', | ||
(mapOfPinValues, mapOfPinPaths) => R.mapObjIndexed( | ||
const createNodesWithBoundValues = def( | ||
'createNodesWithBoundValues :: Map NodeId (Map PinKey DataValue) -> Map NodeId (Map PinKey PatchPath) -> Map PatchPath PinKey -> Map NodeId (Map PinKey Node)', | ||
(mapOfPinValues, mapOfPinPaths, mapOfPinKeys) => R.mapObjIndexed( | ||
(pinsData, nodeId) => R.mapObjIndexed( | ||
(pinValue, pinKey) => { | ||
const type = R.path([nodeId, pinKey], mapOfPinPaths); | ||
const constPinKey = R.prop(type, mapOfPinKeys); | ||
return R.compose( | ||
Project.setPinCurriedValue(CONSTANT_VALUE_PINKEY, pinValue), | ||
Project.curryPin(CONSTANT_VALUE_PINKEY, true), | ||
Project.setBoundValue(constPinKey, pinValue), | ||
Project.createNode({ x: 0, y: 0 }) | ||
@@ -236,4 +281,4 @@ )(type); | ||
const uncurryPins = def( | ||
'uncurryPins :: Map NodeId (Map PinKey DataValue) -> Patch -> Map NodeId Node', | ||
const removeBoundValues = def( | ||
'removeBoundValues :: Map NodeId (Map PinKey DataValue) -> Patch -> Map NodeId Node', | ||
(mapOfPinValues, patch) => R.mapObjIndexed( | ||
@@ -244,3 +289,3 @@ (pinData, nodeId) => { | ||
R.reduce( | ||
(node, pinKey) => Project.curryPin(pinKey, false, node), | ||
(node, pinKey) => Project.removeBoundValue(pinKey, node), | ||
R.__, | ||
@@ -267,12 +312,14 @@ pinKeys | ||
const createLinksFromCurriedPins = def( | ||
'createLinksFromCurriedPins :: Map NodeId (Map PinKey Node) -> Map NodeId (Map PinKey Link)', | ||
R.mapObjIndexed( | ||
'createLinksFromCurriedPins :: Map NodeId (Map PinKey Node) -> Map PinKey PinLabel -> Map NodeId (Map PinKey Link)', | ||
(mapOfPinNodes, mapOfPinKeys) => R.mapObjIndexed( | ||
(pinsData, nodeId) => R.mapObjIndexed( | ||
(node, pinKey) => { | ||
const newNodeId = Project.getNodeId(node); | ||
return Project.createLink(pinKey, nodeId, CONSTANT_VALUE_PINKEY, newNodeId); | ||
const constNodeId = Project.getNodeId(node); | ||
const constNodeType = Project.getNodeType(node); | ||
return Project.createLink(pinKey, nodeId, mapOfPinKeys[constNodeType], constNodeId); | ||
}, | ||
pinsData | ||
) | ||
) | ||
)(mapOfPinNodes) | ||
); | ||
@@ -292,3 +339,3 @@ | ||
(linksMap, patch) => R.reduce( | ||
(p, link) => explode(Project.assocLink(link, p)), | ||
(p, link) => explodeEither(Project.assocLink(link, p)), | ||
patch, | ||
@@ -303,3 +350,3 @@ nestedValues(linksMap) | ||
assocUncurriedNodesToPatch(R.__, patch), | ||
uncurryPins(mapOfNodePinValues) | ||
removeBoundValues(mapOfNodePinValues) | ||
)(patch) | ||
@@ -340,2 +387,15 @@ ); | ||
/** | ||
* Converts JS-typed data value to a string that is valid and expected | ||
* C++ literal representing that value | ||
*/ | ||
const formatValueLiteral = def( | ||
'formatValueLiteral :: DataValue -> String', | ||
R.cond([ | ||
[R.equals(''), R.always('nullptr')], | ||
[R.is(String), x => `::xod::List<char>::fromPlainArray("${x}", ${x.length})`], | ||
[R.T, R.toString], | ||
]) | ||
); | ||
//----------------------------------------------------------------------------- | ||
@@ -356,13 +416,27 @@ // | ||
const entryPointNodes = Project.listNodes(entryPointPatch); | ||
const entryPointLinks = Project.listLinks(entryPointPatch); | ||
const nodesWithCurriedPins = R.filter(isNodeWithCurriedPins, entryPointNodes); | ||
const nodePinValues = getMapOfNodePinValues(entryPointNodes); | ||
const occupiedNodePins = getMapOfNodePinsWithLinks(entryPointNodes, entryPointLinks); | ||
const outputNodePins = getMapOfNodeOutputPins(entryPointNodes, origProject); | ||
const pinsToOmit = R.mergeWith(R.concat, occupiedNodePins, outputNodePins); | ||
const nodePinValues = R.compose( | ||
R.mapObjIndexed( | ||
(pins, nodeId) => R.omit( | ||
R.propOr([], nodeId, pinsToOmit), | ||
pins | ||
) | ||
), | ||
getMapOfNodePinValues | ||
)(entryPointNodes); | ||
const pinPaths = getMapOfPinPaths(nodePinValues, nodesWithCurriedPins, flatProject); | ||
const constPinKeys = getMapOfPathsToPinKeys(CONST_NODETYPES, origProject); | ||
const newConstNodes = createNodesWithCurriedPins(nodePinValues, pinPaths); | ||
const newLinks = createLinksFromCurriedPins(newConstNodes); | ||
const newConstNodes = createNodesWithBoundValues(nodePinValues, pinPaths, constPinKeys); | ||
const newLinks = createLinksFromCurriedPins(newConstNodes, constPinKeys); | ||
const newPatch = updatePatch(newLinks, newConstNodes, nodePinValues, entryPointPatch); | ||
return R.compose( | ||
explode, | ||
explodeEither, | ||
Project.assocPatch(path, newPatch), | ||
@@ -399,15 +473,28 @@ copyConstPatches(pinPaths, origProject) | ||
const names = createPatchNames(path); | ||
const impl = explode(Project.getImplByArray(ARDUINO_IMPLS, patch)); | ||
const isDirty = isConstPath(path); | ||
const impl = explodeMaybe( | ||
`Implementation for ${path} not found`, | ||
Project.getImplByArray(ARDUINO_IMPLS, patch) | ||
); | ||
const isDirty = R.either(isConstPath, R.equals('xod/core/boot'))(path); | ||
const outputs = R.compose( | ||
R.map(R.applySpec({ | ||
type: R.compose(R.prop(R.__, TYPES_MAP), Project.getPinType), | ||
pinKey: Project.getPinKey, | ||
value: R.compose(Project.defaultValueOfType, Project.getPinType), | ||
pinKey: Project.getPinLabel, | ||
value: R.compose( | ||
formatValueLiteral, | ||
Project.defaultValueOfType, | ||
Project.getPinType | ||
), | ||
})), | ||
Project.normalizePinLabels, | ||
Project.listOutputPins | ||
)(patch); | ||
const inputs = R.compose( | ||
R.map(R.applySpec({ pinKey: Project.getPinKey })), | ||
R.map(R.applySpec({ | ||
type: R.compose(R.prop(R.__, TYPES_MAP), Project.getPinType), | ||
pinKey: Project.getPinLabel, | ||
})), | ||
Project.normalizePinLabels, | ||
Project.listInputPins | ||
@@ -426,6 +513,43 @@ )(patch); | ||
R.omit([entryPath]), | ||
R.converge(R.zipObj, [Project.listPatchPaths, Project.listPatches]) | ||
R.indexBy(Project.getPatchPath), | ||
Project.listPatchesWithoutBuiltIns | ||
)(project) | ||
); | ||
const getPinLabelsMap = def( | ||
'getPinLabelsMap :: [Pin] -> Map PinKey PinLabel', | ||
R.compose( | ||
R.map(Project.getPinLabel), | ||
R.indexBy(Project.getPinKey) | ||
) | ||
); | ||
const getNodePinLabels = def( | ||
'getNodePinLabels :: Node -> Project -> Map PinKey PinLabel', | ||
(node, project) => R.compose( | ||
getPinLabelsMap, | ||
Project.normalizePinLabels, | ||
explodeMaybe(`Can’t get node pins of node ${node}. Referred type missing?`), | ||
Project.getNodePins | ||
)(node, project) | ||
); | ||
// TODO: Remove it when `Project.getBoundValue` will return default values | ||
/** | ||
* In case when `getBoundValue` doesn't contain a bound value | ||
* we have to fallback to default pin value. | ||
*/ | ||
const getDefaultPinValue = def( | ||
'getDefaultPinValue :: PinKey -> Node -> Project -> DataValue', | ||
(pinKey, node, project) => R.compose( | ||
explodeMaybe(`Can’t find pin with key ${pinKey} for node ${node}"`), | ||
R.map(R.compose( | ||
Project.defaultValueOfType, | ||
Project.getPinType | ||
)), | ||
R.chain(Project.getPinByKey(pinKey)), | ||
Project.getPatchByNode(R.__, project) | ||
)(node) | ||
); | ||
const getTNodeOutputs = def( | ||
@@ -435,17 +559,14 @@ 'getTNodeOutputs :: Project -> PatchPath -> Node -> [TNodeOutput]', | ||
const nodeId = Project.getNodeId(node); | ||
const nodePins = getNodePinLabels(node, project); | ||
return R.compose( | ||
R.values, | ||
R.mapObjIndexed((links, pinKey) => { | ||
const to = getLinksInputNodeIds(links); | ||
const value = (Project.isPinCurried(pinKey, node)) ? | ||
explode(Project.getPinCurriedValue(pinKey, node)) : | ||
null; | ||
return { | ||
to, | ||
pinKey, | ||
value, | ||
}; | ||
}), | ||
R.mapObjIndexed((links, pinKey) => ({ | ||
to: getLinksInputNodeIds(links), | ||
pinKey: nodePins[pinKey], | ||
value: formatValueLiteral( | ||
Project.getBoundValue(pinKey, node) | ||
.getOrElse(getDefaultPinValue(pinKey, node, project)) | ||
), | ||
})), | ||
R.groupBy(Project.getLinkOutputPinKey), | ||
@@ -459,6 +580,28 @@ R.filter(Project.isLinkOutputNodeIdEquals(nodeId)), | ||
const getOutputPinLabelByLink = def( | ||
'getOutputPinLabelByLink :: Project -> Patch -> Link -> PinLabel', | ||
(project, patch, link) => { | ||
const pinKey = Project.getLinkOutputPinKey(link); | ||
return R.compose( | ||
explodeMaybe(`Can’t find pin with key ${pinKey} for link ${link} on patch ${patch}`), | ||
R.map(R.compose( | ||
Project.getPinLabel, | ||
R.head, | ||
Project.normalizePinLabels, | ||
R.of | ||
)), | ||
R.chain(Project.getPinByKey(pinKey)), | ||
R.chain(Project.getPatchByNode(R.__, project)), | ||
Project.getNodeById(R.__, patch), | ||
Project.getLinkOutputNodeId | ||
)(link); | ||
} | ||
); | ||
const getTNodeInputs = def( | ||
'getTNodeInputs :: Project -> PatchPath -> [TPatch] -> Node -> [TNodeInput]', | ||
(project, entryPath, patches, node) => { | ||
const patch = Project.getPatchByPathUnsafe(entryPath, project); | ||
const nodeId = Project.getNodeId(node); | ||
const nodePins = getNodePinLabels(node, project); | ||
@@ -472,9 +615,8 @@ return R.compose( | ||
), | ||
pinKey: Project.getLinkInputPinKey, | ||
fromPinKey: Project.getLinkOutputPinKey, | ||
pinKey: R.compose(R.prop(R.__, nodePins), Project.getLinkInputPinKey), | ||
fromPinKey: getOutputPinLabelByLink(project, patch), | ||
})), | ||
R.filter(Project.isLinkInputNodeIdEquals(nodeId)), | ||
Project.listLinksByNode(node), | ||
Project.getPatchByPathUnsafe | ||
)(entryPath, project); | ||
Project.listLinksByNode(node) | ||
)(patch); | ||
} | ||
@@ -497,3 +639,7 @@ ); | ||
// Transforms Project into TProject | ||
/** | ||
* Transforms Project into TProject. | ||
* TProject is an object, that ready to be passed into renderer (handlebars) | ||
* and it has a ready-to-use values, nothing needed to compute anymore. | ||
*/ | ||
export const transformProject = def( | ||
@@ -521,3 +667,6 @@ 'transformProject :: [Source] -> Project -> PatchPath -> Either Error TProject', | ||
'transpile :: Project -> PatchPath -> Either Error String', | ||
R.compose(R.map(renderProject), transformProject(ARDUINO_IMPLS)) | ||
R.compose( | ||
R.map(renderProject), | ||
transformProject(ARDUINO_IMPLS) | ||
) | ||
); |
import R from 'ramda'; | ||
import $ from 'sanctuary-def'; | ||
import HMDef from 'hm-def'; | ||
import { env as xEnv } from 'xod-project'; | ||
import { env as xEnv, PinKey, PinLabel } from 'xod-project'; | ||
import XF from 'xod-func-tools'; | ||
@@ -22,2 +22,3 @@ | ||
const AliasType = XF.AliasType(packageName, docUrl); | ||
const OneOfType = XF.OneOfType(packageName, docUrl); | ||
@@ -30,3 +31,3 @@ //----------------------------------------------------------------------------- | ||
const TNodeId = AliasType('TNodeId', $.Number); | ||
const TPinKey = AliasType('TPinKey', $.String); | ||
const TPinKey = OneOfType('TPinKey', [PinKey, PinLabel]); | ||
const DataValue = NullaryType('DataValue', R.complement(R.isNil)); | ||
@@ -33,0 +34,0 @@ |
@@ -1,4 +0,2 @@ | ||
import R from 'ramda'; | ||
import chai, { assert } from 'chai'; | ||
import chaiString from 'chai-string'; | ||
import { assert } from 'chai'; | ||
import * as T from '../src/templates'; | ||
@@ -9,11 +7,5 @@ | ||
import configFixture from './fixtures/config.cpp'; | ||
import patchContextFixture from './fixtures/patchContext.cpp'; | ||
import implFixture from './fixtures/impl.cpp'; | ||
import implListFixture from './fixtures/implList.cpp'; | ||
import programFixture from './fixtures/program.cpp'; | ||
import programWithCustomValueFixture from './fixtures/program.customValue.cpp'; | ||
import projectFixture from './fixtures/project.cpp'; | ||
chai.use(chaiString); | ||
describe('xod-arduino templates', () => { | ||
@@ -45,5 +37,7 @@ let config; | ||
{ | ||
type: 'Number', | ||
pinKey: 'IN1', | ||
}, | ||
{ | ||
type: 'Number', | ||
pinKey: 'IN2', | ||
@@ -64,3 +58,3 @@ }, | ||
pinKey: 'OUT', | ||
value: null, | ||
value: 42, | ||
}, | ||
@@ -94,18 +88,8 @@ ], | ||
const result = T.renderConfig(config); | ||
assert.equalIgnoreSpaces(result, configFixture); | ||
assert.strictEqual(result, configFixture); | ||
}); | ||
it('patchContext should render properly', () => { | ||
const result = T.renderPatchContext(patches[0]); | ||
assert.equalIgnoreSpaces(result, patchContextFixture); | ||
}); | ||
it('implementation should render properly', () => { | ||
const result = T.renderImpl(patches[0]); | ||
assert.equalIgnoreSpaces(result, implFixture); | ||
}); | ||
it('implementation list should render properly', () => { | ||
const result = T.renderImplList(patches); | ||
assert.equalIgnoreSpaces(result, implListFixture); | ||
assert.strictEqual(result, implListFixture); | ||
}); | ||
@@ -115,21 +99,4 @@ | ||
const result = T.renderProgram(project.topology, nodes); | ||
assert.equalIgnoreSpaces(result, programFixture); | ||
assert.strictEqual(result, programFixture); | ||
}); | ||
it('value of pin from patch should be overwritten by value from node', () => { | ||
const nodePinValueLens = R.compose( | ||
R.lensIndex(0), | ||
R.lensProp('outputs'), | ||
R.lensIndex(0), | ||
R.lensProp('value') | ||
); | ||
const newNodes = R.set(nodePinValueLens, 5, nodes); | ||
const result = T.renderProgram(project.topology, newNodes); | ||
assert.equalIgnoreSpaces(result, programWithCustomValueFixture); | ||
}); | ||
it('should render everything and glue parts properly', () => { | ||
const result = T.renderProject(project); | ||
assert.equalIgnoreSpaces(result, projectFixture); | ||
}); | ||
}); |
@@ -1,24 +0,36 @@ | ||
import chai, { assert } from 'chai'; | ||
import chaiString from 'chai-string'; | ||
import fs from 'fs'; | ||
import path from 'path'; | ||
import R from 'ramda'; | ||
import { assert } from 'chai'; | ||
import { foldEither } from 'xod-func-tools'; | ||
import { explode } from 'xod-func-tools'; | ||
import { loadProject } from 'xod-fs'; | ||
import transpile from '../src/transpiler'; | ||
import blinkProject from './fixtures/blink.project.json'; | ||
import blinkCpp from './fixtures/blink.cpp'; | ||
// Returns patch relative to repo’s `workspace` subdir | ||
const wsPath = (...subpath) => path.resolve(__dirname, '../../../workspace', ...subpath); | ||
chai.use(chaiString); | ||
const testFixture = (projName) => { | ||
const expectedCpp = fs.readFileSync(wsPath(projName, '__fixtures__/arduino.cpp'), 'utf-8'); | ||
return loadProject(wsPath(projName)) | ||
.then(transpile(R.__, '@/main')) | ||
.then(explode) | ||
.then(cpp => | ||
assert.strictEqual(cpp, expectedCpp, 'expected and actual C++ don’t match') | ||
); | ||
}; | ||
describe('xod-arduino transpiler (end-to-end test)', () => { | ||
it('should return Either.Right with C++ code', () => { | ||
foldEither( | ||
(err) => { throw err; }, | ||
cpp => assert.equalIgnoreSpaces(cpp, blinkCpp), | ||
transpile(blinkProject, '@/main') | ||
); | ||
describe('xod-arduino transpiler', () => { | ||
describe('correctly transpiles workspace fixture', () => { | ||
specify('blink', () => testFixture('blink')); | ||
specify('two-button-switch', () => testFixture('two-button-switch')); | ||
specify('lcd-time', () => testFixture('lcd-time')); | ||
}); | ||
it('should return Either.Left with error if entry-point patch not found', () => { | ||
const r = transpile(blinkProject, '@/non-existing-patch'); | ||
assert.equal(r.isLeft, true); | ||
}); | ||
it('returns error for non-existing-patch entry point', | ||
() => | ||
loadProject(wsPath('blink')) | ||
.then(transpile(R.__, '@/non-existing-patch')) | ||
.then(result => assert.ok(result.isLeft)) | ||
); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
601155
36
1519
8
2
- Removedhm-def@0.1.2(transitive)
Updatedhm-def@^0.2.0
Updatedxod-func-tools@^0.1.3
Updatedxod-project@^0.1.3