# Form Runtime Engine & Semantics {#form-runtime-engine--semantics}


The MQUAD Form Engine is responsible for rendering, evaluating, and validating complex questionnaires on-device according to the **ODK XForms / XLSForm specification**.

---


## 1. Schema Pipeline & AST Representation {#1-schema-pipeline--ast-representation}


When a survey schema is pulled from `survey-service`, it is stored in the local Drift database as a version-pinned structure (`SurveySchemaEntity`).





```mermaid
graph LR
    JSON[Survey Version JSON] --> Parser[Schema Parser]
    Parser --> AST[QuestionSchema Hierarchy]
    AST --> Scope[Repeat & Group Scoping]
    Scope --> Engine[FormEngineNotifier]
    Engine --> Dispatch[QuestionWidgetFactory]
    Dispatch --> Widgets[Input Widgets]
```






### Core Schema Models: {#core-schema-models}
- **`QuestionSchema`**: Immutable definition of a survey item (type, name, label, hint, appearance, required, relevant expression, constraint expression, calculation formula, media URLs).
- **`ChoiceSchema`**: Allowed option for `select_one` or `select_multiple` questions, including name, label, and localized translations.
- **`FormState`**: Current runtime state holding user answers, calculation values, navigation history, bookmarks, and validation errors.

---


## 2. Dynamic Expression Evaluator {#2-dynamic-expression-evaluator}


The form engine includes an in-memory expression evaluator supporting XPath and XLSForm expression syntax:


### Supported Expression Categories: {#supported-expression-categories}
1. **Mathematical Operations**: `+`, `-`, `*`, `div`, `mod`, `round()`, `int()`, `number()`.
2. **Logical Operators**: `and`, `or`, `not()`, `=`, `!=`, `<`, `<=`, `>`, `>=`.
3. **String Functions**: `string-length()`, `substr()`, `concat()`, `contains()`, `starts-with()`, `regex()`.
4. **Survey-Specific Functions**:
   - `selected(${question}, 'choice_code')`: Evaluates selection membership.
   - `count-selected(${question})`: Returns the number of chosen options.
   - `today()`, `now()`: Evaluates localized dates and timestamps.
   - `pulldata('dataset', 'col', 'key_col', ${key})`: Evaluates external CSV/XML lookup data.
   - `indexed-repeat(${child_question}, ${repeat_group}, index)`: Accesses child answers within specific repeat iterations.

---


## 3. SafeHtml Sanitized Renderer & Typography {#3-safehtml-sanitized-renderer--typography}


To allow questionnaire designers to format text with bolding, italics, or instructions without risking XSS or layout corruption, MQUAD uses an internal `SafeHtml` widget.


### Typography & Normal Font Weight Standard {#typography--normal-font-weight-standard}
By default, all question labels rendered through `QuestionWidgetFactory.buildLabel` enforce **`FontWeight.normal` (`w400`)** as their base text style. This ensures that any embedded `<b>` or `<strong>` tags clearly pop out with high-contrast `FontWeight.bold` (`w700`) rendering.

```dart
// QuestionWidgetFactory.dart
static Widget buildLabel(QuestionSchema q, String? lang, {TextStyle? style}) {
  final effectiveStyle = (style ?? const TextStyle()).copyWith(
    fontWeight: FontWeight.normal,
  );
  return SafeHtml(
    resolveLabel(q, lang),
    style: effectiveStyle,
  );
}
```


### Whitelisted Inline Formatting: {#whitelisted-inline-formatting}
- `<b>`, `<strong>` → `FontWeight.bold`
- `<i>`, `<em>` → `FontStyle.italic`
- `<u>` → `TextDecoration.underline`
- `<strike>` → `TextDecoration.lineThrough`
- `<sup>`, `<sub>` → Superscript and subscript typography
- `<code>`, `<pre>` → Monospace font family
- `<span style="...">` → Inline CSS color, font-size, and background parsing

---


## 4. Question Dispatch & Widget Factory {#4-question-dispatch--widget-factory}


`QuestionWidgetFactory.build()` dynamically routes each question to its dedicated presentation widget based on `QuestionType`:

```dart
switch (question.type) {
  case QuestionType.text:
    return TextInputWidget(...);
  case QuestionType.integer:
  case QuestionType.decimal:
    return NumericInputWidget(...);
  case QuestionType.selectOne:
    return SelectOneInputWidget(...);
  case QuestionType.selectMultiple:
    return SelectMultipleInputWidget(...);
  case QuestionType.date:
  case QuestionType.time:
    return DateTimeInputWidget(...);
  case QuestionType.geopoint:
  case QuestionType.geotrace:
  case QuestionType.geoshape:
    return LocationInputWidget(...);
  case QuestionType.image:
  case QuestionType.audio:
  case QuestionType.video:
    return MediaInputWidget(...);
  case QuestionType.beginRepeat:
    return RepeatWidget(...);
  case QuestionType.beginGroup:
    return GroupWidget(...);
  case QuestionType.note:
    return NoteWidget(...);
  case QuestionType.signature:
    return SignatureInputWidget(...);
  case QuestionType.barcode:
    return BarcodeInputWidget(...);
  case QuestionType.acknowledge:
    return AcknowledgeWidget(...);
  default:
    return UnsupportedQuestionWidget(question: question);
}
```

---


## 5. Repeating Groups & Scoping {#5-repeating-groups--scoping}


Repeat groups (`begin_repeat` / `end_repeat`) create isolated array scopes:
- **Field Keys**: Stored in the answer map with indexed notation: `members[0].age`, `members[1].age`.
- **Skip Logic in Repeats**: Relevance rules within repeat iterations (e.g. asking pregnancy status only for female household members) are evaluated independently per iteration index.
- **Dynamic Repeat Counts**: Supported via the `jr:count` attribute, automatically inflating or truncating iterations when the controlling question changes.

---


## 6. Pre-Submit Validation & Correction Enforcement {#6-pre-submit-validation--correction-enforcement}


Before any interview can be finalized:
1. **Relevance Filtering**: Irrelevant questions are pruned so stale answers to skipped questions are never submitted.
2. **Constraint Verification**: All constraints are evaluated against final values.
3. **Required Field Verification**: Required questions that are visible must have non-empty answers.
4. **Correction Lock (G-AC4 Rule)**: When an enumerator reopens a rejected response for correction:
   - Only the specific questions flagged by the supervisor in `review_flags` are marked as editable.
   - All other questions render as read-only notes, preventing accidental edits to previously approved data.
