feat(xod-project, xod-arduino): introduce “read-only type” concept and evaluateTmpl API

Read-only types can not be redefined during runtime.
This means that their values can be used at compile time (for validation, buffer allocation etc).
If a user defines `evaluateTmpl` function in the patch implementation, read-only values will be passed to it as template arguments.
This commit is contained in:
Evgeny Kochetkov
2019-12-24 16:41:35 +03:00
parent b8eab9e7e8
commit d28ef8af49
8 changed files with 54 additions and 3 deletions

View File

@@ -114,10 +114,11 @@ template<> bool isInputDirty<input_{{ pinKey }}>(Context ctx) {
template<typename OutputT> void emitValue(Context ctx, typename ValueType<OutputT>::T val) {
static_assert(always_false<OutputT>::value,
"Invalid output descriptor. Expected one of:" \
"{{#each outputs}} output_{{pinKey}}{{/each}}");
"{{#each outputs}}{{#unless (isReadOnlyPin this)}} output_{{pinKey}}{{/unless}}{{/each}}");
}
{{#each outputs}}
{{#unless (isReadOnlyPin this)}}
template<> void emitValue<output_{{ pinKey }}>(Context ctx, {{ cppType type }} val) {
{{#unless (isPulse type)}}
ctx->_node->output_{{ pinKey }} = val;
@@ -129,6 +130,7 @@ template<> void emitValue<output_{{ pinKey }}>(Context ctx, {{ cppType type }} v
{{#if ../isDefer}}if (isEarlyDeferPass()) {{/if}}ctx->_node->errors.output_{{ pinKey }} = false;
{{/if}}
}
{{/unless}}
{{/each}}
State* getState(Context ctx) {
@@ -167,7 +169,7 @@ void raiseError(Context ctx) {
{{/each}}
}
{{/if}}
{{/if}}{{!-- raisesErrors --}}
{{#if catchesErrors}}
@@ -184,4 +186,4 @@ template<> uint8_t getError<input_{{ pinKey }}>(Context ctx) {
return ctx->_error_input_{{ pinKey }};
}
{{/each}}
{{/if}}
{{/if}}{{!-- catchesErrors --}}

View File

@@ -370,7 +370,19 @@ void runTransaction() {
{{/unless}}
{{/if}}
{{#if patch.implementsEvaluateTmpl}}
{{ ns patch }}::evaluateTmpl
{{~#if (containsReadOnlyInputs inputs)~}}
<
{{~#each (readOnlyInputs inputs)~}}
{{#if @index}}, {{/if}}node_{{ fromNodeId }}_output_{{ fromPinKey }}
{{~/each~}}
>
{{~/if~}}
(&ctxObj);
{{else}}
{{ ns patch }}::evaluate(&ctxObj);
{{/if}}
// transfer possibly modified dirtiness state from context to g_transaction
{{#eachDirtyablePin outputs}}

View File

@@ -127,6 +127,11 @@ let doesRaiseErrors = (code) =>
|. Code.lastPragmaEndis("error_raise")
|. Endis.toBoolean(Code.doesReferSymbol("raiseError", code));
let implementsEvaluateTmpl = code =>
code
|. Code.lastPragmaEndis("evaluate_tmpl")
|. Endis.toBoolean(Code.doesReferSymbol("evaluateTmpl", code));
let isDirtienessEnabled = (code, identifier) =>
code
|. Code.findXodPragmas

View File

@@ -50,6 +50,18 @@ let doesRaiseErrors: code => bool;
*/
let isDirtienessEnabled: (code, string) => bool;
/**
Returns whether a C++ code requires `evaluateTmpl`
instead of the regular `evaluate`. Prefers an explicit
declaration
#pragma XOD evaluate_tmpl enable
If no pragma found, looks for `evaluateTmpl` symbol in the code and returns true
if it is found.
*/
let implementsEvaluateTmpl: code => bool;
/**
Returns wether node declares itself as an error catcher
*/

View File

@@ -8,6 +8,8 @@ let isNodeIdEnabled = Directives.isNodeIdEnabled;
let doesRaiseErrors = Directives.doesRaiseErrors;
let implementsEvaluateTmpl = Directives.implementsEvaluateTmpl;
let areTimeoutsEnabled = Directives.areTimeoutsEnabled;
let stripCppComments = Directives.stripCppComments;

View File

@@ -267,6 +267,17 @@ Handlebars.registerHelper('isTweakNode', isTweakNode);
Handlebars.registerHelper('isPulse', R.equals(XP.PIN_TYPE.PULSE));
const isReadOnlyPin = ({ type }) => XP.READ_ONLY_TYPES.includes(type);
Handlebars.registerHelper('isReadOnlyPin', isReadOnlyPin);
Handlebars.registerHelper('readOnlyInputs', R.filter(isReadOnlyPin));
Handlebars.registerHelper(
'containsReadOnlyInputs',
R.pipe(R.filter(isReadOnlyPin), R.isEmpty, R.not)
);
// A helper to quickly introduce a new filtered {{each ...}} loop
function registerHandlebarsFilterLoopHelper(name, predicate) {
Handlebars.registerHelper(name, (list, block) =>

View File

@@ -23,6 +23,7 @@ import {
isNodeIdEnabled,
doesRaiseErrors,
isDirtienessEnabled,
implementsEvaluateTmpl,
doesCatchErrors,
findRequireUrls,
} from './directives';
@@ -160,6 +161,7 @@ const convertPatchToTPatch = def(
catchesErrors: doesCatchErrors(impl),
raisesErrors: doesRaiseErrors(impl),
usesNodeId: isNodeIdEnabled(impl),
implementsEvaluateTmpl: implementsEvaluateTmpl(impl),
};
return R.mergeAll([

View File

@@ -245,3 +245,8 @@ export const MANAGED_ATTACHMENT_TEMPLATES = {
[NOT_IMPLEMENTED_IN_XOD_PATH]: IMPL_TEMPLATE,
[TABTEST_MARKER_PATH]: TABTEST_TEMPLATE,
};
/**
* Types that are could not be redefined at runtime.
*/
export const READ_ONLY_TYPES = [PIN_TYPE.PORT];