Multiple nouns in except clauses

Dialog library version 0.46 has support only for a single noun in except clauses. I wanted to add support for multiple nouns like [take all but doll and truck].
No need to change the library, just adding the five six two predicates below (2 library overrides 1 library addon, 1 override , 3 4 new ones) anywhere in your story file is enough.

Here is the code:

%% Adds support for multiple nouns in except clauses, now working:
%% [take all but robot and sassy doll],
%% [take all but a doll and a truck],
%% [take all but the doll and the truck], etc.
(parse $Words as object $Output $Policy $ $AllAllowed)
	*(split $Words by [but except] into $Left and $Right)
	*(parse noun list $Left as $BaseList $Policy $AllAllowed)	
	*(parse noun list $Right as $DecRightBaseList {($_ is one of $BaseList)} 0)
	%%(undecorate list $DecRightBaseList into $RightBaseList)
	(strip decorations from $DecRightBaseList into $RightBaseList)
	(remove from $BaseList matching $RightBaseList into $ObjList)
	(mark multi-object $ObjList into $Output)

%% [take all but a doll] was not working properly due to decoration,
%% now fixed and working as intended.
(parse negative noun [$Number | $Words] from $BaseList into $Result)
	{
		($Number is one of [a an])
		($N = 1)
	(or)
		(parse numeral $Number into $N)
	}
	(just)
	(if) (empty $Words) (then)
		(take $N from $BaseList into $NegList)
	(else)
		*(parse indefinite $Words as $DecNegList $N {($_ is one of $BaseList)})
		%%(undecorate list $DecNegList into $NegList)
		(strip decorations from $DecNegList into $NegList)
	(endif)
	(remove from $BaseList matching $NegList into $Result)

%%(interface (undecorate list $<InputList $>OutputList))
%%(interface (undecorate noun $<Input into $>Output))

%% [[a #brokendoll] [a #sassydoll] #wideeyeddoll]
%% -> [#brokendoll #sassydoll #wideeyeddoll]
%%(undecorate list [] into [])
%%(undecorate list [$Head | $Tail] into [$Undecorated | $Out])
%%	(undecorate noun $Head into $Undecorated)
%%	(undecorate list $Tail into $Out)

%% [a #brokendoll] -> #brokendoll, #brokendoll -> #brokendoll
%%(undecorate noun [a $Out] into $Out)
%%(undecorate noun $Out into $Out)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Example scenario 

#store
(room *)
(name *)		toy store
(look *)		All kinds of dolls and other toys are on the shelves.

#brokendoll
(item *)
(name *)		broken doll
(plural dict *)	dolls
(descr *)		One of its arms is missing.

#sassydoll
(item *)
(name *)		sassy doll
(plural dict *)	dolls
(descr *)		It's clad in a lively sundress.

#wideeyeddoll
(item *)
(name *)		wide-eyed doll
(plural dict *)	dolls
(descr *)		Wonder in its eyes, might be my imagination.

#firetruck
(item *)
(name *)		red firetruck
(dict *)		fire truck
(plural dict *)	trucks
(descr *)		Plastic, almost new.

#dumptruck
(item *)
(name *)		heavy duty dump truck
(dict *)		dump truck
(plural dict *)	trucks
(descr *)		It has a faded blue cab and gray open-box bed.

#robot
(item *)
(name *)		battery powered robot
(descr *)		It has metallic shiny colors, but no batteries or battery cover.

(#brokendoll/#sassydoll/#wideeyeddoll is #in #store)
(#firetruck/#dumptruck/#robot is #in #store)

(*(item $) is handled)

#player
(current player #player)
(* is #in #store)

These examples are working in original as well as with my addition:

take all but fire truck
take all but one
take all dolls but two
take all but dolls
take all but the doll (disambiguation)

The following commands work correctly with the new version:

take all but a doll (was bugged due to decoration in (parse negative noun), fixed)
take all but robot and sassy doll
take all but a doll and a truck
take all but the dolls and the trucks
take all but robot and dolls
take all but the robot and 2 dolls
take all but the doll and the truck (disambiguation with 6 correct choices, original code disregards truck exception and offers 3 choices for dolls only)
take all but robot, doll and truck (disambiguation with 6 correct choices)

Working with new code but maybe it should not (original one gives error):

take all but all dolls

Also take all but all gives a different error now, old one:

(I only understood you as far as wanting to take something.)

new one:

(I only understood you as far as wanting to take the battery powered robot, 
the heavy duty dump track, the red firetruck, the sassy doll, the wide-eyed 
doll, and the broken doll.)

This last command can be made to work by changing
*(parse noun list $Right as $DecRightBaseList {($_ is one of $BaseList)} 0)
to
*(parse noun list $Right as $DecRightBaseList {($_ is one of $BaseList)} $AllAllowed)
Although why would anyone want to type that to take nothing?

Edit: Just a slight change to code, to make it more idiomatic. Functions the same.

Edit 2: I was an idiot. The library already had a (strip decorations) predicate, heh. Reflected the changes in the code above, commenting out unnecessary predicates and queries.

Edit 3: The code below in the second post is better and fixes more problems with less intrusive methods to the data structures used by the standard library.

6 Likes

Outside of the except clause, I have found another parsing problem while tinkering. Multiple indefinite nouns in actions like take a doll and a truck seems to fail most of the time.

Since this one required undecorating the main part of the direct objects of the action, I was reluctant to apply the same solution to this problem. I wasn’t too happy about undecorating even the negative part anyway, so I have found a better solution without touching decorations. I fixed the comparison logic in [$ is one of $] and [remove from $ matching $ into $] by writing new versions of them with decorations in mind and replacing them in relevant code. Here is my final version:

%% Adds support for multiple nouns in except clauses, now working:
%% [take all but robot and sassy doll],
%% [take all but a doll and a truck],
%% [take all but the doll and the truck], etc.
(parse $Words as object $Output $Policy $ $AllAllowed)
	*(split $Words by [but except] into $Left and $Right)	
	*(parse noun list $Left as $BaseList $Policy $AllAllowed)
	*(parse noun list $Right as $RightBaseList {(decorated $_ is one of $BaseList)} 0)
	(remove from decorated $BaseList matching $RightBaseList into $ObjList)
	(mark multi-object $ObjList into $Output)

%% Match logic predicates updated to new ones with decorations support
(parse negative noun [$Number | $Words] from $BaseList into $Result)
	{
		($Number is one of [a an])
		($N = 1)
	(or)
		(parse numeral $Number into $N)
	}
	(just)
	(if) (empty $Words) (then)
		(take $N from $BaseList into $NegList)
	(else)
		*(parse indefinite $Words as $NegList $N {(decorated $_ is one of $BaseList)})
	(endif)
	(remove from decorated $BaseList matching $NegList into $Result)

(parse negative noun $Words from $BaseList into $Result) (just)
	*(parse object name $Words as $NegList 0 { (decorated $_ is one of $BaseList) })
	(remove from decorated $BaseList matching $NegList into $Result)

%% Adding multi-object mark to lists that start with a decorated object
(mark multi-object [[a $Head] | $Tail] into [+ [a $Head] | $Tail])
	(nonempty $Tail)

%% Original (remove from) is not overridden, because it is also used
%% in some fungibility code. Didn't want to break it as a side effect.
(interface (remove from decorated $<InputList matching $<Keys into $OutputList))

(remove from decorated [] matching $ into [])
	(just)
(remove from decorated [$In | $MoreIn] matching $Keys into $MoreOut)
	(decorated $In is one of $Keys)
	(just)
	(remove from decorated $MoreIn matching $Keys into $MoreOut)
(remove from decorated [$In | $MoreIn] matching $Keys into [$In | $MoreOut])
	(remove from decorated $MoreIn matching $Keys into $MoreOut)

%% Decorated version of [$ is one of $] predicate, supports matching
%% a decorated element against undecorated list item (and vice versa).
%% Use regular *[$ is one of $List] predicate to extract actual values
%% from the list, this one is meant to be used with both parameters bound.
(interface (decorated $<Element is one of $<List))

(decorated $Obj is one of $List)
	($Obj is one of $List)
(decorated [a $Obj] is one of $List)
	($Obj is one of $List)
(decorated $Obj is one of $List)
	([a $Obj] is one of $List)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Example scenario 

#store
(room *)
(name *)		toy store
(look *)		All kinds of dolls and other toys are on the shelves.

#brokendoll
(item *)
(name *)		broken doll
(plural dict *)	dolls
(descr *)		One of its arms is missing.

#sassydoll
(item *)
(name *)		sassy red doll
(plural dict *)	dolls
(descr *)		It's clad in a lively sundress.

#wideeyeddoll
(item *)
(name *)		wide-eyed red doll
(plural dict *)	dolls
(descr *)		Wonder in its eyes, might be my imagination.

#firetruck
(item *)
(name *)		red firetruck
(dict *)		fire truck
(plural dict *)	trucks
(descr *)		Plastic, almost new.

#dumptruck
(item *)
(name *)		heavy duty dump truck
(plural dict *)	trucks
(descr *)		It has a faded blue cab and gray open-box bed.

#robot
(item *)
(name *)		battery powered robot
(descr *)		It has metallic shiny colors, but no batteries or battery cover.

(#brokendoll/#sassydoll/#wideeyeddoll is #in #store)
(#firetruck/#dumptruck/#robot is #in #store)

(*(item $) is handled)

#player
(current player #player)
(* is #in #store)

In addition to the examples listed above now these work too:

take a doll and a truck
take a truck and 2 dolls
take a truck and 2 dolls except red (might disambiguate if more than one red item are chosen)
4 Likes