Array Literals in ASH

Veracity

Developer
Staff member
heehee invented Map literals in ASH, and a fine feature they are. A little while ago, I made them a Value, and just now I allowed them to appear on the right hand side of an Assignment.

You could (and can) use them in function calls:

Code:
void my_function( string [int] var )
{
...
}
...
my_function( { 1 : "one", 2 : "two" } );
And you could (and can) use them to initialize a mp:

Code:
static string [item] FAR_FUTURE_OPTION {
     RIKER : "riker",
     KARDASHIAN : "booze",
     POINTY_EARS : "ears",
     MEMORY_DISK : "memory",
     EARL_GREY : "food"
};
And revision 17910 lets you put in an '=' like so to make them look like any other assignment for variable initialization:

Code:
rec1 [int,int] rmap = {4: {4: new rec1( 10, "abc" ) }, 10: {40: new rec1( 100, "xyz" ) } };
And that made me consider allowing simplified syntax if the type of the AggregateLiteral is actually an Array.

We currently can do this:

Code:
void print_agg( string name, string [int] agg )
{
    print( name );
    foreach key, val in agg {
	print( "[ " + key + " ] -> " + val );
    }
}

string [5] array1 = { 0 : "zero", 1 : "one", 2 : "two", 3 : "three", 4 : "four" };

print_agg( "array1", array1 );

string [3] array2 = { 0 : "zero", 1 : "one", 2 : "two" };

print( "" );
print_agg( "array2", array2 );

string [int] map = { 0 : "zero", 1 : "one", 2 : "two", 3 : "three", 4 : "four" };

print( "" );
print_agg( "map", map );
yielding this:

Code:
[color=green]> al4[/color]

array1
[ 0 ] -> zero
[ 1 ] -> one
[ 2 ] -> two
[ 3 ] -> three
[ 4 ] -> four

array2
[ 0 ] -> zero
[ 1 ] -> one
[ 2 ] -> two

map
[ 0 ] -> zero
[ 1 ] -> one
[ 2 ] -> two
[ 3 ] -> three
[ 4 ] -> four
Notice that an array is interchangeable (syntax-wise) with a map whose key is an int - except obviously you can't create arbitrary new indices in an array.

I propose the following:

We can do this:

Code:
string [5] array1 = { 0 : "zero", 1 : "one", 2 : "two", 3 : "three", 4 : "four" };
If the Type of the Aggreate on the LHS is an Array, allow this:

Code:
string [5] array1 = { "zero", "one", "two", "three", "four" };
Where the # of elements must be no more than the size of the Array and the indices go from 0 up.

Perhaps allow this:

Code:
string [] array1 = { "zero", "one", "two", "three", "four" };
Where the Array originally has a size of 0, but is changed - and the actual array holding the content is reallocated - to be the number of elements in the Array Literal.

And yes - this would work in function calls, arrays embedded in records, wherever, just like existing Aggregate Literals.

What do you think?
 

Veracity

Developer
Staff member
By the way, the motivation for this came when I examined a Vamping Out script which manually followed choice chains to get a desired result - which was obviously written before KoLmafia created VampOutManager.java, which lets you do it via choice-goal.

No criticism is intended of the author/code, who was just using what was available at the time, but it seemed to me that this record:

Code:
// Type and helpers for masquerade clan data
record vamp_clan {
  string name;
  // The choices to join this clan
  int [5]join;
  // The optimal nomination choices
  string [4]nominate;
};
which was initialized like this:

Code:
vamp_clan create_vamp_clan(string name, int join1, int join2, int join3, int join4, int join5,
                           string nominate1, string nominate2, string nominate3, string nominate4) {
  vamp_clan clan;
  clan.name = name;
  clan.join[0] = join1; clan.join[1] = join2; clan.join[2] = join3; clan.join[3] = join4; clan.join[4] = join5;
  clan.nominate[0] = nominate1; clan.nominate[1] = nominate2; clan.nominate[2] = nominate3; clan.nominate[3] = nominate4;
  return clan;
}

// Mini database of clan information
vamp_clan [stat]vamp_clans;
vamp_clans[$stat[none]] = create_vamp_clan("Malkovich", 2, 3, 3, 3, 4, "Torremolinos", "Ventrilo", "Brouhaha", "Malkovich");
vamp_clans[$stat[muscle]] = create_vamp_clan("Brouhaha", 3, 1, 2, 4, 1, "Malkovich", "Torremolinos", "Brouhaha", "Ventrilo");
vamp_clans[$stat[mysticality]] = create_vamp_clan("Torremolinos", 4, 2, 1, 1, 2, "Malkovich", "Ventrilo", "Torremolinos", "Brouhaha");
vamp_clans[$stat[moxie]] = create_vamp_clan("Ventrilo", 1, 4, 4, 2, 3, "Ventrilo", "Malkovich", "Brouhaha", "Torremolinos");
would be nicer if it could be done like this:

Code:
// Mini database of clan information
vamp_clan [stat]vamp_clans = {
    $stat[ none ] : new vamp_clan( "Malkovich", { 2, 3, 3, 3, 4}, { "Torremolinos", "Ventrilo", "Brouhaha", "Malkovich" } )
    $stat[muscle] : new vamp_clan( "Brouhaha", { 3, 1, 2, 4, 1 }, { "Malkovich", "Torremolinos", "Brouhaha", "Ventrilo" } ),
    $stat[mysticality] : new vamp_clan( "Torremolinos", { 4, 2, 1, 1, 2 }, { "Malkovich", "Ventrilo", "Torremolinos", "Brouhaha" } ),
    $stat[moxie] : new vamp_clan( "Ventrilo", { 1, 4, 4, 2, 3 }, { "Ventrilo", "Malkovich", "Brouhaha", "Torremolinos" } )
};
Even using existing Aggregate Literals would be "nicer", but that looks pretty sweet, to me.
 

heeheehee

Developer
Staff member
This seems reasonable, and the syntax is certainly compatible with that of C and Java. On the other hand, languages such as Javascript and Python use [] to denote array literals; I don't have a strong preference for one or the other.

One thing I do want to bring up is that we're starting to move away from the existing syntax for plural typed constants, although I don't think I mind this so much.

I always felt the syntax for array literals (by treating it as a int -> * map) was a bit heavy-handed (since it wasn't a direct design goal, and only came about as a side effect of the map literal implementation).
 

Veracity

Developer
Staff member
I may rename "AggregateLiteral" to "MapLiteral" (which, like ArrayLiteral, is a subclass of AggregateLiteral.
After all, a "Map" and an "Array" or both subclasses of an "Aggregate".
(Which, like a "Record", is a subclass of a "Composite".)
 

Veracity

Developer
Staff member
And, FWIW, C and Java are the languages I am currently Expert in (as opposed to Common Lisp and Pascal and Bliss 10, which I have long since lost my edge in) - as opposed to Javascript and Python and Perl - so, I know where I am looking for my syntactical inspiration from. :)
 

Veracity

Developer
Staff member
One thing I do want to bring up is that we're starting to move away from the existing syntax for plural typed constants, although I don't think I mind this so much.
Plural type constants are maps from TYPE to boolean. So, they are, to my eye, sets. And if the keys are, in fact, constant at compile time, they are a perfectly good way to initialize such a set.

Map literals can map from anything to anything. If they are TYPE -> boolean, they are a less convenient equivalent for typed constants - except the keys need not be known at compile time.

Array literals, as I am conceptualizing (and implementing) them right now are int -> TYPE, with the restriction that, in order to leave out the int keys, they go from 0 to n. If they are assigned to a map variable, any number of (implicit) keys are allowed. If assigned to an array of fixed size, you'll get a compile-time error if you give too many values; fewer values are OK. If they are assigned to an array of size [0], the type will be munged at compile time to be the number of values in the literal. Like Java.

I always felt the syntax for array literals (by treating it as a int -> * map) was a bit heavy-handed (since it wasn't a direct design goal, and only came about as a side effect of the map literal implementation).
It was a design anti-goal, per hola, since it opened up a runtime error - Array Index Out of Bounds. When I augmented the ASH runtime to report file & line for runtime exceptions, he/we decided that was sufficient.

But Arrays with an integer index from 0 to N are right out of C or Java, and in ASH, they map directly to the underlying Java array type.
 

Veracity

Developer
Staff member
This:

Code:
record vamp_clan {
  string name;
  int [5]join;
  string [4]nominate;
};

void pvc( stat s, vamp_clan vc )
{
    print( "Clan (" + s + "): " + vc.name );
    foreach i, j in vc.join {
	print( "join[" + i + "] = " + j );
    }
    foreach i, n in vc.nominate {
	print( "nominate[" + i + "] = " + n );
    }
    print( "" );
}
// Mini database of clan information
vamp_clan [stat]vamp_clans = {
    $stat[ none ] : new vamp_clan( "Malkovich", { 2, 3, 3, 3, 4 }, { "Torremolinos", "Ventrilo", "Brouhaha", "Malkovich" } ),
    $stat[muscle] : new vamp_clan( "Brouhaha", { 3, 1, 2, 4, 1 }, { "Malkovich", "Torremolinos", "Brouhaha", "Ventrilo" } ),
    $stat[mysticality] : new vamp_clan( "Torremolinos", { 4, 2, 1, 1, 2 }, { "Malkovich", "Ventrilo", "Torremolinos", "Brouhaha" } ),
    $stat[moxie] : new vamp_clan( "Ventrilo", { 1, 4, 4, 2, 3 }, { "Ventrilo", "Malkovich", "Brouhaha", "Torremolinos" } )
};

foreach s, vc in vamp_clans {
    pvc( s, vc );
}
Now yields this:

Code:
[color=green]> al5.ash[/color]

Clan (Moxie): Ventrilo
join[0] = 1
join[1] = 4
join[2] = 4
join[3] = 2
join[4] = 3
nominate[0] = Ventrilo
nominate[1] = Malkovich
nominate[2] = Brouhaha
nominate[3] = Torremolinos

Clan (Muscle): Brouhaha
join[0] = 3
join[1] = 1
join[2] = 2
join[3] = 4
join[4] = 1
nominate[0] = Malkovich
nominate[1] = Torremolinos
nominate[2] = Brouhaha
nominate[3] = Ventrilo

Clan (Mysticality): Torremolinos
join[0] = 4
join[1] = 2
join[2] = 1
join[3] = 1
join[4] = 2
nominate[0] = Malkovich
nominate[1] = Ventrilo
nominate[2] = Torremolinos
nominate[3] = Brouhaha

Clan (none): Malkovich
join[0] = 2
join[1] = 3
join[2] = 3
join[3] = 3
join[4] = 4
nominate[0] = Torremolinos
nominate[1] = Ventrilo
nominate[2] = Brouhaha
nominate[3] = Malkovich
A bit more testing to ensure I didn't break anything else, and I'll submit it.
 

Veracity

Developer
Staff member
This:

Code:
void parray( string name, string [int] array )
{
    foreach i, s in array {
	print( name + "[" + i + "] = " + s );
    }
    print( "" );
}

string [int] m1 = { "one", "two", "three", "two", "one" };
string [5] a1 = { "one", "three", "two", "one" };
string [] a2 = { "one", "two", "three", "two", "one" };
string [3] a3 = { "one", "two", "three", "two", "one" };

parray( "m1", m1 );
parray( "a1", a1 );
parray( "a2", a2 );
Tields this:

Code:
[color=green]> al6[/color]

[color=red]Array has 3 elements but 5 initializers. (al6.ash, line 12)[/color]
Commenting out line 12 yields this:

Code:
[color=green]> al6[/color]

m1[0] = one
m1[1] = two
m1[2] = three
m1[3] = two
m1[4] = one

a1[0] = one
a1[1] = three
a1[2] = two
a1[3] = one
a1[4] =

a2[0] = one
a2[1] = two
a2[2] = three
a2[3] = two
a2[4] = one
I will submit this.
 

heeheehee

Developer
Staff member
Plural type constants are maps from TYPE to boolean. So, they are, to my eye, sets. And if the keys are, in fact, constant at compile time, they are a perfectly good way to initialize such a set.
I always treated them as arrays, although that's probably conflating the implementation with semantic meaning.

> ashq foreach str in $strings[a,a,a] print(str)

a
a
a

It's not a particularly important point, though.
 

Veracity

Developer
Staff member
Ah, you are correct. A PluralValue maps from TYPE -> boolean, but it is an array internally, not a map. So, it's its own thing.
 

Veracity

Developer
Staff member
Whoah. I wanted to do this:

Code:
typedef string[2] vgc_choice_pair;
typedef vgc_choice_pair[] vgc_path_plan;

static vgc_path_plan [string] vgc_plans = {
    "blackmail" :
    {
	{ "", "Muscle" },
	{ "", "Muscle" },
	{ "", "Muscle" },
	{ "", "fruit-leather negatives" },
	{ "photo counter", "" },
	{ "photo counter", "" },
	{ "briefcase full of sprinkles", "" },
    },
    "raygun" :
    {
	{ "", "Mysticality" },
	{ "", "Mysticality" },
	{ "", "Mysticality" },
	{ "teethpick", "dig" },
	{ "", "dig" },
	{ "", "dig" },
	{ "", "dig" },
	{ "", "dig" },
	{ "", "dig" },
	{ "", "dig" },
    },
    "blackmail+raygun" :
    {
	{ "", "Muscle" },
	{ "", "Muscle" },
	{ "", "Muscle" },
	{ "", "fruit-leather negatives" },
	{ "photo counter", "Mysticality" },
	{ "photo counter", "Mysticality" },
	{ "briefcase full of sprinkles", "Mysticality" },
	{ "teethpick", "dig" },
	{ "", "dig" },
	{ "", "dig" },
	{ "", "dig" },
	{ "", "dig" },
	{ "", "dig" },
	{ "", "dig" },
    },
};

foreach name, plan in vgc_plans {
    print( "plan \"" + name + "\":" );
    foreach n, choices in plan {
	int day = n + 1;
	string noon = choices[0];
	string midnight = choices[1];
	print( "day " + day + " noon = \"" + noon + "\" midnight = \"" + midnight + "\"" );
    }
}
And ran into a number of issues.

Notice what I wanted to do:

- Use typedefs to clarify my arrays
- Nest array literals
- Iterate over a map from key => (typedef) and have the value be of type (typedef)

Bugs:

1) Aggregate Literals did not work with typedefs.
2) Array Literals did not evaluate their values.
3) Foreach decided that a value which is a typedef for an aggregate was simply adding additional keys - which would have been true, had I not been using typedefs but maps of maps of maps.

Revision 17939 fixes all of those and now running that script yields this:

Code:
[color=green]> td.ash[/color]

plan "blackmail":
day 1 noon = "" midnight = "Muscle"
day 2 noon = "" midnight = "Muscle"
day 3 noon = "" midnight = "Muscle"
day 4 noon = "" midnight = "fruit-leather negatives"
day 5 noon = "photo counter" midnight = ""
day 6 noon = "photo counter" midnight = ""
day 7 noon = "briefcase full of sprinkles" midnight = ""
plan "blackmail+raygun":
day 1 noon = "" midnight = "Muscle"
day 2 noon = "" midnight = "Muscle"
day 3 noon = "" midnight = "Muscle"
day 4 noon = "" midnight = "fruit-leather negatives"
day 5 noon = "photo counter" midnight = "Mysticality"
day 6 noon = "photo counter" midnight = "Mysticality"
day 7 noon = "briefcase full of sprinkles" midnight = "Mysticality"
day 8 noon = "teethpick" midnight = "dig"
day 9 noon = "" midnight = "dig"
day 10 noon = "" midnight = "dig"
day 11 noon = "" midnight = "dig"
day 12 noon = "" midnight = "dig"
day 13 noon = "" midnight = "dig"
day 14 noon = "" midnight = "dig"
plan "raygun":
day 1 noon = "" midnight = "Mysticality"
day 2 noon = "" midnight = "Mysticality"
day 3 noon = "" midnight = "Mysticality"
day 4 noon = "teethpick" midnight = "dig"
day 5 noon = "" midnight = "dig"
day 6 noon = "" midnight = "dig"
day 7 noon = "" midnight = "dig"
day 8 noon = "" midnight = "dig"
day 9 noon = "" midnight = "dig"
day 10 noon = "" midnight = "dig"
 
Top