xod-arduino
Advanced tools
Comparing version 0.31.0 to 0.31.1
@@ -33,5 +33,5 @@ 'use strict'; | ||
/* babel-plugin-inline-import '../platform/configuration.tpl.cpp' */var configTpl = '{{!-- Template for program configuration block --}}\n{{!-- Accepts the Config context --}}\n\n/*=============================================================================\n *\n *\n * Configuration\n *\n *\n =============================================================================*/\n\n// Uncomment to turn on debug of the program\n{{#unless XOD_DEBUG}}//{{/unless}}#define XOD_DEBUG\n\n// Uncomment to trace the program runtime in the Serial Monitor\n//#define XOD_DEBUG_ENABLE_TRACE\n\n\n// Uncomment to make possible simulation of the program\n{{#unless XOD_SIMULATION}}//{{/unless}}#define XOD_SIMULATION\n\n#ifdef XOD_SIMULATION\n#include <WasmSerial.h>\n#define XOD_DEBUG_SERIAL WasmSerial\n#else\n#define XOD_DEBUG_SERIAL DEBUG_SERIAL\n#endif\n'; | ||
/* babel-plugin-inline-import '../platform/patchContext.tpl.cpp' */var patchContextTpl = '{{!-- Template for GENERATED_CODE token inside each patch implementation --}}\n{{!-- Accepts TPatch context --}}\n\n{{#if raisesErrors}}\nunion NodeErrors {\n struct {\n {{#each outputs}}\n bool output_{{ pinKey }} : 1;\n {{/each}}\n };\n\n ErrorFlags flags;\n};\n{{/if}}\n\nstruct Node {\n {{#if raisesErrors}}\n NodeErrors errors;\n {{/if}}\n {{#if usesTimeouts}}\n TimeMs timeoutAt;\n {{/if}}\n {{#each outputs}}\n {{ cppType type }} output_{{ pinKey }};\n {{/each}}\n State state;\n};\n\n{{#each inputs}}\nstruct input_{{ pinKey }} { };\n{{/each}}\n{{#each outputs}}\nstruct output_{{ pinKey }} { };\n{{/each}}\n\ntemplate<typename PinT> struct ValueType { using T = void; };\n{{#each inputs}}\ntemplate<> struct ValueType<input_{{ pinKey }}> { using T = {{ cppType type }}; };\n{{/each}}\n{{#each outputs}}\ntemplate<> struct ValueType<output_{{ pinKey }}> { using T = {{ cppType type }}; };\n{{/each}}\n\n\nstruct ContextObject {\n Node* _node;\n {{#if catchesErrors}}\n {{#each inputs}}\n uint8_t _error_input_{{ pinKey }};\n {{/each}}\n {{/if}}\n {{#if usesNodeId}}\n uint16_t _nodeId;\n {{/if}}\n\n {{#each inputs}}\n {{ cppType type }} _input_{{ pinKey }};\n {{/each}}\n\n {{#eachDirtyablePin inputs}}\n bool _isInputDirty_{{ pinKey }};\n {{/eachDirtyablePin}}\n\n {{!--\n // Constants do not store dirtieness. They are never dirty\n // except the very first run\n --}}\n {{#eachDirtyablePin outputs}}\n bool _isOutputDirty_{{ pinKey }} : 1;\n {{/eachDirtyablePin}}\n};\n\nusing Context = ContextObject*;\n\ntemplate<typename PinT> typename ValueType<PinT>::T getValue(Context ctx) {\n static_assert(always_false<PinT>::value,\n "Invalid pin descriptor. Expected one of:" \\\n "{{#each inputs}} input_{{pinKey}}{{/each}}" \\\n "{{#each outputs}} output_{{pinKey}}{{/each}}");\n}\n\n{{#each inputs}}\ntemplate<> {{ cppType type }} getValue<input_{{ pinKey }}>(Context ctx) {\n return ctx->_input_{{ pinKey }};\n}\n{{/each}}\n{{#each outputs}}\ntemplate<> {{ cppType type }} getValue<output_{{ pinKey }}>(Context ctx) {\n return ctx->_node->output_{{ pinKey }};\n}\n{{/each}}\n\ntemplate<typename InputT> bool isInputDirty(Context ctx) {\n static_assert(always_false<InputT>::value,\n "Invalid input descriptor. Expected one of:" \\\n "{{#eachDirtyablePin inputs}} input_{{pinKey}}{{/eachDirtyablePin}}");\n return false;\n}\n\n{{#eachDirtyablePin inputs}}\ntemplate<> bool isInputDirty<input_{{ pinKey }}>(Context ctx) {\n return ctx->_isInputDirty_{{ pinKey }};\n}\n{{/eachDirtyablePin}}\n\ntemplate<typename OutputT> void emitValue(Context ctx, typename ValueType<OutputT>::T val) {\n static_assert(always_false<OutputT>::value,\n "Invalid output descriptor. Expected one of:" \\\n "{{#each outputs}} output_{{pinKey}}{{/each}}");\n}\n\n{{#each outputs}}\ntemplate<> void emitValue<output_{{ pinKey }}>(Context ctx, {{ cppType type }} val) {\n ctx->_node->output_{{ pinKey }} = val;\n {{#if isDirtyable}}\n ctx->_isOutputDirty_{{ pinKey }} = true;\n {{/if}}\n {{#if ../raisesErrors}}\n ctx->_node->errors.output_{{ pinKey }} = false;\n {{/if}}\n}\n{{/each}}\n\nState* getState(Context ctx) {\n return &ctx->_node->state;\n}\n\n{{#if usesNodeId}}\nuint16_t getNodeId(Context ctx) {\n return ctx->_nodeId;\n}\n{{/if}}\n\n\n{{#if raisesErrors}}\ntemplate<typename OutputT> void raiseError(Context ctx) {\n static_assert(always_false<OutputT>::value,\n "Invalid output descriptor. Expected one of:" \\\n "{{#each outputs}} output_{{pinKey}}{{/each}}");\n}\n\n{{#each outputs}}\ntemplate<> void raiseError<output_{{ pinKey }}>(Context ctx) {\n ctx->_node->errors.output_{{ pinKey }} = true;\n {{#if isDirtyable}}\n ctx->_isOutputDirty_{{ pinKey }} = true;\n {{/if}}\n}\n{{/each}}\n\nvoid raiseError(Context ctx) {\n {{#each outputs}}\n ctx->_node->errors.output_{{ pinKey }} = true;\n {{#if isDirtyable}}\n ctx->_isOutputDirty_{{ pinKey }} = true;\n {{/if}}\n {{/each}}\n}\n\n{{/if}}\n\n\n{{#if catchesErrors}}\n\ntemplate<typename InputT> uint8_t getError(Context ctx) {\n static_assert(always_false<InputT>::value,\n "Invalid input descriptor. Expected one of:" \\\n "{{#each inputs}} input_{{pinKey}}{{/each}}");\n return 0;\n}\n\n{{#each inputs}}\ntemplate<> uint8_t getError<input_{{ pinKey }}>(Context ctx) {\n return ctx->_error_input_{{ pinKey }};\n}\n{{/each}}\n{{/if}}\n'; | ||
/* babel-plugin-inline-import '../platform/patchContext.tpl.cpp' */var patchContextTpl = '{{!-- Template for GENERATED_CODE token inside each patch implementation --}}\n{{!-- Accepts TPatch context --}}\n\n{{#if raisesErrors}}\nunion NodeErrors {\n struct {\n {{#each outputs}}\n bool output_{{ pinKey }} : 1;\n {{/each}}\n };\n\n ErrorFlags flags;\n};\n{{/if}}\n\nstruct Node {\n {{#if raisesErrors}}\n NodeErrors errors;\n {{/if}}\n {{#if usesTimeouts}}\n TimeMs timeoutAt;\n {{/if}}\n {{#each outputs}}\n {{ cppType type }} output_{{ pinKey }};\n {{/each}}\n State state;\n};\n\n{{#each inputs}}\nstruct input_{{ pinKey }} { };\n{{/each}}\n{{#each outputs}}\nstruct output_{{ pinKey }} { };\n{{/each}}\n\ntemplate<typename PinT> struct ValueType { using T = void; };\n{{#each inputs}}\ntemplate<> struct ValueType<input_{{ pinKey }}> { using T = {{ cppType type }}; };\n{{/each}}\n{{#each outputs}}\ntemplate<> struct ValueType<output_{{ pinKey }}> { using T = {{ cppType type }}; };\n{{/each}}\n\n\nstruct ContextObject {\n Node* _node;\n {{#if catchesErrors}}\n {{#each inputs}}\n uint8_t _error_input_{{ pinKey }};\n {{/each}}\n {{/if}}\n {{#if usesNodeId}}\n uint16_t _nodeId;\n {{/if}}\n\n {{#each inputs}}\n {{ cppType type }} _input_{{ pinKey }};\n {{/each}}\n\n {{#eachDirtyablePin inputs}}\n bool _isInputDirty_{{ pinKey }};\n {{/eachDirtyablePin}}\n\n {{!--\n // Constants do not store dirtieness. They are never dirty\n // except the very first run\n --}}\n {{#eachDirtyablePin outputs}}\n bool _isOutputDirty_{{ pinKey }} : 1;\n {{/eachDirtyablePin}}\n};\n\nusing Context = ContextObject*;\n\ntemplate<typename PinT> typename ValueType<PinT>::T getValue(Context ctx) {\n static_assert(always_false<PinT>::value,\n "Invalid pin descriptor. Expected one of:" \\\n "{{#each inputs}} input_{{pinKey}}{{/each}}" \\\n "{{#each outputs}} output_{{pinKey}}{{/each}}");\n}\n\n{{#each inputs}}\ntemplate<> {{ cppType type }} getValue<input_{{ pinKey }}>(Context ctx) {\n return ctx->_input_{{ pinKey }};\n}\n{{/each}}\n{{#each outputs}}\ntemplate<> {{ cppType type }} getValue<output_{{ pinKey }}>(Context ctx) {\n return ctx->_node->output_{{ pinKey }};\n}\n{{/each}}\n\ntemplate<typename InputT> bool isInputDirty(Context ctx) {\n static_assert(always_false<InputT>::value,\n "Invalid input descriptor. Expected one of:" \\\n "{{#eachDirtyablePin inputs}} input_{{pinKey}}{{/eachDirtyablePin}}");\n return false;\n}\n\n{{#eachDirtyablePin inputs}}\ntemplate<> bool isInputDirty<input_{{ pinKey }}>(Context ctx) {\n return ctx->_isInputDirty_{{ pinKey }};\n}\n{{/eachDirtyablePin}}\n\ntemplate<typename OutputT> void emitValue(Context ctx, typename ValueType<OutputT>::T val) {\n static_assert(always_false<OutputT>::value,\n "Invalid output descriptor. Expected one of:" \\\n "{{#each outputs}} output_{{pinKey}}{{/each}}");\n}\n\n{{#each outputs}}\ntemplate<> void emitValue<output_{{ pinKey }}>(Context ctx, {{ cppType type }} val) {\n ctx->_node->output_{{ pinKey }} = val;\n {{#if isDirtyable}}\n ctx->_isOutputDirty_{{ pinKey }} = true;\n {{/if}}\n {{#if ../raisesErrors}}\n {{#if ../isDefer}}if (isEarlyDeferPass()) {{/if}}ctx->_node->errors.output_{{ pinKey }} = false;\n {{/if}}\n}\n{{/each}}\n\nState* getState(Context ctx) {\n return &ctx->_node->state;\n}\n\n{{#if usesNodeId}}\nuint16_t getNodeId(Context ctx) {\n return ctx->_nodeId;\n}\n{{/if}}\n\n\n{{#if raisesErrors}}\ntemplate<typename OutputT> void raiseError(Context ctx) {\n static_assert(always_false<OutputT>::value,\n "Invalid output descriptor. Expected one of:" \\\n "{{#each outputs}} output_{{pinKey}}{{/each}}");\n}\n\n{{#each outputs}}\ntemplate<> void raiseError<output_{{ pinKey }}>(Context ctx) {\n ctx->_node->errors.output_{{ pinKey }} = true;\n {{#if isDirtyable}}\n ctx->_isOutputDirty_{{ pinKey }} = true;\n {{/if}}\n}\n{{/each}}\n\nvoid raiseError(Context ctx) {\n {{#each outputs}}\n ctx->_node->errors.output_{{ pinKey }} = true;\n {{#if isDirtyable}}\n ctx->_isOutputDirty_{{ pinKey }} = true;\n {{/if}}\n {{/each}}\n}\n\n{{/if}}\n\n\n{{#if catchesErrors}}\n\ntemplate<typename InputT> uint8_t getError(Context ctx) {\n static_assert(always_false<InputT>::value,\n "Invalid input descriptor. Expected one of:" \\\n "{{#each inputs}} input_{{pinKey}}{{/each}}");\n return 0;\n}\n\n{{#each inputs}}\ntemplate<> uint8_t getError<input_{{ pinKey }}>(Context ctx) {\n return ctx->_error_input_{{ pinKey }};\n}\n{{/each}}\n{{/if}}\n'; | ||
/* babel-plugin-inline-import '../platform/implList.tpl.cpp' */var implListTpl = '{{!-- Template for program graph --}}\n{{!-- Accepts the context with list of TPatch --}}\n/*=============================================================================\n *\n *\n * Native node implementations\n *\n *\n =============================================================================*/\n\nnamespace xod {\n\n{{#each this}}\n{{#unless isConstant}}\n//-----------------------------------------------------------------------------\n// {{ patchPath }} implementation\n//-----------------------------------------------------------------------------\nnamespace {{ns this }} {\n\n{{ implementation }}\n\n} // namespace {{ns this }}\n\n{{/unless}}\n{{/each}}\n} // namespace xod\n'; | ||
/* babel-plugin-inline-import '../platform/program.tpl.cpp' */var programTpl = '\n/*=============================================================================\n *\n *\n * Main loop components\n *\n *\n =============================================================================*/\n\nnamespace xod {\n\n// Define/allocate persistent storages (state, timeout, output data) for all nodes\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored "-Wmissing-field-initializers"\n{{#each nodes}}\n{{~ mergePins }}\n{{#each outputs }}\n{{ decltype type value }} node_{{ ../id }}_output_{{ pinKey }} = {{ cppValue type value }};\n{{/each}}\n{{/each}}\n\n#pragma GCC diagnostic pop\n\nstruct TransactionState {\n{{#each nodes}}\n{{#unless patch.isConstant}}\n bool node_{{id}}_isNodeDirty : 1;\n {{#each outputs}}\n bool node_{{ ../id }}_isOutputDirty_{{ pinKey }} : 1;\n {{/each}}\n {{#if (hasUpstreamErrorRaisers this)}}\n bool node_{{id}}_hasUpstreamError : 1;\n {{/if}}\n{{/unless}}\n{{/each}}\n TransactionState() {\n {{#each nodes}}\n {{#unless patch.isConstant}}\n node_{{id}}_isNodeDirty = true;\n {{#eachDirtyablePin outputs}}\n node_{{ ../id }}_isOutputDirty_{{ pinKey }} = {{ isDirtyOnBoot }};\n {{/eachDirtyablePin}}\n {{/unless}}\n {{/each}}\n }\n};\n\nTransactionState g_transaction;\n\n{{#each nodes}}\n{{#unless patch.isConstant}}\n{{ ns patch }}::Node node_{{ id }} = {\n {{#if patch.raisesErrors}}\n {{#each outputs}}\n false, // {{ pinKey }} has no errors on start\n {{/each}}\n {{/if}}\n {{#if patch.usesTimeouts}}\n 0, // timeoutAt\n {{/if}}\n {{#each outputs}}\n node_{{ ../id }}_output_{{ pinKey }}, // output {{ pinKey }} default\n {{/each}}\n {{ ns patch }}::State() // state default\n};\n{{/unless}}\n{{/each}}\n\n#if defined(XOD_DEBUG) || defined(XOD_SIMULATION)\nnamespace detail {\nvoid handleTweaks() {\n if (XOD_DEBUG_SERIAL.available() > 0 && XOD_DEBUG_SERIAL.find("+XOD:", 5)) {\n int tweakedNodeId = XOD_DEBUG_SERIAL.parseInt();\n\n switch (tweakedNodeId) {\n {{#eachTweakNode nodes}}\n case {{ id }}:\n {\n {{#switchByTweakType patch.patchPath}}\n {{#case "number"}}\n node_{{ id }}.output_OUT = XOD_DEBUG_SERIAL.parseFloat();\n {{/case}}\n {{#case "byte"}}\n node_{{ id }}.output_OUT = XOD_DEBUG_SERIAL.parseInt();\n {{/case}}\n {{#case "pulse"}}\n node_{{ id }}.output_OUT = 1;\n {{/case}}\n {{#case "boolean"}}\n node_{{ id }}.output_OUT = (bool)XOD_DEBUG_SERIAL.parseInt();\n {{/case}}\n {{#case "string"}}\n XOD_DEBUG_SERIAL.read(); // consume the \':\' separator that was left after parsing node id\n size_t readChars = XOD_DEBUG_SERIAL.readBytesUntil(\'\\r\', node_{{ id }}.state.buff, {{getStringTweakLength patch.patchPath}});\n node_{{ id }}.state.buff[readChars] = \'\\0\';\n {{/case}}\n {{/switchByTweakType}}\n // to run evaluate and mark all downstream nodes as dirty\n g_transaction.node_{{ id }}_isNodeDirty = true;\n g_transaction.node_{{ id }}_isOutputDirty_OUT = true;\n }\n break;\n\n {{/eachTweakNode}}\n }\n\n XOD_DEBUG_SERIAL.find(\'\\n\');\n }\n}\n} // namespace detail\n#endif\n\nvoid runTransaction() {\n g_transactionTime = millis();\n\n XOD_TRACE_F("Transaction started, t=");\n XOD_TRACE_LN(g_transactionTime);\n\n#if defined(XOD_DEBUG) || defined(XOD_SIMULATION)\n detail::handleTweaks();\n#endif\n\n // Check for timeouts\n {{#eachNodeUsingTimeouts nodes}}\n g_transaction.node_{{id}}_isNodeDirty |= detail::isTimedOut(&node_{{id}});\n {{/eachNodeUsingTimeouts}}\n\n // defer-* nodes are always at the very bottom of the graph, so no one will\n // recieve values emitted by them. We must evaluate them before everybody\n // else to give them a chance to emit values.\n //\n // If trigerred, keep only output dirty, not the node itself, so it will\n // evaluate on the regular pass only if it pushed a new value again.\n {{#eachDeferNode nodes}}\n {\n if (g_transaction.node_{{id}}_isNodeDirty) {\n {{#eachInputPinWithUpstreamRaisers inputs}}\n bool error_input_{{ pinKey }} = false;\n {{#each upstreamErrorRaisers}}\n error_input_{{ ../pinKey }} |= node_{{ nodeId }}.errors.output_{{ pinKey }};\n {{/each}}\n {{/eachInputPinWithUpstreamRaisers}}\n\n XOD_TRACE_F("Trigger defer node #");\n XOD_TRACE_LN({{ id }});\n\n {{ns patch }}::ContextObject ctxObj;\n ctxObj._node = &node_{{ id }};\n ctxObj._isInputDirty_IN = false;\n ctxObj._error_input_IN = 0;\n\n {{#each inputs}}\n ctxObj._error_input_{{ pinKey }} = {{#if (hasUpstreamErrorRaisers this)~}}\n error_input_{{ pinKey }}\n {{~else~}}\n 0\n {{~/if~}};\n {{/each}}\n\n // initialize temporary output dirtyness state in the context,\n // where it can be modified from `raiseError` and `emitValue`\n {{#eachDirtyablePin outputs}}\n ctxObj._isOutputDirty_{{ pinKey }} = false;\n {{/eachDirtyablePin}}\n\n {{ns patch }}::NodeErrors previousErrors = node_{{ id }}.errors;\n\n {{!--\n // \u0421lean errors from pulse outputs\n --}}\n {{#eachPulseOutput patch.outputs}}\n node_{{ ../id }}.errors.output_{{ pinKey }} = false;\n {{/eachPulseOutput}}\n\n {{ ns patch }}::evaluate(&ctxObj);\n\n // transfer possibly modified dirtiness state from context to g_transaction\n {{#eachDirtyablePin outputs}}\n g_transaction.node_{{ ../id }}_isOutputDirty_{{ pinKey }} = ctxObj._isOutputDirty_{{ pinKey }};\n {{/eachDirtyablePin}}\n\n if (previousErrors.flags != node_{{ id }}.errors.flags) {\n detail::printErrorToDebugSerial({{ id }}, node_{{ id }}.errors.flags);\n\n // if an error was just raised or cleared from an output,\n // mark nearest downstream error catchers as dirty\n {{#each outputs}}\n if (node_{{ ../id }}.errors.output_{{ pinKey }} != previousErrors.output_{{ pinKey }}) {\n {{#each nearestDownstreamCatchers}}\n g_transaction.node_{{this}}_isNodeDirty = true;\n {{/each}}\n }\n {{/each}}\n\n {{#eachPulseOutput outputs}}\n // if a pulse output was cleared from error, mark downstream nodes as dirty\n // (no matter if a pulse was emitted or not)\n if (previousErrors.output_{{ pinKey }} && !node_{{ ../id }}.errors.output_{{ pinKey }}) {\n {{#each to}}\n g_transaction.node_{{this}}_isNodeDirty = true;\n {{/each}}\n }\n {{/eachPulseOutput}}\n }\n\n // mark downstream nodes dirty\n {{#each outputs }}\n {{#if isDirtyable ~}}\n {{#each to}}\n g_transaction.node_{{ this }}_isNodeDirty |= g_transaction.node_{{ ../../id }}_isOutputDirty_{{ ../pinKey }} || node_{{ ../../id }}.errors.flags;\n {{/each}}\n {{else}}\n {{#each to}}\n g_transaction.node_{{ this }}_isNodeDirty = true;\n {{/each}}\n {{/if}}\n {{/each}}\n\n g_transaction.node_{{id}}_isNodeDirty = false;\n detail::clearTimeout(&node_{{ id }});\n }\n\n // propagate the error hold by the defer node\n if (node_{{ id }}.errors.flags) {\n {{#each outputs}}\n if (node_{{ ../id }}.errors.output_{{ pinKey }}) {\n {{#each to}}\n g_transaction.node_{{this}}_hasUpstreamError = true;\n {{/each}}\n }\n {{/each}}\n }\n }\n {{/eachDeferNode}}\n\n // Evaluate all dirty nodes\n {{#eachNonConstantNode nodes}}\n { // {{ ns patch }} #{{ id }}\n {{#if (hasUpstreamErrorRaisers this)}}\n\n {{#if patch.catchesErrors}}\n if (g_transaction.node_{{id}}_isNodeDirty) {\n {{!--\n // finding upstream errors for each individual input\n // matters only if a node is a catcher\n --}}\n {{#eachInputPinWithUpstreamRaisers inputs}}\n bool error_input_{{ pinKey }} = false;\n {{#each upstreamErrorRaisers}}\n error_input_{{ ../pinKey }} |= node_{{ nodeId }}.errors.output_{{ pinKey }};\n {{/each}}\n {{/eachInputPinWithUpstreamRaisers}}\n {{else}}\n if (g_transaction.node_{{id}}_hasUpstreamError) {\n {{#each outputs}}\n {{#each to}}\n g_transaction.node_{{this}}_hasUpstreamError = true;\n {{/each}}\n {{/each}}\n } else if (g_transaction.node_{{id}}_isNodeDirty) {\n {{/if}}\n {{else}}\n if (g_transaction.node_{{id}}_isNodeDirty) {\n {{/if}}\n XOD_TRACE_F("Eval node #");\n XOD_TRACE_LN({{ id }});\n\n {{ns patch }}::ContextObject ctxObj;\n ctxObj._node = &node_{{ id }};\n {{#if patch.usesNodeId}}\n ctxObj._nodeId = {{ id }};\n {{/if}}\n\n {{#if patch.catchesErrors}}\n {{#each inputs}}\n ctxObj._error_input_{{ pinKey }} = {{#if (hasUpstreamErrorRaisers this)~}}\n error_input_{{ pinKey }}\n {{~else~}}\n 0\n {{~/if~}};\n {{/each}}\n {{/if}}\n\n // copy data from upstream nodes into context\n {{#eachLinkedInput inputs}}\n {{!--\n // We refer to node_42.output_FOO as data source in case\n // of a regular node and directly use node_42_output_VAL\n // initial value constexpr in case of a constant. It\u2019s\n // because store no Node structures at the global level\n --}}\n ctxObj._input_{{ pinKey }} = node_{{ fromNodeId }}\n {{~#if fromPatch.isConstant }}_{{else}}.{{/if~}}\n output_{{ fromPinKey }};\n {{/eachLinkedInput}}\n\n {{#eachNonlinkedInput inputs}}\n {{!--\n // Nonlinked pulse inputs are never dirty, all value types\n // are linked (to extracted constant nodes) and so will be\n // processed in another loop.\n --}}\n {{#if isDirtyable}}\n ctxObj._isInputDirty_{{ pinKey }} = false;\n {{/if}}\n {{/eachNonlinkedInput}}\n {{#eachLinkedInput inputs}}\n {{!--\n // Constants do not store dirtieness. They are never dirty\n // except the very first run\n --}}\n {{#if isDirtyable}}\n {{#if fromPatch.isConstant}}\n ctxObj._isInputDirty_{{ pinKey }} = g_isSettingUp;\n {{else if fromOutput.isDirtyable}}\n ctxObj._isInputDirty_{{ pinKey }} = g_transaction.node_{{fromNodeId}}_isOutputDirty_{{ fromPinKey }};\n {{else}}\n ctxObj._isInputDirty_{{ pinKey }} = true;\n {{/if}}\n {{/if}}\n {{/eachLinkedInput}}\n\n // initialize temporary output dirtyness state in the context,\n // where it can be modified from `raiseError` and `emitValue`\n {{#eachDirtyablePin outputs}}\n ctxObj._isOutputDirty_{{ pinKey }} = {{#if (isTweakNode ../this) ~}}\n g_transaction.node_{{ ../id }}_isOutputDirty_{{ pinKey }}\n {{~else~}}\n false\n {{~/if~}};\n {{/eachDirtyablePin}}\n\n {{#if patch.raisesErrors}}\n {{ns patch }}::NodeErrors previousErrors = node_{{ id }}.errors;\n\n {{#unless patch.isDefer}}\n {{!--\n // \u0421lean errors from pulse outputs\n --}}\n {{#eachPulseOutput patch.outputs}}\n node_{{ ../id }}.errors.output_{{ pinKey }} = false;\n {{/eachPulseOutput}}\n {{/unless}}\n {{/if}}\n\n {{ ns patch }}::evaluate(&ctxObj);\n\n // transfer possibly modified dirtiness state from context to g_transaction\n {{#eachDirtyablePin outputs}}\n g_transaction.node_{{ ../id }}_isOutputDirty_{{ pinKey }} = ctxObj._isOutputDirty_{{ pinKey }};\n {{/eachDirtyablePin}}\n\n {{#if patch.raisesErrors}}\n if (previousErrors.flags != node_{{ id }}.errors.flags) {\n detail::printErrorToDebugSerial({{ id }}, node_{{ id }}.errors.flags);\n\n // if an error was just raised or cleared from an output,\n // mark nearest downstream error catchers as dirty\n {{#each outputs}}\n if (node_{{ ../id }}.errors.output_{{ pinKey }} != previousErrors.output_{{ pinKey }}) {\n {{#each nearestDownstreamCatchers}}\n g_transaction.node_{{this}}_isNodeDirty = true;\n {{/each}}\n }\n {{/each}}\n\n // if a pulse output was cleared from error, mark downstream nodes as dirty\n // (no matter if a pulse was emitted or not)\n {{#eachPulseOutput outputs}}\n if (previousErrors.output_{{ pinKey }} && !node_{{ ../id }}.errors.output_{{ pinKey }}) {\n {{#each to}}\n g_transaction.node_{{this}}_isNodeDirty = true;\n {{/each}}\n }\n {{/eachPulseOutput}}\n }\n {{/if}}\n\n // mark downstream nodes dirty\n {{#each outputs }}\n {{#if isDirtyable ~}}\n {{#each to}}\n g_transaction.node_{{ this }}_isNodeDirty |= g_transaction.node_{{ ../../id }}_isOutputDirty_{{ ../pinKey }};\n {{/each}}\n {{else}}\n {{#each to}}\n g_transaction.node_{{this}}_isNodeDirty = true;\n {{/each}}\n {{/if}}\n {{/each}}\n }\n\n {{#if patch.raisesErrors}}\n // propagate errors hold by the node outputs\n if (node_{{ id }}.errors.flags) {\n {{#each outputs}}\n if (node_{{ ../id }}.errors.output_{{ pinKey }}) {\n {{#each to}}\n g_transaction.node_{{this}}_hasUpstreamError = true;\n {{/each}}\n }\n {{/each}}\n }\n {{/if}}\n }\n {{/eachNonConstantNode}}\n\n // Clear dirtieness and timeouts for all nodes and pins\n memset(&g_transaction, 0, sizeof(g_transaction));\n\n {{#eachNodeUsingTimeouts nodes}}\n detail::clearStaleTimeout(&node_{{ id }});\n {{/eachNodeUsingTimeouts}}\n\n XOD_TRACE_F("Transaction completed, t=");\n XOD_TRACE_LN(millis());\n}\n\n} // namespace xod\n'; | ||
/* babel-plugin-inline-import '../platform/program.tpl.cpp' */var programTpl = '\n/*=============================================================================\n *\n *\n * Main loop components\n *\n *\n =============================================================================*/\n\nnamespace xod {\n\n// Define/allocate persistent storages (state, timeout, output data) for all nodes\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored "-Wmissing-field-initializers"\n{{#each nodes}}\n{{~ mergePins }}\n{{#each outputs }}\n{{ decltype type value }} node_{{ ../id }}_output_{{ pinKey }} = {{ cppValue type value }};\n{{/each}}\n{{/each}}\n\n#pragma GCC diagnostic pop\n\nstruct TransactionState {\n{{#each nodes}}\n{{#unless patch.isConstant}}\n bool node_{{id}}_isNodeDirty : 1;\n {{#each outputs}}\n bool node_{{ ../id }}_isOutputDirty_{{ pinKey }} : 1;\n {{/each}}\n {{#if (needsHasUpstreamErrorFlag this)}}\n bool node_{{id}}_hasUpstreamError : 1;\n {{/if}}\n{{/unless}}\n{{/each}}\n TransactionState() {\n {{#each nodes}}\n {{#unless patch.isConstant}}\n node_{{id}}_isNodeDirty = true;\n {{#eachDirtyablePin outputs}}\n node_{{ ../id }}_isOutputDirty_{{ pinKey }} = {{ isDirtyOnBoot }};\n {{/eachDirtyablePin}}\n {{/unless}}\n {{/each}}\n }\n};\n\nTransactionState g_transaction;\n\n{{#each nodes}}\n{{#unless patch.isConstant}}\n{{ ns patch }}::Node node_{{ id }} = {\n {{#if patch.raisesErrors}}\n {{#each outputs}}\n false, // {{ pinKey }} has no errors on start\n {{/each}}\n {{/if}}\n {{#if patch.usesTimeouts}}\n 0, // timeoutAt\n {{/if}}\n {{#each outputs}}\n node_{{ ../id }}_output_{{ pinKey }}, // output {{ pinKey }} default\n {{/each}}\n {{ ns patch }}::State() // state default\n};\n{{/unless}}\n{{/each}}\n\n#if defined(XOD_DEBUG) || defined(XOD_SIMULATION)\nnamespace detail {\nvoid handleTweaks() {\n if (XOD_DEBUG_SERIAL.available() > 0 && XOD_DEBUG_SERIAL.find("+XOD:", 5)) {\n int tweakedNodeId = XOD_DEBUG_SERIAL.parseInt();\n\n switch (tweakedNodeId) {\n {{#eachTweakNode nodes}}\n case {{ id }}:\n {\n {{#switchByTweakType patch.patchPath}}\n {{#case "number"}}\n node_{{ id }}.output_OUT = XOD_DEBUG_SERIAL.parseFloat();\n {{/case}}\n {{#case "byte"}}\n node_{{ id }}.output_OUT = XOD_DEBUG_SERIAL.parseInt();\n {{/case}}\n {{#case "pulse"}}\n node_{{ id }}.output_OUT = 1;\n {{/case}}\n {{#case "boolean"}}\n node_{{ id }}.output_OUT = (bool)XOD_DEBUG_SERIAL.parseInt();\n {{/case}}\n {{#case "string"}}\n XOD_DEBUG_SERIAL.read(); // consume the \':\' separator that was left after parsing node id\n size_t readChars = XOD_DEBUG_SERIAL.readBytesUntil(\'\\r\', node_{{ id }}.state.buff, {{getStringTweakLength patch.patchPath}});\n node_{{ id }}.state.buff[readChars] = \'\\0\';\n {{/case}}\n {{/switchByTweakType}}\n // to run evaluate and mark all downstream nodes as dirty\n g_transaction.node_{{ id }}_isNodeDirty = true;\n g_transaction.node_{{ id }}_isOutputDirty_OUT = true;\n }\n break;\n\n {{/eachTweakNode}}\n }\n\n XOD_DEBUG_SERIAL.find(\'\\n\');\n }\n}\n} // namespace detail\n#endif\n\nvoid handleDefers() {\n {{#eachDeferNode nodes}}\n {\n if (g_transaction.node_{{id}}_isNodeDirty) {\n {{#eachInputPinWithUpstreamRaisers inputs}}\n bool error_input_{{ pinKey }} = false;\n {{#each upstreamErrorRaisers}}\n error_input_{{ ../pinKey }} |= node_{{ nodeId }}.errors.output_{{ pinKey }};\n {{/each}}\n {{/eachInputPinWithUpstreamRaisers}}\n\n XOD_TRACE_F("Trigger defer node #");\n XOD_TRACE_LN({{ id }});\n\n {{ns patch }}::ContextObject ctxObj;\n ctxObj._node = &node_{{ id }};\n {{#each inputs}}\n {{#if isDirtyable}}\n ctxObj._isInputDirty_{{ pinKey }} = false;\n {{/if}}\n {{/each}}\n\n {{#eachLinkedInput inputs}}\n {{!--\n // We refer to node_42.output_FOO as data source in case\n // of a regular node and directly use node_42_output_VAL\n // initial value constexpr in case of a constant. It\u2019s\n // because store no Node structures at the global level\n --}}\n ctxObj._input_{{ pinKey }} = node_{{ fromNodeId }}\n {{~#if fromPatch.isConstant }}_{{else}}.{{/if~}}\n output_{{ fromPinKey }};\n {{/eachLinkedInput}}\n\n {{#each inputs}}\n ctxObj._error_input_{{ pinKey }} = {{#if (hasUpstreamErrorRaisers this)~}}\n error_input_{{ pinKey }}\n {{~else~}}\n 0\n {{~/if~}};\n {{/each}}\n\n // initialize temporary output dirtyness state in the context,\n // where it can be modified from `raiseError` and `emitValue`\n {{#eachDirtyablePin outputs}}\n ctxObj._isOutputDirty_{{ pinKey }} = false;\n {{/eachDirtyablePin}}\n\n {{ns patch }}::NodeErrors previousErrors = node_{{ id }}.errors;\n\n {{!--\n // \u0421lean errors from pulse outputs\n --}}\n {{#eachPulseOutput patch.outputs}}\n node_{{ ../id }}.errors.output_{{ pinKey }} = false;\n {{/eachPulseOutput}}\n\n {{ ns patch }}::evaluate(&ctxObj);\n\n // transfer possibly modified dirtiness state from context to g_transaction\n {{#eachDirtyablePin outputs}}\n g_transaction.node_{{ ../id }}_isOutputDirty_{{ pinKey }} = ctxObj._isOutputDirty_{{ pinKey }};\n {{/eachDirtyablePin}}\n\n if (previousErrors.flags != node_{{ id }}.errors.flags) {\n detail::printErrorToDebugSerial({{ id }}, node_{{ id }}.errors.flags);\n\n // if an error was just raised or cleared from an output,\n // mark nearest downstream error catchers as dirty\n {{#each outputs}}\n if (node_{{ ../id }}.errors.output_{{ pinKey }} != previousErrors.output_{{ pinKey }}) {\n {{#each nearestDownstreamCatchers}}\n g_transaction.node_{{this}}_isNodeDirty = true;\n {{/each}}\n }\n {{/each}}\n\n {{#eachPulseOutput outputs}}\n // if a pulse output was cleared from error, mark downstream nodes as dirty\n // (no matter if a pulse was emitted or not)\n if (previousErrors.output_{{ pinKey }} && !node_{{ ../id }}.errors.output_{{ pinKey }}) {\n {{#each to}}\n g_transaction.node_{{this}}_isNodeDirty = true;\n {{/each}}\n }\n {{/eachPulseOutput}}\n }\n\n // mark downstream nodes dirty\n {{#each outputs }}\n {{#if isDirtyable ~}}\n {{#each to}}\n g_transaction.node_{{ this }}_isNodeDirty |= g_transaction.node_{{ ../../id }}_isOutputDirty_{{ ../pinKey }} || node_{{ ../../id }}.errors.flags;\n {{/each}}\n {{else}}\n {{#each to}}\n g_transaction.node_{{ this }}_isNodeDirty = true;\n {{/each}}\n {{/if}}\n {{/each}}\n\n g_transaction.node_{{id}}_isNodeDirty = false;\n detail::clearTimeout(&node_{{ id }});\n }\n\n // propagate the error hold by the defer node\n if (node_{{ id }}.errors.flags) {\n {{#each outputs}}\n if (node_{{ ../id }}.errors.output_{{ pinKey }}) {\n {{#each to}}\n g_transaction.node_{{this}}_hasUpstreamError = true;\n {{/each}}\n }\n {{/each}}\n }\n }\n {{/eachDeferNode}}\n}\n\nvoid runTransaction() {\n g_transactionTime = millis();\n\n XOD_TRACE_F("Transaction started, t=");\n XOD_TRACE_LN(g_transactionTime);\n\n#if defined(XOD_DEBUG) || defined(XOD_SIMULATION)\n detail::handleTweaks();\n#endif\n\n // Check for timeouts\n {{#eachNodeUsingTimeouts nodes}}\n g_transaction.node_{{id}}_isNodeDirty |= detail::isTimedOut(&node_{{id}});\n {{/eachNodeUsingTimeouts}}\n\n // defer-* nodes are always at the very bottom of the graph, so no one will\n // recieve values emitted by them. We must evaluate them before everybody\n // else to give them a chance to emit values.\n //\n // If trigerred, keep only output dirty, not the node itself, so it will\n // evaluate on the regular pass only if it receives a new value again.\n if (!isSettingUp()) {\n g_isEarlyDeferPass = true;\n handleDefers();\n g_isEarlyDeferPass = false;\n }\n\n // Evaluate all dirty nodes\n {{#eachNonConstantNode nodes}}\n { // {{ ns patch }} #{{ id }}\n {{#if (hasUpstreamErrorRaisers this)}}\n\n {{#if patch.catchesErrors}}\n if (g_transaction.node_{{id}}_isNodeDirty) {\n {{!--\n // finding upstream errors for each individual input\n // matters only if a node is a catcher\n --}}\n {{#eachInputPinWithUpstreamRaisers inputs}}\n bool error_input_{{ pinKey }} = false;\n {{#each upstreamErrorRaisers}}\n error_input_{{ ../pinKey }} |= node_{{ nodeId }}.errors.output_{{ pinKey }};\n {{/each}}\n {{/eachInputPinWithUpstreamRaisers}}\n {{else}}\n if (g_transaction.node_{{id}}_hasUpstreamError) {\n {{#each outputs}}\n {{#each to}}\n g_transaction.node_{{this}}_hasUpstreamError = true;\n {{/each}}\n {{/each}}\n } else if (g_transaction.node_{{id}}_isNodeDirty) {\n {{/if}}\n {{else}}\n if (g_transaction.node_{{id}}_isNodeDirty) {\n {{/if}}\n XOD_TRACE_F("Eval node #");\n XOD_TRACE_LN({{ id }});\n\n {{ns patch }}::ContextObject ctxObj;\n ctxObj._node = &node_{{ id }};\n {{#if patch.usesNodeId}}\n ctxObj._nodeId = {{ id }};\n {{/if}}\n\n {{#if patch.catchesErrors}}\n {{#each inputs}}\n ctxObj._error_input_{{ pinKey }} = {{#if (hasUpstreamErrorRaisers this)~}}\n error_input_{{ pinKey }}\n {{~else~}}\n 0\n {{~/if~}};\n {{/each}}\n {{/if}}\n\n // copy data from upstream nodes into context\n {{#eachLinkedInput inputs}}\n {{!--\n // We refer to node_42.output_FOO as data source in case\n // of a regular node and directly use node_42_output_VAL\n // initial value constexpr in case of a constant. It\u2019s\n // because store no Node structures at the global level\n --}}\n ctxObj._input_{{ pinKey }} = node_{{ fromNodeId }}\n {{~#if fromPatch.isConstant }}_{{else}}.{{/if~}}\n output_{{ fromPinKey }};\n {{/eachLinkedInput}}\n\n {{#eachNonlinkedInput inputs}}\n {{!--\n // Nonlinked pulse inputs are never dirty, all value types\n // are linked (to extracted constant nodes) and so will be\n // processed in another loop.\n --}}\n {{#if isDirtyable}}\n ctxObj._isInputDirty_{{ pinKey }} = false;\n {{/if}}\n {{/eachNonlinkedInput}}\n {{#eachLinkedInput inputs}}\n {{!--\n // Constants do not store dirtieness. They are never dirty\n // except the very first run\n --}}\n {{#if isDirtyable}}\n {{#if fromPatch.isConstant}}\n ctxObj._isInputDirty_{{ pinKey }} = g_isSettingUp;\n {{else if fromOutput.isDirtyable}}\n ctxObj._isInputDirty_{{ pinKey }} = g_transaction.node_{{fromNodeId}}_isOutputDirty_{{ fromPinKey }};\n {{else}}\n ctxObj._isInputDirty_{{ pinKey }} = true;\n {{/if}}\n {{/if}}\n {{/eachLinkedInput}}\n\n // initialize temporary output dirtyness state in the context,\n // where it can be modified from `raiseError` and `emitValue`\n {{#eachDirtyablePin outputs}}\n ctxObj._isOutputDirty_{{ pinKey }} = {{#if (isTweakNode ../this) ~}}\n g_transaction.node_{{ ../id }}_isOutputDirty_{{ pinKey }}\n {{~else~}}\n false\n {{~/if~}};\n {{/eachDirtyablePin}}\n\n {{#if patch.raisesErrors}}\n {{ns patch }}::NodeErrors previousErrors = node_{{ id }}.errors;\n\n {{#unless patch.isDefer}}\n {{!--\n // \u0421lean errors from pulse outputs\n --}}\n {{#eachPulseOutput patch.outputs}}\n node_{{ ../id }}.errors.output_{{ pinKey }} = false;\n {{/eachPulseOutput}}\n {{/unless}}\n {{/if}}\n\n {{ ns patch }}::evaluate(&ctxObj);\n\n // transfer possibly modified dirtiness state from context to g_transaction\n {{#eachDirtyablePin outputs}}\n g_transaction.node_{{ ../id }}_isOutputDirty_{{ pinKey }} = ctxObj._isOutputDirty_{{ pinKey }};\n {{/eachDirtyablePin}}\n\n {{#if patch.raisesErrors}}\n if (previousErrors.flags != node_{{ id }}.errors.flags) {\n detail::printErrorToDebugSerial({{ id }}, node_{{ id }}.errors.flags);\n\n // if an error was just raised or cleared from an output,\n // mark nearest downstream error catchers as dirty\n {{#each outputs}}\n if (node_{{ ../id }}.errors.output_{{ pinKey }} != previousErrors.output_{{ pinKey }}) {\n {{#each nearestDownstreamCatchers}}\n g_transaction.node_{{this}}_isNodeDirty = true;\n {{/each}}\n }\n {{/each}}\n\n // if a pulse output was cleared from error, mark downstream nodes as dirty\n // (no matter if a pulse was emitted or not)\n {{#eachPulseOutput outputs}}\n if (previousErrors.output_{{ pinKey }} && !node_{{ ../id }}.errors.output_{{ pinKey }}) {\n {{#each to}}\n g_transaction.node_{{this}}_isNodeDirty = true;\n {{/each}}\n }\n {{/eachPulseOutput}}\n }\n {{/if}}\n\n // mark downstream nodes dirty\n {{#each outputs }}\n {{#if isDirtyable ~}}\n {{#each to}}\n g_transaction.node_{{ this }}_isNodeDirty |= g_transaction.node_{{ ../../id }}_isOutputDirty_{{ ../pinKey }};\n {{/each}}\n {{else}}\n {{#each to}}\n g_transaction.node_{{this}}_isNodeDirty = true;\n {{/each}}\n {{/if}}\n {{/each}}\n }\n\n {{#if patch.raisesErrors}}\n // propagate errors hold by the node outputs\n if (node_{{ id }}.errors.flags) {\n {{#each outputs}}\n if (node_{{ ../id }}.errors.output_{{ pinKey }}) {\n {{#each to}}\n g_transaction.node_{{this}}_hasUpstreamError = true;\n {{/each}}\n }\n {{/each}}\n }\n {{/if}}\n }\n {{/eachNonConstantNode}}\n\n // Clear dirtieness and timeouts for all nodes and pins\n memset(&g_transaction, 0, sizeof(g_transaction));\n\n {{#eachNodeUsingTimeouts nodes}}\n detail::clearStaleTimeout(&node_{{ id }});\n {{/eachNodeUsingTimeouts}}\n\n XOD_TRACE_F("Transaction completed, t=");\n XOD_TRACE_LN(millis());\n}\n\n} // namespace xod\n'; | ||
/* babel-plugin-inline-import '../platform/preamble.h' */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// - Configuration section\n// - STL shim\n// - Immutable list classes and functions\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'; | ||
@@ -45,3 +45,3 @@ /* babel-plugin-inline-import '../platform/listViews.h' */var listViewsH = '/*=============================================================================\n *\n *\n * XOD-specific list/array implementations\n *\n *\n =============================================================================*/\n\n#ifndef XOD_LIST_H\n#define XOD_LIST_H\n\nnamespace xod {\nnamespace detail {\n\n/*\n * Cursors are used internaly by iterators and list views. They are not exposed\n * directly to a list consumer.\n *\n * The base `Cursor` is an interface which provides the bare minimum of methods\n * to facilitate a single iteration pass.\n */\ntemplate<typename T> class Cursor {\n public:\n virtual ~Cursor() { }\n virtual bool isValid() const = 0;\n virtual bool value(T* out) const = 0;\n virtual void next() = 0;\n};\n\ntemplate<typename T> class NilCursor : public Cursor<T> {\n public:\n virtual bool isValid() const { return false; }\n virtual bool value(T*) const { return false; }\n virtual void next() { }\n};\n\n} // namespace detail\n\n/*\n * Iterator is an object used to iterate a list once.\n *\n * Users create new iterators by calling `someList.iterate()`.\n * Iterators are created on stack and are supposed to have a\n * short live, e.g. for a duration of `for` loop or node\u2019s\n * `evaluate` function. Iterators can\u2019t be copied.\n *\n * Implemented as a pimpl pattern wrapper over the cursor.\n * Once created for a cursor, an iterator owns that cursor\n * and will delete the cursor object once destroyed itself.\n */\ntemplate<typename T>\nclass Iterator {\n public:\n static Iterator<T> nil() {\n return Iterator<T>(new detail::NilCursor<T>());\n }\n\n Iterator(detail::Cursor<T>* cursor)\n : _cursor(cursor)\n { }\n\n ~Iterator() {\n if (_cursor)\n delete _cursor;\n }\n\n Iterator(const Iterator& that) = delete;\n Iterator& operator=(const Iterator& that) = delete;\n\n Iterator(Iterator&& it)\n : _cursor(it._cursor)\n {\n it._cursor = nullptr;\n }\n\n Iterator& operator=(Iterator&& it) {\n auto tmp = it._cursor;\n it._cursor = _cursor;\n _cursor = tmp;\n return *this;\n }\n\n operator bool() const { return _cursor->isValid(); }\n\n bool value(T* out) const {\n return _cursor->value(out);\n }\n\n T operator*() const {\n T out;\n _cursor->value(&out);\n return out;\n }\n\n Iterator& operator++() {\n _cursor->next();\n return *this;\n }\n\n private:\n detail::Cursor<T>* _cursor;\n};\n\n/*\n * An interface for a list view. A particular list view provides a new\n * kind of iteration over existing data. This way we can use list slices,\n * list concatenations, list rotations, etc without introducing new data\n * buffers. We just change the way already existing data is iterated.\n *\n * ListView is not exposed to a list user directly, it is used internally\n * by the List class. However, deriving a new ListView is necessary if you\n * make a new list/string processing node.\n */\ntemplate<typename T> class ListView {\n public:\n virtual Iterator<T> iterate() const = 0;\n};\n\n/*\n * The list as it seen by data consumers. Have a single method `iterate`\n * to create a new iterator.\n *\n * Implemented as pimpl pattern wrapper over a list view. Takes pointer\n * to a list view in constructor and expects the view will be alive for\n * the whole life time of the list.\n */\ntemplate<typename T> class List {\n public:\n constexpr List()\n : _view(nullptr)\n { }\n\n List(const ListView<T>* view)\n : _view(view)\n { }\n\n Iterator<T> iterate() const {\n return _view ? _view->iterate() : Iterator<T>::nil();\n }\n\n // pre 0.15.0 backward compatibility\n List* operator->() __attribute__ ((deprecated)) { return this; }\n const List* operator->() const __attribute__ ((deprecated)) { return this; }\n\n private:\n const ListView<T>* _view;\n};\n\n/*\n * A list view over an old good plain C array.\n *\n * Expects the array will be alive for the whole life time of the\n * view.\n */\ntemplate<typename T> class PlainListView : public ListView<T> {\n public:\n class Cursor : public detail::Cursor<T> {\n public:\n Cursor(const PlainListView* owner)\n : _owner(owner)\n , _idx(0)\n { }\n\n bool isValid() const override {\n return _idx < _owner->_len;\n }\n\n bool value(T* out) const override {\n if (!isValid())\n return false;\n *out = _owner->_data[_idx];\n return true;\n }\n\n void next() override { ++_idx; }\n\n private:\n const PlainListView* _owner;\n size_t _idx;\n };\n\n public:\n PlainListView(const T* data, size_t len)\n : _data(data)\n , _len(len)\n { }\n\n virtual Iterator<T> iterate() const override {\n return Iterator<T>(new Cursor(this));\n }\n\n private:\n friend class Cursor;\n const T* _data;\n size_t _len;\n};\n\n/*\n * A list view over a null-terminated C-String.\n *\n * Expects the char buffer will be alive for the whole life time of the view.\n * You can use string literals as a buffer, since they are persistent for\n * the program execution time.\n */\nclass CStringView : public ListView<char> {\n public:\n class Cursor : public detail::Cursor<char> {\n public:\n Cursor(const char* str)\n : _ptr(str)\n { }\n\n bool isValid() const override {\n return (bool)*_ptr;\n }\n\n bool value(char* out) const override {\n *out = *_ptr;\n return (bool)*_ptr;\n }\n\n void next() override { ++_ptr; }\n\n private:\n const char* _ptr;\n };\n\n public:\n CStringView(const char* str = nullptr)\n : _str(str)\n { }\n\n CStringView& operator=(const CStringView& rhs) {\n _str = rhs._str;\n return *this;\n }\n\n virtual Iterator<char> iterate() const override {\n return _str ? Iterator<char>(new Cursor(_str)) : Iterator<char>::nil();\n }\n\n private:\n friend class Cursor;\n const char* _str;\n};\n\n/*\n * A list view over two other lists (Left and Right) which first iterates the\n * left one, and when exhausted, iterates the right one.\n *\n * Expects both Left and Right to be alive for the whole view life time.\n */\ntemplate<typename T> class ConcatListView : public ListView<T> {\n public:\n class Cursor : public detail::Cursor<T> {\n public:\n Cursor(Iterator<T>&& left, Iterator<T>&& right)\n : _left(std::move(left))\n , _right(std::move(right))\n { }\n\n bool isValid() const override {\n return _left || _right;\n }\n\n bool value(T* out) const override {\n return _left.value(out) || _right.value(out);\n }\n\n void next() override {\n _left ? ++_left : ++_right;\n }\n\n private:\n Iterator<T> _left;\n Iterator<T> _right;\n };\n\n public:\n ConcatListView() { }\n\n ConcatListView(List<T> left, List<T> right)\n : _left(left)\n , _right(right)\n { }\n\n ConcatListView& operator=(const ConcatListView& rhs) {\n _left = rhs._left;\n _right = rhs._right;\n return *this;\n }\n\n virtual Iterator<T> iterate() const override {\n return Iterator<T>(new Cursor(_left.iterate(), _right.iterate()));\n }\n\n private:\n friend class Cursor;\n List<T> _left;\n List<T> _right;\n};\n\n//----------------------------------------------------------------------------\n// Text string helpers\n//----------------------------------------------------------------------------\n\nusing XString = List<char>;\n\n/*\n * List and list view in a single pack. An utility used to define constant\n * string literals in XOD.\n */\nclass XStringCString : public XString {\n public:\n XStringCString(const char* str)\n : XString(&_view)\n , _view(str)\n { }\n\n private:\n CStringView _view;\n};\n\n} // namespace xod\n\n#endif\n'; | ||
/* babel-plugin-inline-import '../platform/stl.h' */var stlH = '/*=============================================================================\n *\n *\n * STL shim. Provides implementation for vital std::* constructs\n *\n *\n =============================================================================*/\n\nnamespace xod {\nnamespace std {\n\ntemplate< class T > struct remove_reference {typedef T type;};\ntemplate< class T > struct remove_reference<T&> {typedef T type;};\ntemplate< class T > struct remove_reference<T&&> {typedef T type;};\n\ntemplate <class T>\ntypename remove_reference<T>::type&& move(T&& a) {\n return static_cast<typename remove_reference<T>::type&&>(a);\n}\n\n} // namespace std\n} // namespace xod\n'; | ||
/* babel-plugin-inline-import '../platform/runtime.cpp' */var runtimeCpp = '\n/*=============================================================================\n *\n *\n * Runtime\n *\n *\n =============================================================================*/\n\n//----------------------------------------------------------------------------\n// Debug routines\n//----------------------------------------------------------------------------\n// #ifndef DEBUG_SERIAL\n#if defined(XOD_DEBUG) && !defined(DEBUG_SERIAL)\n# define DEBUG_SERIAL Serial\n#endif\n\n#if defined(XOD_DEBUG) && defined(XOD_DEBUG_ENABLE_TRACE)\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// PGM space utilities\n//----------------------------------------------------------------------------\n#define pgm_read_nodeid(address) (pgm_read_word(address))\n\n/*\n * Workaround for bugs:\n * https://github.com/arduino/ArduinoCore-sam/pull/43\n * https://github.com/arduino/ArduinoCore-samd/pull/253\n * Remove after the PRs merge\n */\n#if !defined(ARDUINO_ARCH_AVR) && defined(pgm_read_ptr)\n# undef pgm_read_ptr\n# define pgm_read_ptr(addr) (*(const void **)(addr))\n#endif\n\nnamespace xod {\n//----------------------------------------------------------------------------\n// Global variables\n//----------------------------------------------------------------------------\n\nTimeMs g_transactionTime;\nbool g_isSettingUp;\n\n//----------------------------------------------------------------------------\n// Metaprogramming utilities\n//----------------------------------------------------------------------------\n\ntemplate<typename T> struct always_false {\n enum { value = 0 };\n};\n\n//----------------------------------------------------------------------------\n// Forward declarations\n//----------------------------------------------------------------------------\n\nTimeMs transactionTime();\nvoid runTransaction();\n\n//----------------------------------------------------------------------------\n// Engine (private API)\n//----------------------------------------------------------------------------\n\nnamespace detail {\n\ntemplate<typename NodeT>\nbool isTimedOut(const NodeT* node) {\n TimeMs t = node->timeoutAt;\n // TODO: deal with uint32 overflow\n return t && t < transactionTime();\n}\n\ntemplate<typename NodeT>\nvoid clearTimeout(NodeT* node) {\n node->timeoutAt = 0;\n}\n\ntemplate<typename NodeT>\nvoid clearStaleTimeout(NodeT* node) {\n if (isTimedOut(node))\n clearTimeout(node);\n}\n\nvoid printErrorToDebugSerial(uint16_t nodeId, ErrorFlags errorFlags) {\n#if defined(XOD_DEBUG) || defined(XOD_SIMULATION)\n XOD_DEBUG_SERIAL.print(F("+XOD_ERR:"));\n XOD_DEBUG_SERIAL.print(g_transactionTime);\n XOD_DEBUG_SERIAL.print(\':\');\n XOD_DEBUG_SERIAL.print(nodeId);\n XOD_DEBUG_SERIAL.print(\':\');\n XOD_DEBUG_SERIAL.print(errorFlags, DEC);\n XOD_DEBUG_SERIAL.print(\'\\r\');\n XOD_DEBUG_SERIAL.print(\'\\n\');\n#endif\n}\n\n} // namespace detail\n\n//----------------------------------------------------------------------------\n// Public API (can be used by native nodes\u2019 `evaluate` functions)\n//----------------------------------------------------------------------------\n\nTimeMs transactionTime() {\n return g_transactionTime;\n}\n\nbool isSettingUp() {\n return g_isSettingUp;\n}\n\ntemplate<typename ContextT>\nvoid setTimeout(ContextT* ctx, TimeMs timeout) {\n ctx->_node->timeoutAt = transactionTime() + timeout;\n}\n\ntemplate<typename ContextT>\nvoid clearTimeout(ContextT* ctx) {\n detail::clearTimeout(ctx->_node);\n}\n\ntemplate<typename ContextT>\nbool isTimedOut(const ContextT* ctx) {\n return detail::isTimedOut(ctx->_node);\n}\n\nbool isValidDigitalPort(uint8_t port) {\n#if defined(__AVR__) && defined(NUM_DIGITAL_PINS)\n return port < NUM_DIGITAL_PINS;\n#else\n return true;\n#endif\n}\n\nbool isValidAnalogPort(uint8_t port) {\n#if defined(__AVR__) && defined(NUM_ANALOG_INPUTS)\n return port >= A0 && port < A0 + NUM_ANALOG_INPUTS;\n#else\n return true;\n#endif\n}\n\n} // namespace xod\n\n//----------------------------------------------------------------------------\n// Entry point\n//----------------------------------------------------------------------------\nvoid setup() {\n // FIXME: looks like there is a rounding bug. Waiting for 100ms fights it\n delay(100);\n\n#if defined(XOD_DEBUG) || defined(XOD_SIMULATION)\n XOD_DEBUG_SERIAL.begin(115200);\n XOD_DEBUG_SERIAL.setTimeout(10);\n#endif\n XOD_TRACE_FLN("\\n\\nProgram started");\n\n xod::g_isSettingUp = true;\n xod::runTransaction();\n xod::g_isSettingUp = false;\n}\n\nvoid loop() {\n xod::runTransaction();\n}\n'; | ||
/* babel-plugin-inline-import '../platform/runtime.cpp' */var runtimeCpp = '\n/*=============================================================================\n *\n *\n * Runtime\n *\n *\n =============================================================================*/\n\n//----------------------------------------------------------------------------\n// Debug routines\n//----------------------------------------------------------------------------\n// #ifndef DEBUG_SERIAL\n#if defined(XOD_DEBUG) && !defined(DEBUG_SERIAL)\n# define DEBUG_SERIAL Serial\n#endif\n\n#if defined(XOD_DEBUG) && defined(XOD_DEBUG_ENABLE_TRACE)\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// PGM space utilities\n//----------------------------------------------------------------------------\n#define pgm_read_nodeid(address) (pgm_read_word(address))\n\n/*\n * Workaround for bugs:\n * https://github.com/arduino/ArduinoCore-sam/pull/43\n * https://github.com/arduino/ArduinoCore-samd/pull/253\n * Remove after the PRs merge\n */\n#if !defined(ARDUINO_ARCH_AVR) && defined(pgm_read_ptr)\n# undef pgm_read_ptr\n# define pgm_read_ptr(addr) (*(const void **)(addr))\n#endif\n\nnamespace xod {\n//----------------------------------------------------------------------------\n// Global variables\n//----------------------------------------------------------------------------\n\nTimeMs g_transactionTime;\nbool g_isSettingUp;\nbool g_isEarlyDeferPass;\n\n//----------------------------------------------------------------------------\n// Metaprogramming utilities\n//----------------------------------------------------------------------------\n\ntemplate<typename T> struct always_false {\n enum { value = 0 };\n};\n\n//----------------------------------------------------------------------------\n// Forward declarations\n//----------------------------------------------------------------------------\n\nTimeMs transactionTime();\nvoid runTransaction();\n\n//----------------------------------------------------------------------------\n// Engine (private API)\n//----------------------------------------------------------------------------\n\nnamespace detail {\n\ntemplate<typename NodeT>\nbool isTimedOut(const NodeT* node) {\n TimeMs t = node->timeoutAt;\n // TODO: deal with uint32 overflow\n return t && t < transactionTime();\n}\n\ntemplate<typename NodeT>\nvoid clearTimeout(NodeT* node) {\n node->timeoutAt = 0;\n}\n\ntemplate<typename NodeT>\nvoid clearStaleTimeout(NodeT* node) {\n if (isTimedOut(node))\n clearTimeout(node);\n}\n\nvoid printErrorToDebugSerial(uint16_t nodeId, ErrorFlags errorFlags) {\n#if defined(XOD_DEBUG) || defined(XOD_SIMULATION)\n XOD_DEBUG_SERIAL.print(F("+XOD_ERR:"));\n XOD_DEBUG_SERIAL.print(g_transactionTime);\n XOD_DEBUG_SERIAL.print(\':\');\n XOD_DEBUG_SERIAL.print(nodeId);\n XOD_DEBUG_SERIAL.print(\':\');\n XOD_DEBUG_SERIAL.print(errorFlags, DEC);\n XOD_DEBUG_SERIAL.print(\'\\r\');\n XOD_DEBUG_SERIAL.print(\'\\n\');\n#endif\n}\n\n} // namespace detail\n\n//----------------------------------------------------------------------------\n// Public API (can be used by native nodes\u2019 `evaluate` functions)\n//----------------------------------------------------------------------------\n\nTimeMs transactionTime() {\n return g_transactionTime;\n}\n\nbool isSettingUp() {\n return g_isSettingUp;\n}\n\nbool isEarlyDeferPass() {\n return g_isEarlyDeferPass;\n}\n\ntemplate<typename ContextT>\nvoid setTimeout(ContextT* ctx, TimeMs timeout) {\n ctx->_node->timeoutAt = transactionTime() + timeout;\n}\n\ntemplate<typename ContextT>\nvoid clearTimeout(ContextT* ctx) {\n detail::clearTimeout(ctx->_node);\n}\n\ntemplate<typename ContextT>\nbool isTimedOut(const ContextT* ctx) {\n return detail::isTimedOut(ctx->_node);\n}\n\nbool isValidDigitalPort(uint8_t port) {\n#if defined(__AVR__) && defined(NUM_DIGITAL_PINS)\n return port < NUM_DIGITAL_PINS;\n#else\n return true;\n#endif\n}\n\nbool isValidAnalogPort(uint8_t port) {\n#if defined(__AVR__) && defined(NUM_ANALOG_INPUTS)\n return port >= A0 && port < A0 + NUM_ANALOG_INPUTS;\n#else\n return true;\n#endif\n}\n\n} // namespace xod\n\n//----------------------------------------------------------------------------\n// Entry point\n//----------------------------------------------------------------------------\nvoid setup() {\n // FIXME: looks like there is a rounding bug. Waiting for 100ms fights it\n delay(100);\n\n#if defined(XOD_DEBUG) || defined(XOD_SIMULATION)\n XOD_DEBUG_SERIAL.begin(115200);\n XOD_DEBUG_SERIAL.setTimeout(10);\n#endif\n XOD_TRACE_FLN("\\n\\nProgram started");\n\n xod::g_isSettingUp = true;\n xod::runTransaction();\n xod::g_isSettingUp = false;\n}\n\nvoid loop() {\n xod::runTransaction();\n}\n'; | ||
@@ -189,6 +189,10 @@ // ============================================================================= | ||
_handlebars2.default.registerHelper('hasUpstreamErrorRaisers', function (nodeOrInput) { | ||
var hasUpstreamErrorRaisers = function hasUpstreamErrorRaisers(nodeOrInput) { | ||
return nodeOrInput.upstreamErrorRaisers.length > 0; | ||
}); | ||
}; | ||
_handlebars2.default.registerHelper('hasUpstreamErrorRaisers', hasUpstreamErrorRaisers); | ||
_handlebars2.default.registerHelper('needsHasUpstreamErrorFlag', R.either(hasUpstreamErrorRaisers, R.path(['patch', 'isDefer']))); | ||
var isTweakNode = R.pipe(R.path(['patch', 'patchPath']), XP.isTweakPath); | ||
@@ -195,0 +199,0 @@ |
{ | ||
"name": "xod-arduino", | ||
"version": "0.31.0", | ||
"version": "0.31.1", | ||
"description": "XOD project: Arduino transpiler", | ||
@@ -5,0 +5,0 @@ "scripts": { |
@@ -239,5 +239,10 @@ import * as R from 'ramda'; | ||
const hasUpstreamErrorRaisers = nodeOrInput => | ||
nodeOrInput.upstreamErrorRaisers.length > 0; | ||
Handlebars.registerHelper('hasUpstreamErrorRaisers', hasUpstreamErrorRaisers); | ||
Handlebars.registerHelper( | ||
'hasUpstreamErrorRaisers', | ||
nodeOrInput => nodeOrInput.upstreamErrorRaisers.length > 0 | ||
'needsHasUpstreamErrorFlag', | ||
R.either(hasUpstreamErrorRaisers, R.path(['patch', 'isDefer'])) | ||
); | ||
@@ -244,0 +249,0 @@ |
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
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
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
337665
2692