https://github.com/akkartik/mu/blob/master/042name.cc
  1 //: A big convenience high-level languages provide is the ability to name memory
  2 //: locations. In Mu, a transform called 'transform_names' provides this
  3 //: convenience.
  4 
  5 :(scenario transform_names)
  6 def main [
  7   x:num <- copy 0
  8 ]
  9 +name: assign x 2
 10 +mem: storing 0 in location 2
 11 
 12 :(scenarios transform)
 13 :(scenario transform_names_fails_on_use_before_define)
 14 % Hide_errors = true;
 15 def main [
 16   x:num <- copy y:num
 17 ]
 18 +error: main: tried to read ingredient 'y' in 'x:num <- copy y:num' but it hasn't been written to yet
 19 # todo: detect conditional defines
 20 
 21 :(after "End Type Modifying Transforms")
 22 Transform.push_back(transform_names);  // idempotent
 23 
 24 :(before "End Globals")
 25 map<recipe_ordinal, map<string, int> > Name;
 26 
 27 //: the Name map is a global, so save it before tests and reset it for every
 28 //: test, just to be safe.
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/*
 * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
 * See LICENSE file for license details.
 */

#define TAGS \
const char *tags[] = { "1", "2", "3", "4", "5", NULL };

#define DEFMODE			dotile /* dofloat */
#define FLOATSYMBOL		"><>"
#define TILESYMBOL		"[]="

#define FONT			"fixed"
#define NORMBGCOLOR		"#333366"
#define NORMFGCOLOR		"#cccccc"
#define SELBGCOLOR		"#666699"
#define SELFGCOLOR		"#eeeeee"
#define STATUSBGCOLOR		"#dddddd"
#define STATUSFGCOLOR		"#222222"

#define MASTERW			60 /* percent */
#define MODKEY			Mod1Mask

#define KEYS \
static Key key[] = { \
	/* modifier			key		function	arguments */ \
	{ MODKEY|ShiftMask,		XK_Return,	spawn,		{ .cmd = "exec xterm" } }, \
	{ MODKEY,			XK_Tab,		focusnext,	{ 0 } }, \
	{ MODKEY|ShiftMask,		XK_Tab,		focusprev,	{ 0 } }, \
	{ MODKEY,			XK_Return,	zoom,		{ 0 } }, \
	{ MODKEY,			XK_g,		resizecol,	{ .i = 20 } }, \
	{ MODKEY,			XK_s,		resizecol,	{ .i = -20 } }, \
	{ MODKEY|ShiftMask,		XK_1,		tag,		{ .i = 0 } }, \
	{ MODKEY|ShiftMask,		XK_2,		tag,		{ .i = 1 } }, \
	{ MODKEY|ShiftMask,		XK_3,		tag,		{ .i = 2 } }, \
	{ MODKEY|ShiftMask,		XK_4,		tag,		{ .i = 3 } }, \
	{ MODKEY|ShiftMask,		XK_5,		tag,		{ .i = 4 } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_1,		toggletag,	{ .i = 0 } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_2,		toggletag,	{ .i = 1 } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_3,		toggletag,	{ .i = 2 } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_4,		toggletag,	{ .i = 3 } }, \
	{ MODKEY|ControlMask|ShiftMask,	XK_5,		toggletag,	{ .i = 4 } }, \
	{ MODKEY|ShiftMask,		XK_c,		killclient,	{ 0 } }, \
	{ MODKEY,			XK_space,	togglemode,	{ 0 } }, \
	{ MODKEY,			XK_0,		viewall,	{ 0 } }, \
	{ MODKEY,			XK_1,		view,		{ .i = 0 } }, \
	{ MODKEY,			XK_2,		view,		{ .i = 1 } }, \
	{ MODKEY,			XK_3,		view,		{ .i = 2 } }, \
	{ MODKEY,			XK_4,		view,		{ .i = 3 } }, \
	{ MODKEY,			XK_5,		view,		{ .i = 4 } }, \
	{ MODKEY|ControlMask,		XK_1,		toggleview,	{ .i = 0 } }, \
	{ MODKEY|ControlMask,		XK_2,		toggleview,	{ .i = 1 } }, \
	{ MODKEY|ControlMask,		XK_3,		toggleview,	{ .i = 2 } }, \
	{ MODKEY|ControlMask,		XK_4,		toggleview,	{ .i = 3 } }, \
	{ MODKEY|ControlMask,		XK_5,		toggleview,	{ .i = 4 } }, \
	{ MODKEY|ShiftMask,		XK_q,		quit,		{ 0 } }, \
};

/* Query class:instance:title for regex matching info with following command:
 * xprop | awk -F '"' '/^WM_CLASS/ { printf("%s:%s:",$4,$2) }; /^WM_NAME/ { printf("%s\n",$2) }' */
#define RULES \
static Rule rule[] = { \
	/* class:instance:title regex	tags regex	isfloat */ \
	{ "Firefox.*",			"2",		False }, \
	{ "Gimp.*",			NULL,		True}, \
};
eNr"> 76 if (is_disqualified(product, inst, caller.name)) continue; 77 if (is_numeric_location(product)) numeric_locations_used = true; 78 if (is_named_location(product)) names_used = true; 79 if (is_integer(product.name)) continue; 80 if (names.find(product.name) == names.end()) { 81 trace(9993, "name") << "assign " << product.name << " " << curr_idx << end(); 82 names[product.name] = curr_idx; 83 curr_idx += size_of(product); 84 } 85 int v = lookup_name(product, r); 86 if (v >= 0) { 87 product.set_value(v); 88 // Done Placing Product(product, inst, caller) 89 } 90 else { 91 raise << maybe(caller.name) << "can't find a place to store '" << product.name << "'\n" << end(); 92 return; 93 } 94 } 95 } 96 if (names_used && numeric_locations_used) 97 raise << maybe(caller.name) << "mixing variable names and numeric addresses\n" << end(); 98 } 99 100 bool is_disqualified(/*mutable*/ reagent& x, const instruction& inst, const string& recipe_name) { 101 if (!x.type) { 102 raise << maybe(recipe_name) << "missing type for '" << x.original_string << "' in '" << to_original_string(inst) << "'\n" << end(); 103 // missing-type Error 1 104 return true; 105 } 106 if (is_raw(x)) return true; 107 if (is_literal(x)) return true; 108 // End is_disqualified Special-cases 109 if (x.initialized) return true; 110 return false; 111 } 112 113 bool already_transformed(const reagent& r, const map<string, int>& names) { 114 return contains_key(names, r.name); 115 } 116 117 int lookup_name(const reagent& r, const recipe_ordinal default_recipe) { 118 return Name[default_recipe][r.name]; 119 } 120 121 type_ordinal skip_addresses(type_tree* type) { 122 while (type && is_compound_type_starting_with(type, "address")) 123 type = type->right; 124 if (!type) return -1; // error handled elsewhere 125 if (type->atom) return type->value; 126 const type_tree* base_type = type; 127 // Update base_type in skip_addresses 128 if (base_type->atom) 129 return base_type->value; 130 assert(base_type->left->atom); 131 return base_type->left->value; 132 } 133 134 bool is_compound_type_starting_with(const type_tree* type, const string& expected_name) { 135 if (!type) return false; 136 if (type->atom) return false; 137 if (!type->left->atom) return false; 138 return type->left->value == get(Type_ordinal, expected_name); 139 } 140 141 int find_element_offset(const type_ordinal t, const string& name, const string& recipe_name) { 142 const type_info& container = get(Type, t); 143 for (int i = 0; i < SIZE(container.elements); ++i) 144 if (container.elements.at(i).name == name) return i; 145 raise << maybe(recipe_name) << "unknown element '" << name << "' in container '" << get(Type, t).name << "'\n" << end(); 146 return -1; 147 } 148 int find_element_location(int base_address, const string& name, const type_tree* type, const string& recipe_name) { 149 int offset = find_element_offset(get_base_type(type)->value, name, recipe_name); 150 if (offset == -1) return offset; 151 int result = base_address; 152 for (int i = 0; i < offset; ++i) 153 result += size_of(element_type(type, i)); 154 return result; 155 } 156 157 bool is_numeric_location(const reagent& x) { 158 if (is_literal(x)) return false; 159 if (is_raw(x)) return false; 160 if (x.name == "0") return false; // used for chaining lexical scopes 161 return is_integer(x.name); 162 } 163 164 bool is_named_location(const reagent& x) { 165 if (is_literal(x)) return false; 166 if (is_raw(x)) return false; 167 if (is_special_name(x.name)) return false; 168 return !is_integer(x.name); 169 } 170 171 // all names here should either be disqualified or also in bind_special_scenario_names 172 bool is_special_name(const string& s) { 173 if (s == "_") return true; 174 if (s == "0") return true; 175 // End is_special_name Special-cases 176 return false; 177 } 178 179 bool is_raw(const reagent& r) { 180 return has_property(r, "raw"); 181 } 182 183 :(scenario transform_names_supports_containers) 184 def main [ 185 x:point <- merge 34, 35 186 y:num <- copy 3 187 ] 188 +name: assign x 2 189 # skip location 2 because x occupies two locations 190 +name: assign y 4 191 192 :(scenario transform_names_supports_static_arrays) 193 def main [ 194 x:@:num:3 <- create-array 195 y:num <- copy 3 196 ] 197 +name: assign x 2 198 # skip locations 2, 3, 4 because x occupies four locations 199 +name: assign y 6 200 201 :(scenario transform_names_passes_dummy) 202 # _ is just a dummy result that never gets consumed 203 def main [ 204 _, x:num <- copy 0, 1 205 ] 206 +name: assign x 2 207 -name: assign _ 2 208 209 //: an escape hatch to suppress name conversion that we'll use later 210 :(scenarios run) 211 :(scenario transform_names_passes_raw) 212 % Hide_errors = true; 213 def main [ 214 x:num/raw <- copy 0 215 ] 216 -name: assign x 2 217 +error: can't write to location 0 in 'x:num/raw <- copy 0' 218 219 :(scenarios transform) 220 :(scenario transform_names_fails_when_mixing_names_and_numeric_locations) 221 % Hide_errors = true; 222 def main [ 223 x:num <- copy 1:num 224 ] 225 +error: main: mixing variable names and numeric addresses 226 227 :(scenario transform_names_fails_when_mixing_names_and_numeric_locations_2) 228 % Hide_errors = true; 229 def main [ 230 x:num <- copy 1 231 1:num <- copy x:num 232 ] 233 +error: main: mixing variable names and numeric addresses 234 235 :(scenario transform_names_does_not_fail_when_mixing_names_and_raw_locations) 236 def main [ 237 x:num <- copy 1:num/raw 238 ] 239 -error: main: mixing variable names and numeric addresses 240 $error: 0 241 242 :(scenario transform_names_does_not_fail_when_mixing_names_and_literals) 243 def main [ 244 x:num <- copy 1 245 ] 246 -error: main: mixing variable names and numeric addresses 247 $error: 0 248 249 //:: Support element names for containers in 'get' and 'get-location' and 'put'. 250 //: (get-location is implemented later) 251 252 :(before "End update GET offset_value in Check") 253 else { 254 if (!offset.initialized) { 255 raise << maybe(get(Recipe, r).name) << "uninitialized offset '" << offset.name << "' in '" << to_original_string(inst) << "'\n" << end(); 256 break; 257 } 258 offset_value = offset.value; 259 } 260 261 :(scenario transform_names_transforms_container_elements) 262 def main [ 263 p:&:point <- copy null 264 a:num <- get *p:&:point, y:offset 265 b:num <- get *p:&:point, x:offset 266 ] 267 +name: element y of type point is at offset 1 268 +name: element x of type point is at offset 0 269 270 :(before "End transform_names(inst) Special-cases") 271 // replace element names of containers with offsets 272 if (inst.name == "get" || inst.name == "get-location" || inst.name == "put") { 273 //: avoid raising any errors here; later layers will support overloading new 274 //: instructions with the same names (static dispatch), which could lead to 275 //: spurious errors 276 if (SIZE(inst.ingredients) < 2) 277 break; // error raised elsewhere 278 if (!is_literal(inst.ingredients.at(1))) 279 break; // error raised elsewhere 280 if (inst.ingredients.at(1).name.find_first_not_of("0123456789") != string::npos) { 281 // since first non-address in base type must be a container, we don't have to canonize 282 type_ordinal base_type = skip_addresses(inst.ingredients.at(0).type); 283 if (contains_key(Type, base_type)) { // otherwise we'll raise an error elsewhere 284 inst.ingredients.at(1).set_value(find_element_offset(base_type, inst.ingredients.at(1).name, get(Recipe, r).name)); 285 trace(9993, "name") << "element " << inst.ingredients.at(1).name << " of type " << get(Type, base_type).name << " is at offset " << no_scientific(inst.ingredients.at(1).value) << end(); 286 } 287 } 288 } 289 290 :(scenario missing_type_in_get) 291 % Hide_errors = true; 292 def main [ 293 get a, x:offset 294 ] 295 +error: main: missing type for 'a' in 'get a, x:offset' 296 297 :(scenario transform_names_handles_containers) 298 def main [ 299 a:point <- merge 0, 0 300 b:num <- copy 0 301 ] 302 +name: assign a 2 303 +name: assign b 4 304 305 //:: Support variant names for exclusive containers in 'maybe-convert'. 306 307 :(scenarios run) 308 :(scenario transform_names_handles_exclusive_containers) 309 def main [ 310 12:num <- copy 1 311 13:num <- copy 35 312 14:num <- copy 36 313 20:point, 22:bool <- maybe-convert 12:number-or-point/unsafe, p:variant 314 ] 315 +name: variant p of type number-or-point has tag 1 316 +mem: storing 1 in location 22 317 +mem: storing 35 in location 20 318 +mem: storing 36 in location 21 319 320 :(before "End transform_names(inst) Special-cases") 321 // convert variant names of exclusive containers 322 if (inst.name == "maybe-convert") { 323 if (SIZE(inst.ingredients) != 2) { 324 raise << maybe(get(Recipe, r).name) << "exactly 2 ingredients expected in '" << to_original_string(inst) << "'\n" << end(); 325 break; 326 } 327 assert(is_literal(inst.ingredients.at(1))); 328 if (inst.ingredients.at(1).name.find_first_not_of("0123456789") != string::npos) { 329 // since first non-address in base type must be an exclusive container, we don't have to canonize 330 type_ordinal base_type = skip_addresses(inst.ingredients.at(0).type); 331 if (contains_key(Type, base_type)) { // otherwise we'll raise an error elsewhere 332 inst.ingredients.at(1).set_value(find_element_offset(base_type, inst.ingredients.at(1).name, get(Recipe, r).name)); 333 trace(9993, "name") << "variant " << inst.ingredients.at(1).name << " of type " << get(Type, base_type).name << " has tag " << no_scientific(inst.ingredients.at(1).value) << end(); 334 } 335 } 336 } 337 338 :(scenario missing_type_in_maybe_convert) 339 % Hide_errors = true; 340 def main [ 341 maybe-convert a, x:variant 342 ] 343 +error: main: missing type for 'a' in 'maybe-convert a, x:variant'