diff --git a/README.md b/README.md index 1b6f68770..e75c24e5f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Inform 7 -[Version](notes/versioning.md): 10.2.0-beta+6X33 'Krypton' (26 January 2024) +[Version](notes/versioning.md): 10.2.0-beta+6X34 'Krypton' (7 February 2024) ## About Inform diff --git a/build.txt b/build.txt index f808c2527..8a2f47366 100644 --- a/build.txt +++ b/build.txt @@ -1,3 +1,3 @@ Prerelease: beta -Build Date: 26 January 2024 -Build Number: 6X33 +Build Date: 7 February 2024 +Build Number: 6X34 diff --git a/docs/DialogueKit/S-chc.html b/docs/DialogueKit/S-chc.html index c18d518ae..89cc9cb0b 100644 --- a/docs/DialogueKit/S-chc.html +++ b/docs/DialogueKit/S-chc.html @@ -178,7 +178,7 @@ looks to see if the current action matches the given action pattern. rtrue; ]; -[ DirectorFollowFlowMarker dc chdata; +[ DirectorFollowFlowMarker dc chdata b; if ((dc <= 0) || (dc > NO_DIALOGUE_CHOICES)) rfalse; WriteGProperty(DIALOGUE_CHOICE_TY, dc, performed, true); chdata = TableOfDialogueChoices-->dc; @@ -198,7 +198,10 @@ looks to see if the current action matches the given action pattern. if (chdata-->TYPE_DCMETADATA == ENDING_FINALLY_DSEL or ENDING_FINALLY_SAYING_DSEL) story_complete = true; rtrue; - PERFORM_DSEL: DirectorPerformBeat(chdata-->CONTENT_DCMETADATA); rtrue; + PERFORM_DSEL: + b = chdata-->CONTENT_DCMETADATA; + if (b ofclass Routine) b = b(); + DirectorPerformBeat(b); rtrue; default: print "*** Unimplemented choice ***^"; rfalse; } rtrue; diff --git a/docs/if-module/6-dc.html b/docs/if-module/6-dc.html index 52e48bb7b..b9a01b639 100644 --- a/docs/if-module/6-dc.html +++ b/docs/if-module/6-dc.html @@ -101,6 +101,7 @@ function togglePopup(material_id) { struct parse_node *selection; struct wording selection_parameter; struct dialogue_beat *to_perform; + struct parse_node *to_perform_expression; struct dialogue_choice_compilation_data compilation_data; int selection_type; CLASS_DEFINITION @@ -117,6 +118,7 @@ function togglePopup(material_id) { dc->selection = NULL; dc->selection_parameter = EMPTY_WORDING; dc->to_perform = NULL; + dc->to_perform_expression = NULL; dc->selection_type = AGAIN_DSEL; dc->compilation_data = RTDialogueChoices::new(PN, dc); @@ -392,16 +394,20 @@ is one. if (Wordings::match(dc->selection_parameter, db->beat_name)) dc->to_perform = db; if (dc->to_perform == NULL) { - Problems::quote_source(1, current_sentence); - Problems::quote_wording(2, dc->selection_parameter); - StandardProblems::handmade_problem(Task::syntax_tree(), - _p_(PM_ChoicePerformsUnknown)); - Problems::issue_problem_segment( - "The dialogue choice offered by %1 asks to perform the beat '%2', " - "but I don't recognise that as the name of any beat in the story."); - Problems::issue_problem_end(); - } else { - LOG("Dialogue beat: $O\n", dc->to_perform->as_instance); + if (<s-value>(dc->selection_parameter)) { + parse_node *val = <<rp>>; + if (Dash::check_value(val, K_dialogue_beat) == ALWAYS_MATCH) + dc->to_perform_expression = val; + } else { + Problems::quote_source(1, current_sentence); + Problems::quote_wording(2, dc->selection_parameter); + StandardProblems::handmade_problem(Task::syntax_tree(), + _p_(PM_ChoicePerformsUnknown)); + Problems::issue_problem_segment( + "The dialogue choice offered by %1 asks to perform the beat '%2', " + "but I don't recognise that as the name of any beat in the story."); + Problems::issue_problem_end(); + } } } } diff --git a/docs/runtime-module/2-ec.html b/docs/runtime-module/2-ec.html index 131087c95..21a444683 100644 --- a/docs/runtime-module/2-ec.html +++ b/docs/runtime-module/2-ec.html @@ -94,10 +94,10 @@ instruction last emitted, not after it.
  • EmitCode::up then returns us back to where we were.
  • -void EmitCode::up(void) {
    +void EmitCode::up(void) {
         Produce::up(Emit::tree());
     }
    -void EmitCode::down(void) {
    +void EmitCode::down(void) {
         Produce::down(Emit::tree());
     }
     
    @@ -113,7 +113,7 @@ have made, net:

    §5. Structural.

    -void EmitCode::code(void) {
    +void EmitCode::code(void) {
         Produce::code(Emit::tree());
     }
     
    @@ -283,7 +283,7 @@ in such cases, this function must exist in the kits somewhere.
     

    -void EmitCode::inv(inter_ti bip) {
    +void EmitCode::inv(inter_ti bip) {
         Produce::inv_primitive(Emit::tree(), bip);
     }
     
    @@ -301,11 +301,11 @@ current function:
     

    -void EmitCode::rtrue(void) {
    +void EmitCode::rtrue(void) {
         Produce::rtrue(Emit::tree());
     }
     
    -void EmitCode::rfalse(void) {
    +void EmitCode::rfalse(void) {
         Produce::rfalse(Emit::tree());
     }
     
    diff --git a/docs/runtime-module/2-hrr.html b/docs/runtime-module/2-hrr.html index 3c73240ce..1bb1949aa 100644 --- a/docs/runtime-module/2-hrr.html +++ b/docs/runtime-module/2-hrr.html @@ -1110,6 +1110,7 @@ and The Standard Kits ( enum CHOICE_ARRAY_HL enum CHOICE_AVAILABLE_FN_HL enum CHOICE_ACTION_MATCH_FN_HL +enum CHOICE_PERFORMANCE_FN_HL

    §8.1.24. Establish instances8.1.24 =

    @@ -1230,6 +1231,7 @@ and The Standard Kits ( H_C_U(CHOICE_ARRAY_HL, I"choice_data") H_F_U(CHOICE_AVAILABLE_FN_HL, I"available_fn") H_F_U(CHOICE_ACTION_MATCH_FN_HL, I"action_match_fn") + H_F_U(CHOICE_PERFORMANCE_FN_HL, I"performance_fn") H_F_U(INST_SHOWME_FN_HL, I"showme_fn") H_BEGIN_AP(INLINE_PROPERTIES_HAP, I"inline_property", I"_inline_property") H_C_U(INLINE_PROPERTY_HL, I"inline") diff --git a/docs/runtime-module/5-dci.html b/docs/runtime-module/5-dci.html index 73d37b08e..e7facdb68 100644 --- a/docs/runtime-module/5-dci.html +++ b/docs/runtime-module/5-dci.html @@ -69,6 +69,7 @@ function togglePopup(material_id) { struct inter_name *choice_array_iname; struct inter_name *available_fn_iname; struct inter_name *action_match_fn_iname; + struct inter_name *performance_fn_iname; } dialogue_choice_compilation_data; dialogue_choice_compilation_data RTDialogueChoices::new(parse_node *PN, dialogue_choice *dc) { @@ -77,6 +78,7 @@ function togglePopup(material_id) { dccd.choice_array_iname = NULL; dccd.available_fn_iname = NULL; dccd.action_match_fn_iname = NULL; + dccd.performance_fn_iname = NULL; return dccd; } @@ -99,12 +101,19 @@ function togglePopup(material_id) { return dc->compilation_data.available_fn_iname; } -inter_name *RTDialogueChoices::action_match_fn_iname(dialogue_choice *dc) { +inter_name *RTDialogueChoices::action_match_fn_iname(dialogue_choice *dc) { if (dc->compilation_data.action_match_fn_iname == NULL) dc->compilation_data.action_match_fn_iname = Hierarchy::make_iname_in(CHOICE_ACTION_MATCH_FN_HL, RTDialogueChoices::package(dc)); return dc->compilation_data.action_match_fn_iname; } + +inter_name *RTDialogueChoices::performance_fn_iname(dialogue_choice *dc) { + if (dc->compilation_data.performance_fn_iname == NULL) + dc->compilation_data.performance_fn_iname = + Hierarchy::make_iname_in(CHOICE_PERFORMANCE_FN_HL, RTDialogueChoices::package(dc)); + return dc->compilation_data.performance_fn_iname; +}

    §2. Compilation of dialogue.

    @@ -123,7 +132,7 @@ function togglePopup(material_id) {

    §3.

    -void RTDialogueChoices::choice_compilation_agent(compilation_subtask *ct) {
    +void RTDialogueChoices::choice_compilation_agent(compilation_subtask *ct) {
         dialogue_choice *dc = RETRIEVE_POINTER_dialogue_choice(ct->data);
         current_sentence = dc->compilation_data.where_created;
         package_request *PR = RTDialogueChoices::package(dc);
    @@ -141,7 +150,8 @@ function togglePopup(material_id) {
         EmitArrays::end(save);
     
         if (make_availability_function) Compile the available function3.6;
    -    if (Wordings::nonempty(APW)) Compile the action-matching function3.7;
    +    if (dc->to_perform_expression) Compile the performance function3.7;
    +    if (Wordings::nonempty(APW)) Compile the action-matching function3.8;
     }
     

    §3.1. Scan the clauses further3.1 = @@ -194,7 +204,10 @@ function togglePopup(material_id) { } break; case PERFORM_DSEL: - EmitArrays::iname_entry(RTInstances::value_iname(dc->to_perform->as_instance)); + if (dc->to_perform) + EmitArrays::iname_entry(RTInstances::value_iname(dc->to_perform->as_instance)); + else + EmitArrays::iname_entry(RTDialogueChoices::performance_fn_iname(dc)); break; case INSTEAD_OF_DSEL: case AFTER_DSEL: @@ -292,7 +305,19 @@ function togglePopup(material_id) { }

    -

    §3.7. Compile the action-matching function3.7 = +

    §3.7. Compile the performance function3.7 = +

    + +
    +    packaging_state save = Functions::begin(RTDialogueChoices::performance_fn_iname(dc));
    +    EmitCode::inv(RETURN_BIP);
    +    EmitCode::down();
    +        CompileValues::to_code_val_of_kind(dc->to_perform_expression, K_dialogue_beat);
    +    EmitCode::up();
    +    Functions::end(save);
    +
    + +

    §3.8. Compile the action-matching function3.8 =

    diff --git a/inform7/Figures/memory-diagnostics.txt b/inform7/Figures/memory-diagnostics.txt
    index e9377b97b..e098055fb 100644
    --- a/inform7/Figures/memory-diagnostics.txt
    +++ b/inform7/Figures/memory-diagnostics.txt
    @@ -1,6 +1,6 @@
    -Total memory consumption was 141945K = 139 MB
    +Total memory consumption was 141946K = 139 MB
     
    - ---- was used for 2127403 objects, in 374858 frames in 0 x 800K = 0K = 0 MB:
    + ---- was used for 2127404 objects, in 374859 frames in 0 x 800K = 0K = 0 MB:
     
         29.7%  inter_tree_node_array                    60 x 8192 = 491520 objects, 43255680 bytes
         19.0%  text_stream_array                        4926 x 100 = 492600 objects, 27743232 bytes
    @@ -44,7 +44,7 @@ Total memory consumption was 141945K = 139 MB
          0.1%  inter_annotation_array                   2 x 8192 = 16384 objects, 262208 bytes
          0.1%  vanilla_function                         3823 objects, 244672 bytes
          0.1%  binary_predicate                         330 objects, 174240 bytes
    -     0.1%  hierarchy_location                       1190 objects, 171360 bytes
    +     0.1%  hierarchy_location                       1191 objects, 171504 bytes
          0.1%  linguistic_stock_item                    3338 objects, 160224 bytes
          0.1%  rule_family_data                         404 objects, 148672 bytes
          ----  nonterminal                              773 objects, 142232 bytes
    @@ -57,7 +57,7 @@ Total memory consumption was 141945K = 139 MB
          ----  imperative_defn                          1418 objects, 102096 bytes
          ----  noun_usage                               2419 objects, 96760 bytes
          ----  anl_entry_array                          2 x 1000 = 2000 objects, 96064 bytes
    -     ----  inter_tree                               7 objects, 95928 bytes
    +     ----  inter_tree                               7 objects, 95984 bytes
          ----  preposition                              274 objects, 87680 bytes
          ----  lexical_cluster                          2535 objects, 81120 bytes
          ----  kind_variable_declaration                1659 objects, 79632 bytes
    @@ -262,9 +262,9 @@ Total memory consumption was 141945K = 139 MB
          ----  loop_over_scope                          1 object, 40 bytes
          ----  kind_template_definition                 1 object, 40 bytes
     
    -99.9% was used for memory not allocated for objects:
    +100.0% was used for memory not allocated for objects:
     
    -    63.0%  text stream storage                      91603788 bytes in 513069 claims
    +    63.0%  text stream storage                      91604284 bytes in 513073 claims
          3.7%  dictionary storage                       5497920 bytes in 7767 claims
          ----  sorting                                  2624 bytes in 531 claims
          4.9%  source text                              7200000 bytes in 3 claims
    @@ -282,5 +282,5 @@ Total memory consumption was 141945K = 139 MB
          ----  code generation workspace for objects    3528 bytes in 19 claims
          0.1%  emitter array storage                    281184 bytes in 2006 claims
     
    --134.-3% was overhead - -195282544 bytes = -190705K = -186 MB
    +-134.-3% was overhead - -195282744 bytes = -190705K = -186 MB
     
    diff --git a/inform7/Figures/timings-diagnostics.txt b/inform7/Figures/timings-diagnostics.txt
    index 8f6a51cca..64206d33c 100644
    --- a/inform7/Figures/timings-diagnostics.txt
    +++ b/inform7/Figures/timings-diagnostics.txt
    @@ -1,29 +1,29 @@
     100.0% in inform7 run
    -     67.2% in compilation to Inter
    -         45.3% in //Sequence::undertake_queued_tasks//
    +     67.5% in compilation to Inter
    +         45.2% in //Sequence::undertake_queued_tasks//
               4.3% in //MajorNodes::pre_pass//
    -          3.5% in //MajorNodes::pass_1//
    -          2.1% in //RTPhrasebook::compile_entries//
    -          1.7% in //ImperativeDefinitions::assess_all//
    +          3.6% in //MajorNodes::pass_1//
    +          1.8% in //ImperativeDefinitions::assess_all//
    +          1.8% in //RTPhrasebook::compile_entries//
               1.4% in //RTKindConstructors::compile//
               1.0% in //Sequence::lint_inter//
               0.7% in //ImperativeDefinitions::compile_first_block//
               0.7% in //Sequence::undertake_queued_tasks//
    +          0.7% in //World::stage_V//
               0.3% in //CompletionModule::compile//
               0.3% in //MajorNodes::pass_2//
               0.3% in //Sequence::undertake_queued_tasks//
    -          0.3% in //World::stage_V//
    -          4.7% not specifically accounted for
    -     27.6% in running Inter pipeline
    -          8.9% in step 14/15: generate inform6 -> auto.inf
    -          7.1% in step 5/15: load-binary-kits
    -          6.1% in step 6/15: make-synoptic-module
    -          1.7% in step 9/15: make-identifiers-unique
    +          4.8% not specifically accounted for
    +     27.7% in running Inter pipeline
    +          9.1% in step 14/15: generate inform6 -> auto.inf
    +          6.9% in step 5/15: load-binary-kits
    +          5.8% in step 6/15: make-synoptic-module
    +          1.8% in step 9/15: make-identifiers-unique
               0.3% in step 11/15: eliminate-redundant-labels
               0.3% in step 12/15: eliminate-redundant-operations
               0.3% in step 4/15: compile-splats
               0.3% in step 7/15: shorten-wiring
               0.3% in step 8/15: detect-indirect-calls
    -          1.8% not specifically accounted for
    -      4.3% in supervisor
    +          2.2% not specifically accounted for
    +      4.0% in supervisor
           0.8% not specifically accounted for
    diff --git a/inform7/Internal/Inter/Architecture16Kit/kit_metadata.json b/inform7/Internal/Inter/Architecture16Kit/kit_metadata.json
    index e2f4fd8a8..efb0a25e0 100644
    --- a/inform7/Internal/Inter/Architecture16Kit/kit_metadata.json
    +++ b/inform7/Internal/Inter/Architecture16Kit/kit_metadata.json
    @@ -2,7 +2,7 @@
         "is": {
             "type": "kit",
             "title": "Architecture16Kit",
    -        "version": "10.2.0-beta+6X33"
    +        "version": "10.2.0-beta+6X34"
         },
         "compatibility": "16-bit",
         "kit-details": {
    diff --git a/inform7/Internal/Inter/Architecture32Kit/kit_metadata.json b/inform7/Internal/Inter/Architecture32Kit/kit_metadata.json
    index 174510f5a..4d3be1acf 100644
    --- a/inform7/Internal/Inter/Architecture32Kit/kit_metadata.json
    +++ b/inform7/Internal/Inter/Architecture32Kit/kit_metadata.json
    @@ -2,7 +2,7 @@
         "is": {
             "type": "kit",
             "title": "Architecture32Kit",
    -        "version": "10.2.0-beta+6X33"
    +        "version": "10.2.0-beta+6X34"
         },
         "compatibility": "32-bit",
         "kit-details": {
    diff --git a/inform7/Internal/Inter/BasicInformKit/kit_metadata.json b/inform7/Internal/Inter/BasicInformKit/kit_metadata.json
    index 75ea80ce1..25b875c42 100644
    --- a/inform7/Internal/Inter/BasicInformKit/kit_metadata.json
    +++ b/inform7/Internal/Inter/BasicInformKit/kit_metadata.json
    @@ -2,7 +2,7 @@
         "is": {
             "type": "kit",
             "title": "BasicInformKit",
    -        "version": "10.2.0-beta+6X33"
    +        "version": "10.2.0-beta+6X34"
         },
         "needs": [ {
             "need": {
    diff --git a/inform7/Internal/Inter/CommandParserKit/kit_metadata.json b/inform7/Internal/Inter/CommandParserKit/kit_metadata.json
    index c06ec71a6..c0f1d78a6 100644
    --- a/inform7/Internal/Inter/CommandParserKit/kit_metadata.json
    +++ b/inform7/Internal/Inter/CommandParserKit/kit_metadata.json
    @@ -2,7 +2,7 @@
         "is": {
             "type": "kit",
             "title": "CommandParserKit",
    -        "version": "10.2.0-beta+6X33"
    +        "version": "10.2.0-beta+6X34"
         },
         "needs": [ {
             "need": {
    diff --git a/inform7/Internal/Inter/DialogueKit/Sections/Choices.i6t b/inform7/Internal/Inter/DialogueKit/Sections/Choices.i6t
    index 7ee418965..eb60ed1db 100644
    --- a/inform7/Internal/Inter/DialogueKit/Sections/Choices.i6t
    +++ b/inform7/Internal/Inter/DialogueKit/Sections/Choices.i6t
    @@ -132,7 +132,7 @@ Constant OR_DSEL = 19;                   ! -- or
     	rtrue;
     ];
     
    -[ DirectorFollowFlowMarker dc chdata;
    +[ DirectorFollowFlowMarker dc chdata b;
     	if ((dc <= 0) || (dc > NO_DIALOGUE_CHOICES)) rfalse;
     	WriteGProperty(DIALOGUE_CHOICE_TY, dc, performed, true);
     	chdata = TableOfDialogueChoices-->dc;
    @@ -152,7 +152,10 @@ Constant OR_DSEL = 19;                   ! -- or
     			if (chdata-->TYPE_DCMETADATA == ENDING_FINALLY_DSEL or ENDING_FINALLY_SAYING_DSEL)
     				story_complete = true;
     			rtrue;
    -		PERFORM_DSEL: DirectorPerformBeat(chdata-->CONTENT_DCMETADATA); rtrue;
    +		PERFORM_DSEL:
    +			b = chdata-->CONTENT_DCMETADATA;
    +			if (b ofclass Routine) b = b();
    +			DirectorPerformBeat(b); rtrue;
     		default: print "*** Unimplemented choice ***^"; rfalse;
     	}
     	rtrue;
    diff --git a/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json b/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json
    index d3e832ad1..1595b2e97 100644
    --- a/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json
    +++ b/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json
    @@ -2,7 +2,7 @@
         "is": {
             "type": "kit",
             "title": "EnglishLanguageKit",
    -        "version": "10.2.0-beta+6X33"
    +        "version": "10.2.0-beta+6X34"
         },
         "needs": [ {
             "need": {
    diff --git a/inform7/Internal/Inter/WorldModelKit/kit_metadata.json b/inform7/Internal/Inter/WorldModelKit/kit_metadata.json
    index 4f0b80139..eb49aeefa 100644
    --- a/inform7/Internal/Inter/WorldModelKit/kit_metadata.json
    +++ b/inform7/Internal/Inter/WorldModelKit/kit_metadata.json
    @@ -2,7 +2,7 @@
         "is": {
             "type": "kit",
             "title": "WorldModelKit",
    -        "version": "10.2.0-beta+6X33"
    +        "version": "10.2.0-beta+6X34"
         },
         "needs": [ {
             "need": {
    diff --git a/inform7/if-module/Chapter 6/Dialogue Choices.w b/inform7/if-module/Chapter 6/Dialogue Choices.w
    index 1c536fd27..ac34ac049 100644
    --- a/inform7/if-module/Chapter 6/Dialogue Choices.w	
    +++ b/inform7/if-module/Chapter 6/Dialogue Choices.w	
    @@ -38,6 +38,7 @@ typedef struct dialogue_choice {
     	struct parse_node *selection;
     	struct wording selection_parameter;
     	struct dialogue_beat *to_perform;
    +	struct parse_node *to_perform_expression;
     	struct dialogue_choice_compilation_data compilation_data;
     	int selection_type;
     	CLASS_DEFINITION
    @@ -50,6 +51,7 @@ typedef struct dialogue_choice {
     	dc->selection = NULL;
     	dc->selection_parameter = EMPTY_WORDING;
     	dc->to_perform = NULL;
    +	dc->to_perform_expression = NULL;
     	dc->selection_type = AGAIN_DSEL;
     	dc->compilation_data = RTDialogueChoices::new(PN, dc);
     
    @@ -305,16 +307,20 @@ void DialogueChoices::decide_choice_performs(void) {
     				if (Wordings::match(dc->selection_parameter, db->beat_name))
     					dc->to_perform = db;
     			if (dc->to_perform == NULL) {
    -				Problems::quote_source(1, current_sentence);
    -				Problems::quote_wording(2, dc->selection_parameter);
    -				StandardProblems::handmade_problem(Task::syntax_tree(),
    -					_p_(PM_ChoicePerformsUnknown));
    -				Problems::issue_problem_segment(
    -					"The dialogue choice offered by %1 asks to perform the beat '%2', "
    -					"but I don't recognise that as the name of any beat in the story.");
    -				Problems::issue_problem_end();
    -			} else {
    -				LOG("Dialogue beat: $O\n", dc->to_perform->as_instance);
    +				if ((dc->selection_parameter)) {
    +					parse_node *val = <>;
    +					if (Dash::check_value(val, K_dialogue_beat) == ALWAYS_MATCH)
    +						dc->to_perform_expression = val;
    +				} else {
    +					Problems::quote_source(1, current_sentence);
    +					Problems::quote_wording(2, dc->selection_parameter);
    +					StandardProblems::handmade_problem(Task::syntax_tree(),
    +						_p_(PM_ChoicePerformsUnknown));
    +					Problems::issue_problem_segment(
    +						"The dialogue choice offered by %1 asks to perform the beat '%2', "
    +						"but I don't recognise that as the name of any beat in the story.");
    +					Problems::issue_problem_end();
    +				}
     			}
     		}
     	}
    diff --git a/inform7/runtime-module/Chapter 2/Hierarchy.w b/inform7/runtime-module/Chapter 2/Hierarchy.w
    index 66e1c50af..e506167ad 100644
    --- a/inform7/runtime-module/Chapter 2/Hierarchy.w	
    +++ b/inform7/runtime-module/Chapter 2/Hierarchy.w	
    @@ -990,6 +990,7 @@ void Hierarchy::establish(void) {
     @e CHOICE_ARRAY_HL
     @e CHOICE_AVAILABLE_FN_HL
     @e CHOICE_ACTION_MATCH_FN_HL
    +@e CHOICE_PERFORMANCE_FN_HL
     
     @ =
     	submodule_identity *instances = LargeScale::register_submodule_identity(I"instances");
    @@ -1107,6 +1108,7 @@ void Hierarchy::establish(void) {
     			H_C_U(CHOICE_ARRAY_HL,                          I"choice_data")
     			H_F_U(CHOICE_AVAILABLE_FN_HL,                   I"available_fn")
     			H_F_U(CHOICE_ACTION_MATCH_FN_HL,                I"action_match_fn")
    +			H_F_U(CHOICE_PERFORMANCE_FN_HL,                 I"performance_fn")
     			H_F_U(INST_SHOWME_FN_HL,                        I"showme_fn")
     			H_BEGIN_AP(INLINE_PROPERTIES_HAP,               I"inline_property", I"_inline_property")
     				H_C_U(INLINE_PROPERTY_HL,                   I"inline")
    diff --git a/inform7/runtime-module/Chapter 5/Dialogue Choice Instances.w b/inform7/runtime-module/Chapter 5/Dialogue Choice Instances.w
    index a67a5e14f..bbe941384 100644
    --- a/inform7/runtime-module/Chapter 5/Dialogue Choice Instances.w	
    +++ b/inform7/runtime-module/Chapter 5/Dialogue Choice Instances.w	
    @@ -11,6 +11,7 @@ typedef struct dialogue_choice_compilation_data {
     	struct inter_name *choice_array_iname;
     	struct inter_name *available_fn_iname;
     	struct inter_name *action_match_fn_iname;
    +	struct inter_name *performance_fn_iname;
     } dialogue_choice_compilation_data;
     
     dialogue_choice_compilation_data RTDialogueChoices::new(parse_node *PN, dialogue_choice *dc) {
    @@ -19,6 +20,7 @@ dialogue_choice_compilation_data RTDialogueChoices::new(parse_node *PN, dialogue
     	dccd.choice_array_iname = NULL;
     	dccd.available_fn_iname = NULL;
     	dccd.action_match_fn_iname = NULL;
    +	dccd.performance_fn_iname = NULL;
     	return dccd;
     }
     
    @@ -48,6 +50,13 @@ inter_name *RTDialogueChoices::action_match_fn_iname(dialogue_choice *dc) {
     	return dc->compilation_data.action_match_fn_iname;
     }
     
    +inter_name *RTDialogueChoices::performance_fn_iname(dialogue_choice *dc) {
    +	if (dc->compilation_data.performance_fn_iname == NULL)
    +		dc->compilation_data.performance_fn_iname =
    +			Hierarchy::make_iname_in(CHOICE_PERFORMANCE_FN_HL, RTDialogueChoices::package(dc));
    +	return dc->compilation_data.performance_fn_iname;
    +}
    +
     @h Compilation of dialogue.
     
     =
    @@ -80,6 +89,7 @@ void RTDialogueChoices::choice_compilation_agent(compilation_subtask *ct) {
     	EmitArrays::end(save);
     
     	if (make_availability_function) @;
    +	if (dc->to_perform_expression) @;
     	if (Wordings::nonempty(APW)) @;
     }
     
    @@ -118,7 +128,10 @@ void RTDialogueChoices::choice_compilation_agent(compilation_subtask *ct) {
     			}
     			break;
     		case PERFORM_DSEL:
    -			EmitArrays::iname_entry(RTInstances::value_iname(dc->to_perform->as_instance));
    +			if (dc->to_perform)
    +				EmitArrays::iname_entry(RTInstances::value_iname(dc->to_perform->as_instance));
    +			else
    +				EmitArrays::iname_entry(RTDialogueChoices::performance_fn_iname(dc));
     			break;
     		case INSTEAD_OF_DSEL:
     		case AFTER_DSEL:
    @@ -203,6 +216,14 @@ void RTDialogueChoices::choice_compilation_agent(compilation_subtask *ct) {
     		}
     	}
     
    +@ =
    +	packaging_state save = Functions::begin(RTDialogueChoices::performance_fn_iname(dc));
    +	EmitCode::inv(RETURN_BIP);
    +	EmitCode::down();
    +		CompileValues::to_code_val_of_kind(dc->to_perform_expression, K_dialogue_beat);
    +	EmitCode::up();
    +	Functions::end(save);
    +
     @ =
     	packaging_state save = Functions::begin(RTDialogueChoices::action_match_fn_iname(dc));
     	if ((APW)) {