{"id":18994925,"url":"https://github.com/wevre/wry","last_synced_at":"2026-02-25T22:33:09.832Z","repository":{"id":65773309,"uuid":"59032805","full_name":"wevre/wry","owner":"wevre","description":"Scripting language","archived":false,"fork":false,"pushed_at":"2019-12-23T05:17:53.000Z","size":622,"stargazers_count":9,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-21T11:53:03.211Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"ANTLR","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wevre.png","metadata":{"files":{"readme":"README.wry.txt","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-05-17T15:05:31.000Z","updated_at":"2023-07-10T02:56:45.000Z","dependencies_parsed_at":"2023-02-09T06:25:11.128Z","dependency_job_id":null,"html_url":"https://github.com/wevre/wry","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/wevre/wry","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wevre%2Fwry","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wevre%2Fwry/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wevre%2Fwry/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wevre%2Fwry/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wevre","download_url":"https://codeload.github.com/wevre/wry/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wevre%2Fwry/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29843460,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-25T21:18:31.832Z","status":"ssl_error","status_checked_at":"2026-02-25T21:18:29.265Z","response_time":61,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-08T17:27:37.251Z","updated_at":"2026-02-25T22:33:09.806Z","avatar_url":"https://github.com/wevre.png","language":"ANTLR","funding_links":[],"categories":[],"sub_categories":[],"readme":"project : Wry readme\n\tauthor : Mike Weaver\n\tcreated : 2018-06-13\n\tcopyright : © 2018 All rights reserved.\n\tlicense : See [*License].\n\nA scripting language for web authoring.\n\nsection : Grammar\n\n\tSee {wry grammar}[*grammars/Wry.g4].\n\nsection : Types\n\n\tThere are (at least) seven types: `Int`, `Float`, `Bool`, `String`,\n\t`Null`, `Array`, and `Function`. The `Array` type replaces what would be\n\tarrays, dictionaries, class instances, and parameter lists in other\n\tlanguages. It is an ordered map and behaves similarly to PHP's array type.\n\n\t!!!\n\t\tNot sure if anyone else has coined this already, but I think I'll refer to\n\t\tthe data structure that implements arrays as a \"sortmap\".\n\n\t!!!\n\t\tProbably a \"chain\" (or whatever I call it, maybe \"stack\"?) will be another\n\t\ttype. It's a stack of arrays. Functions and chains can also be combined,\n\t\tand is that a different type or is that still a function type (just with\n\t\tsome data already chained to it)?\n\n\tThe simple types (`Integer`, `Float`, `Bool`, `String`, `Null`) will be\n\tfamiliar to users of other languages, and can be created with literals that\n\tare obvious.\n\n\t```\n\t\tcount = 7\n\t\tradius = 13.6\n\t\tisChecked = false\n\t\temployee = \"John Smith\"\n\t\tflag = null\n\n\tThose simple types are scalars, whereas the `Array` type is a compound\n\tobject.\n\nsection : Arrays\n\n\tAn array is an ordered hashmap, meaning it preserves order of keys as items\n\tare inserted into the array.\n\n\t!!!\n\t \tImplementing an array means using a hashmap combined with a linked list,\n\t \tgoing to callit a \"sortmap\".\n\n\tTo create an array, list out elements (keys and values) separated by commas.\n\tA trailing comma is allowed, which simplifies code maintenance and can also\n\tbe usefule to coerce something to an array. An array can be a simple list of\n\tother types (no keys), in which case incrementing integer keys will\n\tautomatically be applied. Or it can be more like a dictionary with keys and\n\tvalues, separate by colons and commas. Keys are of type `String` or `Int`, or\n\texpressions that evaluate to one of these. Entries in the array can be\n\tretrieved by key.\n\n\t```\n\t\temployee = ('first': 'mike', 'last': 'weaver', 'empl-id': 1234, 10.5)\n\n\tThis syntax is similar to creating a dictionary in other languages, say JSON.\n\tThe parenthesis are needed because of operator precedence. Without them\n\t`employee` would be the first item in a much different array, instead of\n\tbeing the name to which the array is assigned.\n\n\t!!!\n\t\tOr it might be a syntax error. Without parenthesis the statement would be\n\t\tinterpreted as\n\n\t\t```\n\t\t\t(employee = 'first') : 'mike', ...\n\n\t\tWhich would be a syntax error. The first term in parenthesis is an\n\t\tassignment which doesn't return a value so can't be used as a key in an\n\t\tarray.\n\n\tAn `Array` of items without keys is declared in a similar way:\n\n\t```\n\t\tcutoffs = (15.0, 25.0, 47.0)\n\n\tThese items would automatically receive integer keys 0, 1, and 2.\n\n\tItems in the array are accessed with a subscript operator that uses\n\tsquare brackets. Inside the brackets use an expression that evaluates to\n\teither an `Int` or `String` that represents the key.\n\n\t```\n\t\tcutoffs[0]   // 15.0\n\t\temployee['first']   // 'mike'\n\t\tkey = 'first'\n\t\temployee[key]   // 'mike'\n\n\tAn alternate way to retrieve keyed items in the array uses a dot syntax. In\n\tthis case, the key for the item must be a string that is also a valid name.\n\n\t```\n\t\temployee.first   // equivalent to employee['first']\n\n\tItems can be stored in an array with any string for the key, but dot\n\tmembership will only work for string keys that are also valid names.\n\n\t!!! Experimental subscripts\n\n\t\tMultiple items can be accessed using multiple items in the subscript,\n\t\tseparated by commas. Expressions that resolve to other than String or\n\t\tInteger will emit a warning.\n\n\t\t```\n\t\t\tcutoffs[0, 2]   //  0 and 1\n\t\t\temployee['first', 'empl-id']\n\n\t\tThe two expressions above, instead of returning a single item from the\n\t\toriginal array, will each return an array with two items. Integer indices\n\t\tin the returned array will be reset to 0, but string keys will be\n\t\tpreserved from the original. (... or, why not preserve integer indices? we\n\t\tcan always have a method to \"reset\" array indices if that is desirable.)\n\n\t\tWould this kind of syntax be allowed as an lvalue?\n\n\tArrays keys can be declared in different ways. The following are equivalent:\n\n\t```\n\t\tmydata = ('name': 'mike', 'age': 15)\n\t\tmydata = (name='mike', age=15)\n\t\tname = 'mike'\n\t\tage = 15\n\t\tmydata = (~name, ~age)\n\t\tmydata\n\t\t\tname = 'mike'\n\t\t\tage = 15\n\t\tmydata\n\t\t\t'name': 'mike'\n\t\t\t'age': 15\n\t\t'mydata': ('name': 'mike', 'age': 15)\n\n\tWhen declaring keys, one can use a string, or an expression that evaluates\n\tto a string, followed by a colon. Or one can use a bare name (no quotes) and\n\tan equals sign. Or one can use multiple lines and indentation to layout the\n\tstructure of the array. A tilde preceding a bare name converts it as follows:\n\t`~name` is expanded to `'name': name` where the expression on the left is a\n\tstring, and the expression on the right will be evaluated.\n\n\tI was also thinking of an instance where, using indentation, if we need a\n\tnon-named array we can use a bare comma:\n\n\t```\n\t\tpeople\n\t\t\t,   // Or maybe empty brackets `[]`, or empty parens `()`\n\t\t\t\t'name': 'mike'\n\t\t\t\t'age': 42\n\t\t\t,   // Another option might be to use a bare underscore: _\n\t\t\t\t'name': 'fred'\n\t\t\t\t'age': 34\n\n\t!!!\n\t\tI'm liking empty parens as representative of an empty array. Also use to\n\t\tinitialize an empty array.\n\t!!!\n\t\tThis of course screams out to use records.\n\n!!! Continue converting this into Wry prose.\n\nA bare expression (something that is not part of an assignment statement) will act as an\nassignment into the local scope, using the next integer key.\n\nDo we need syntax for appending to an array? PHP makes this simple using empty brackets to\nrepresent \"the next available slot\".\n\n  \tb[] = \"next\"\n\nDo we want something like that? I'm currently thinking we use the `+` operator:\n\n  \tb += 'next'\n\n  \ta = b + ( 'surname' : 'thompson' )\n\nWhat if we need to declare an array of arrays? I think we can do that with parens and\ntrailing commas:\n\n  \tb = (1, 2, 3)\n  \tc = b + 4\n  \td = b + (4,5,6)\n  \te = b + ((4,5,6),)\n\n\n\n### Local Scope\n\nWhatever the current scope is, its variables can be accessed via `$loc`, as follows:\n\n  \t$loc['a']['name'] = 'Fred'\n  \tprint $loc.a['name']\n  \tprint $loc.a.name\n  \tprint $loc['a'].name\n\nNormally, the search to resolve a name will start with `$loc`, so usually it is not\nnecessary to explicitly reference it.\n\nThis works:\n\n  \t$loc['count'] = 100\n  \tstart = 0\n  \tstop = count\n\nAnother way to affect the scope is the `with` statement. This is also a way to append to\nan array. All of these are equivalent. The `with` statement makes the array `a` the local\nscope and expressions and statements affect `a`.\n\n  \ta.name = 'Fred'\n  \ta['name'] = 'Fred'\n  \twith a\n \t\t\tname = 'Fred'\n\nConceptually the `with` statement is pushing a new scope (for `a`) onto the scope stack\nand then popping it off after the with block is done. Any assignments or bare expressions\nwill affect the current scope, `a`. This makes it easy to construct an array and use\nbranching statements to conditionally add elements to the array.\n\n  \thasSurname = true\n  \ta\n \t\t\tname = 'Fred\n \t\t\tif needsSurname surname = \"Thompson\"\n\nWe can also use the `with` statement to create a temporary scope where we may override\nvalues outside. I'm not sure how I want that syntax to look:\n\n\tfunc bailout\n\t\t...\n\n\twith\n\t\tfunc bailout   // Overrides the bailout function at the outer level.\n\t\t\t...\n\t\t\t...\n\t\tbailout()\n\n\tbailout()   // Calls the original, outer `bailout`.\n\nMaybe a \"bare\" `with` as above. Or maybe a `with` statement followed by an underscore. We\nshould also allow for labels in case we want to be able to refer to overriden values at\nspecific points in the hierarchy. (Which begs the question, how do we refer to the outermost\n$out if we are several levels deep?) (This also begs the question, why would you need/want to\ndo this?)\n\n\n\n### Importing files\n\nScripts can be split into multiple files and then included inside one another. We want to\nbe able to include a file, but keep it somewhat isolated from the current scope, to avoid\ncollisions. When a file is included, using the `wend` statement, it create a new level\nin the scope hierarchy. The current hierarchy is pushed, or \"wound\", up the stack; the\nincluded file is executed in its own scope, and then _that_ is wound up the stack and a\nnew scope level opened for the rest of the script.\n\nThis allows one, for example, to set variables prior to the `wend` statement that will affect\nthe execution of the imported file, and still be available (if not overridden) to later\nparts of the script. It also allows the author to override something in the included file,\nbecause the override is happening at the current (lower) scope level. A `wend` statement can\ninclude a label so one can refer back to objects explicitly at that level. As with the above\ndiscussion on local scope, this begs the question of how we refer to a scope that has been\npushed above a `wend` or `with` statement. Perhaps we allow for a label like this:\n\n\t#script\n\n\t...\n\n\twend 'include/inc-standard.wry' #standard\n\n\tfunc bailout   // This overrides the function defined in 'inc-standard'.\n\t\t@standard.bailout()   // Call the overridden function \u003c- need to think about syntax,\n\n\t@script.some_var\n\nThe act of pushing the scope up the stack will be triggered by a `wend` statement\nand also by EOF.\n\nSimilar to the bare `with` above (and maybe, replacing it) would be a bare `wend` statement\nwhich would push the current scope up the stack and open up a new one. This would let you\noverride something in the current scope, but still make it available if needed.\n\n\t#current\n\n\tmyFunc = ...   // define something\n\n\twend #next\n\n\tmyFunc = ...   // will override (but not replace) the prior one\n\n\tmyFunc()\n\n\t@current.myFunc()   // reaches back to version defined prior to `wend`\n\nsection : Loops\n\n\t!!!\n\t\t2018-04-26: I'm thinking instead of `continue`, the keyword to loop back\n\t\tshould be `repeat`, which meaning is a little more obvious.\n\n\tThere are two loop statements, `do`, and `for`. `for` takes an array and\n\titerates through the elements of the array, providing variables `key` and\n\t`val` to the statements inside the block at each iteration. The `for`\n\tstatement, unlike `do`, does *NOT* need an explicit `continue` at the bottom.\n\n\t!!!\n\t\tQuestion: should `val` be a reference to the array element, instead of a\n\t\tcopy? One could always access the original object using the `key`. For\n\t\tinstance, suppose we wanted to iterate over an array of numbers and add 1\n\t\tto each one:\n\t\t```\n\t\t\tfor arr_of_numbers\n\t\t\t\tarr_of_numbers[key] += 1\n\t\t\tarray_of_numbers-\u003emap({ $obj[key] += 1})\n\n\t!!!\n\t\t2018-04-26: With `for` loops, if they are nested, we need to distinguish\n\t\tbetween `key` and `val` at different levels. Perhaps the `for` loop needs\n\t\ta way to specify what variables we want to use, and `key` and `val` are\n\t\tthe defaults if not specified.\n\n\t```\n\t\tfor array_of_stuff\n\t\t\t// we can use key and val\n\n\t\tfor array_of_stuff use index=value\n\t\t\t// now we can use variables index and value.\n\n\t\tfor array_of_stuff\u003ckey,val\u003e\n\t\t\tfor val\u003ciKey,iVal\u003e\n\t\t\t\t...\n\t\tfor array_of_stuff #outer\n\t\t\tfor val #inner\n\t\t\t\t// here we can use key and val, but also\n\t\t\t\t// @outer.key and @outer.val ????\n\n\t!!!\n\t\tI like that last one, it uses labels and hints at the notion of nested\n\t\tscopes, so does that mean there is some sort of implicit wend going on\n\t\twith the loops? It also has cleaner syntax, because we don't have to\n\t\tintroduce a new keyword `as` or `using` or something.\n\t!!!\n\t\tGood question about scopes. In PHP, variables defined inside of loops or\n\t\tbranches are available outside the loop or branch, which I prefer to, say\n\t\tC++ where brackets introduce a new scope, even for if and while\n\t\tstatements.\n\t!!!\n\t\tI'm starting to think of angle brackets as the syntax to use for\n\t\t\"placeholders\", whatever that might mean in different contexts. But how\n\t\twould the example above be distinguished from chaining an argument scope?\n\nThe `do` statement begins a block of code that can be repeated with a `continue` statement\nsomewhere inside the block. The `do` block can also be exited with a `break` statement. If\nthe block is never \"broken\" out of, then an optional, trailing `then` block will be\nexecuted. But if a `break` occurs, the `then` block will be skipped. (`break`, `continue`,\nand `then` also apply the same to a `for` block.)\n\n  \tdo\n \t\t\t\u003csome stuff\u003e\n \t\t\tif \u003cexpression\u003e continue   // Skip the remaining statements and start loop over.\n \t\t\t\u003csome stuff\u003e\n \t\t\tif \u003cexpression\u003e break   // Stop iterating and jump past the `then` block.\n \t\t\tcontinue\n  \tthen\n \t\t\t\u003csome stuff\u003e   // Will only be executed if there was no `break` above.\n\nMany loops will start with a test, similar to a standard `while` statement in C:\n\n  \tdo\n \t\t\tif !\u003cwhile-condition\u003e break\n \t\t\t\t...\n\nTo keep the syntax clean (and prevent excessive indenting), a special form of the `do`\nstatement allows for a test at the top of the loop:\n\n  \tdo if \u003ccondition\u003e\n \t\t\t\u003csome stuff\u003e\n  \tthen\n \t\t\t\u003csome stuff\u003e   // Executed even if we fail the condition, as long as no break.\n\nIt's very common to have an incrementing statement right before continue, such as:\n\n  \ti = 0\n  \tdo if i \u003c max\n \t\t\t\u003cstatements\u003e\n \t\t\ti += 1\n \t\t\tcontinue\n\nTo clean that up a bit, both `continue` and `break` allow for a statement after, to\ncapture change to the iterator variable, for example.\n\n  \ti = 0\n  \tdo if i\u003cmax\n \t\t\t\u003csome stuff\u003e\n \t\t\tcontinue i += 1\n\nThis nicely keeps the logic controlling the loop close to the important `do-if` and\n`continue` statements.\n\nBoth `do` and `for` statements can be given a label, and `break` and `continue` can refer\nto that label in order to break out of nested loops.\n\n  \tdo #outer\n \t\t\t\u003csome stuff\u003e\n \t\t\tdo #inner\n\t\t\t\t\t\u003csome stuff\u003e\n\t\t\t\t\tif \u003cexpression\u003e break @outer\n \t\t\t\u003csome stuff\u003e\n  \tthen\n \t\t\t\u003cstuff\u003e   // won't be called if we broke out of the #outer loop\n\n!!!\n\tDo we use the octothorpe on the break statement? Or would it be more appropriate\n\tto use the ampersand? Or do we need any punctuation at all? We probably don't\n\t_need_ punctuation, but it is probably useful as a visual marker.\n\nThe label always comes at the end of the line, so `do-if` has its test expression first,\nthen the label. And `continue` could have an optional incrementing statement first, then\nthe label, if any.\n\nIf we do exceptions, then we will \"throw\" an array, and we can test the array, using\n`$exc`, to determine how we want to handle it in the `catch` clause, like so\n\n  \ttry\n \t\t\t\u003csome stuff\u003e\n  \tcatch if $exc.name == 'error'\n \t\t\t\u003cstuff\u003e\n  \tcatch if $exc.name == 'blahblahblah'\n \t\t\t\u003cstuff\u003e\n  \tcatch\n \t\t\t\u003cstuff\u003e   // for all other exceptions\n\nI don't think we'll need a `finally` statement, because we'll adopt the concept of `defer`\nfrom Swift.\n\n\n\n### Object Serializing\n\nMaybe we can borrow from the ideas behind dStruct and create a mechanism to save arrays in\na database. There can be some initialization files or configuration files that grant the\nscript access to a database, and then simple calls like this will make objects permanent\nfrom one run of the script to another:\n\n  \t$dbs['person'] += ('name': 'Mike', 'age': 24)\n\n  \tfor $dbs['persons']\n \t\t\t\u003cdo something with all the persons returned from the database\u003e\n\nI think to make this work, we'll need hooks that are called when array items are `set` or\n`get`. Those could be buried away and hidden from the user, or made available for anyone\nto take advantage of them.\n\nI'm thinking something similar (`$fil`) will be used to manage access to the file system.\n\n\n\n### Functions\n\nTo create a function, use the keyword `func` to open a new block and then write\nstatements in an indented block that follows.\n```\n  \tfunc celsiusFromFahrenheit\n \t\t\treturn ($0-32.0)*5.0/9.0\n```\nCalling the function is accomplished by appending parenthesis after a name. The\nparenthesis can be empty, or they can contain array entries that will be passed in as\narguments and available inside the function via `$arg`.\n\nYou can also compose an array with a function call, and the array will be available inside\nthe function as `$obj`. Inside the function, name resolution follows a search path through\n`$loc`, `$arg`, `$obj`, and finally `$out`. These arrays can be referenced explicitly or\nsearching will happen implicitly in the given order. Note that `$arg` is read-only,\nwhereas `$obj` is a reference to the original object, so changes to `$obj` inside the\nfunction will be visible outside (or after) the function.\n\nWhen passing similarly named arguments from one function to another, the tilde syntax\n(`~name`) becomes very useful. And if an entire array needs to be passed, used double\ntilde syntax: `~~name`.\n\nWhen an array, `b`, is composed with a function, the search to resolve the function name\nbegins in `b` and then (if not found) search begins in the local scope. The result is that\ncomposing becomes a natural way to call functions that are defined inside an array. (It\nbehaves like member lookup.)\n\nSuppose we have an array `p` in the local scope that has been set up as a Person object,\nwith a first and last name, and a function `fullName`. Ignoring for the moment how we set\nup this object, we would call the `fullName` method like so:\n\n  \ts = p-\u003efullName()\n\nBecause of the compose operator, the process of resolving the name \"fullName\" will begin\nwith `p`, where it will indeed find a function under the key \"fullName\". The function will\nbe called with `p` as the `$obj`. If instead we did any of the following:\n\n  \tp.fullName()\n  \tp['fullName']()\n\nthe function would be found and called, but it would not have the `$obj` array as expected\nand would probably produce meaningless results. We can \"hijack\" the function, being able\nto make a good guess what it does, and do something strange like this:\n\n  \timposter = ( 'firstName': 'Mike', 'lastName': 'Smith' )\n  \timposter-\u003ep.fullName()\n\nand would probably get the results we expected. Now the `fullName` function has an `$obj`\narray with the entries it expects to see.\n\nThis is sort of a convoluted example because if all we really cared about was composing\na full name, we might want to pass first and last names in as (read-only) arguments,\nrather than composing with an object. But here we are trying to provide an example of an\nobject (the Person) that includes data and functions to work on that data.\nSo there you go.\n\nRelated to this, if we have a function with the same name defined in the local\nscope, it _won't_ get called by the straightforward composition operator. We instead have\nto explicitly refer to it's containing scope (`$loc`), like so:\n\n  \tp-\u003e$loc.fullName()\n\nSome functions don't need named parameters, so a simple array object can be provided.\nInternally, the function refers to the arguments with an automatic variable `$arg`.\n\n  \tfunc sum\n \t\t\tt = 0\n \t\t\tfor $arg t += $val\n \t\t\treturn t\n\nThe `for` block operates on an array in a loop, and at each iteration provides variables\n`key` and `val` for the current entry.\n\n  \ttotal = sum(1, 2, 3, 4) // returns 10\n\nOften the variables we use to construct an array share the same name as the key we\nuse in the array:\n\n  \tvalues = ( 'name' : name, 'id' : id, )\n\nFor this case we have a shortcut syntax using the tilde (`~`):\n\n  \tkey = 'class'\n  \tvalue = 'ampm'\n  \ta = ( 'key' : key, 'value' : value)\n  \t// or\n  \ta = ( ~key, ~value)\n\nA function can return a value, using the `return` statement. If a function does not\nexplicitly return a value and is chained to another function, then the original `$obj`\nscope will be passed to the next function in the chain. If it returns a value, however,\nthen that value will take over and replace the `$obj` scope for the next function in the\nchain. (Note that a function that returns a value is interpreted, at the call site, as an\nexpression and the returned value will be added to the current scope.)\n\nMaybe another way to think about that last paragraph is that if a function\ndoesn't have a  return statement, it should be considered to have an implicit\n`return $obj` (which brings up questions about copies vs references).\n\nSuppose we have this function defined:\n\n  \tfunc transformer\n \t\t\t$obj['name'] ?= \"anonymous\"\n\nNote: The `?=` operator tests if a name is defined and, if not, assigns the value on the\nright.\n\n  \tsome_array -\u003e transformer() -\u003e print()\n\nThe above passes the object to transformer, which inserts a 'name' key (if it doesn't\nexist already) and, by lacking a return statement, implicitly passes it's original `$obj`\nas the `$obj` to the next function.\n\nThe idea here is that composing allows you to change the original array. Values passed in\nas arguments aren't intended to be changed (they can be, but the changes die when the\nfunction exits).\n\nClosures. I think we can achieve something similar to closures by composing an object with\na function, but not calling the function. The resulting value will be--what should we call\nit, a `Composition`?--something that can be executed but already has it's `$obj` bundled\nwith it. Really it's a function with some extra data tagging along with it. You can even\ncompose something with it when you call it, and the earlier composition will still be\nfirst in the scope resolution hierarchy.\n\nIf the function is a member of an array, then other properties of that array should be\navailable to the function by default. (They will be, under composition, because they will\nbe found in `$obj`, as long as they are not hidden by something in `$loc` or `$arg`, which\nare checked first.)\n\nHere is an example from PHP and wCommon. We have a function, `addButtons`, that takes\nan array of button definitions (each of those is itself an array), rearranges the info for\neach button into the standard parts of an HTML element, and calls `addElement` to add the\nbutton to our eventual HTML output (also wrapping it in its own 'p' element). `addButtons`\nis defined in a 'FormBuilder' object that has a member, `cp`, a Composer, to accumulate\nthe HTML output. So in the PHP code, `$this` refers to the FormBuilder, and `cp` is its\nmember.\n\nHere is how this function looks in PHP:\n\n  \t// in PHP\n  \tfunction addButtons($butts) {\n \t\t\t$this-\u003ecp-\u003ebeginElement('p');\n \t\t\tforeach ($butts as $items) {\n\t\t\t\t\tif (!$items['type']) { $items['type'] = 'submit'; }\n\t\t\t\t\tif (!$items['name']) { $items['name'] = self::KEY_ACTION; }\n\t\t\t\t\tif (!$items['id']) { $items['id'] = $items['name'] . '-' . $items['value']; }\n\t\t\t\t\t$attribs = array_intersect_key($items, array_flip('type', 'name', 'id', 'value'));\n\t\t\t\t\t$attribs = array_merge($attribs, $items['xattr']);\n\t\t\t\t\t$this-\u003ecp-\u003eaddElement('button', $attribs, $items['content']);\n \t\t\t}\n \t\t\t$this-\u003ecp-\u003eendElement();\n  \t}\n\nIf a button wasn't defined with a 'type', then it gets the default 'submit'. A default\n'name' is similarly supplied. If it doesn't have an 'id', then one is created by\nconcatenating 'name' and 'value'. The caller can pass in extra attributes (such as \"class\"\nor \"onclick\") via 'xattr'. And so on (you can go check it out in\n[wCommon](https://github.com/wevrem/wCommon) for more details).\n\nAnd here is how it could look in Wry:\n\n  \t// in Wry\n  \tfunc addButtons   // pass in an array of button objects\n \t\t\tcp-\u003ebeginElement('p')\n \t\t\tfor $args\n\t\t\t\t\t$val.type ?= 'submit'\n\t\t\t\t\t$val.name ?= keyAction\n\t\t\t\t\t$val.id ?= $val.name + '-' + $val.value\n\t\t\t\t\tattribs = $val['type', 'id', 'name', 'value'] + $val.xattr\n\t\t\t\t\tcp-\u003eaddElement('button', ~attribs, ~$val.content)\n \t\t\tcp-\u003eendElement()\n\n\nWe would call this function like so:\n\n  \tbuttons\n \t\t\tvalue='submit', content='Submit'\n \t\t\tvalue='cancel', content='Cancel'\n\n  \tfb-\u003eaddButtons(buttons)\n\nwhere `fb` is an object representing a `FormBuilder`.\n\nWith records, *a la* [SAM](https://github.com/mbakeranalecta/sam), we could even do this:\n\n  \tbuttons :: value, content\n \t\t\t'submit', 'Submit'\n \t\t\t'cancel', 'Cancel'\n\nLet's look at the function that the one above relies on, it is `getElement` in the\ncomposer object. This has an interesting (and probably poorly designed) signature where it\nwill accept an array of attributes, but if instead the caller passes a string, it will be\ninterpreted as a \"class\" attribute and an attribute array constructed from it.\nThis makes it convenient to call the function with a\nsimple class name (which will often be the use case) instead of having to construct an\narray around that class name. In Wry we don't have to do those sorts of gymnastics.\n\n  \t// in PHP\n  \tprotected static function getElement($elem, $attribs=[], $content='', $close=false) {\n \t\t\tif (is_string($attribs)) { $attribs = [ 'class'=\u003e$attribs ]; }\n \t\t\telse if (!is_array($attribs)) { $attribs = []; }\n \t\t\tif ($class = $attribs['class']) { self::registerClass($class); }\n \t\t\t$attribString = implode(' ', array_key_map('attribParam', array_filter($attribs, 'is_not_null')));   //(1)\n \t\t\tif (self::isEmptyElement($elem)) {\n\t\t\t\t\treturn '\u003c' . $elem . prefixIfCe($attribString, ' ') . ' /\u003e';\n \t\t\t} else {\n\t\t\t\t\treturn '\u003c' . $elem . prefixIfCe($attribString, ' ') . '\u003e' . $content . ( $close ? \"\u003c/$elem\u003e\" : '' );\n \t\t\t}\n  \t}\n\n  \t// in Wry\n  \tfunc getElement   //(elem, class?, attribs?, content?, close?=false)\n \t\t\tif class attribs.class = class\n \t\t\tregisterClass(attribs.class)\n \t\t\ts\n\t\t\t\t\t\"\u003c\" + $0 + \" \"\n\t\t\t\t\tattribs-\u003efiltered()-\u003emapped(composeAttrib)-\u003ejoined(' ')   //(1)\n\t\t\t\t\tif emptyElements-\u003econtains($0) \" /\u003e\"\n\t\t\t\t\telse\n\t\t\t\t\t\t\"\u003e\" + content\n\t\t\t\t\t\tif close \"\u003c/\" + $0 + \"\u003e\"\n \t\t\treturn s-\u003ejoined()\n  \t}\n\nIn both versions, the logic at (1) turns the array of keys and values into a string that\nwill work as\nattributes of an HTML tag, like so:\n\n  \tkey1=\"val1\" key2=\"val2\" ...\n\nThe helper functions would be defined as follows. Note that the function passed in to\n`mapped` will be called with two arguments: `key` and `val`.\n\n  \tfunc composeAttrib\n \t\t\treturn key + '=\"' + val + '\"'\n\nNote: this is an example of something cool: when we have to generate a string from pieces\nand have logic that turns on or off pieces, the simple way to do it is to declare an array\nin a block, and make use of flow control statements inside the block. Then at the end we\njoin the pieces of the array.\n\nComposing can be chained: `firstArray -\u003e secondArray -\u003e myFunc` means both arrays will end\nup being sent as `$obj` to the function `myFunc`.\nInside `myFunc`, `$obj` will be a composition of both arrays, not that `myFunc` will be\nable to distinguish them. Really it means that when resolving names, `secondArray` will be\nsearched first and `firstArray` will be searched last. Name lookup to retrieve a value\nwill work its way down the chain. Name lookup for assignment will stop at the first level\nof the hierarchy.\n\nYou can pre-compose and then pass that to a function later:\n\n  \tnew_array = firstArray -\u003e secondArray\n\n  \tnew_array-\u003edoSomething()\n\nComposition preserves references to objects, so `new_array` contains a reference to the\ntwo originals, and and they are combined in a hierarchy, like a stack.\n\nWhat do you get when you compose different things?\n\n  \tmyArray -\u003e anotherArray   // returns an array that combines both `myArray` and `anotherArray`.\n  \t// They won't be merged, they will be arranged more like a stack with `anotherArray`\n  \t// at the top and, thus, searched first.\n\n  \tanArray -\u003e aFunction   // returns a function with `anArray` composed as its `$obj`\n\n  \tanArray -\u003e map(mapFunc)   // this composes `anArray` with, and then calls, the `map` function\n\n  \tanArray-\u003emyFunc\u003cmyArgs\u003e   // composes `anArray` with `myFunc` using arguments `myArgs`\n  \t// but does not execute the function\n\nArguments can be composed with a function, but the function not called, by using `\u003c` and\n`\u003e` around the arguments. If the result is further composed with arguments, or the\nfunction later called with arguments, the most recent arguments will \"win\" (because `$arg`\nis not stacked up like `$obj`, there is only ever one `$arg` attached to a function so\nmultiple compositions will just combine `$arg`s).\n\nIf a non-array item is thrown into a composition, it will first be converted to an array,\nusing default integer keys.\n\n### Comments\n\nTwo slashes create a comment to the end of the line.\nI don't think we'll have multi-line comments like `/**/`.\nInstead, the keyword `comment` will change the line where it appears *and all indented lines below*\ninto comments. This make it easy to comment out a block of code, you just prepend `comment`\n\n\tcomment do\n\t\t...some stuff...\n\nThe `comment` comments out the `do` and all the code belonging to the `do` block.\n\n(2018--07-20) Alternatively, a comment block could be introduced by ///.\n\nI've used # as a comment character in some places when writing about Wry. But  I\nthink # needs to be reserved for labeling scopes and loops, so it is out as a\ncomment character.\n\nsection : Values and references\n\n\tArrays exhibit reference semantics, but standard operations will produce\n\tcopies of things:\n\n\t```\n\t\ta = ( name='mike', age='42' )\n\t\tb = a\n\n\tThe above will make a _copy_ of `a`, referred to as `b`. If you change either\n\t`a` or `b`, it won't affect the other.\n\n\t```\n\t\ta.name-\u003eprint()   // prints 'mike'\n\t\tb.name-\u003eprint()   // prints 'mike'\n\t\ta.name = 'tom'\n\t\ta.name-\u003eprint()   // still prints 'mike'\n\t\tb.name-\u003eprint()   // now prints 'tom'\n\n\tActually in the run-time, `b` refers to `a`, but it is a read-only reference.\n\tWriting to either `a` or `b` will mask the original values. Here is how that\n\tworks:\n\n\tLet `A` represent the array we created above, with keys 'name' and 'age'.\n\tOriginally, variable `a` points to that array, so\n\n\t```(txt)\n\t\ta --\u003e A\n\n\twhen the runtime encounters the statement `b = a` it alters `a` by chaining\n\twith an empty array\n\n\t```(txt)\n\t\ta --\u003e [] --\u003e A\n\n\tand `b` similarly becomes\n\n\t```(txt)\n\t\tb --\u003e [] --\u003e A\n\n\tBoth `b` and `a` point to `A`, but there is an empty array first in each\n\tchain. When looking up key `name`, the search will skip the empty array and\n\tfind the value in `A` (which is how we achieve a read-only reference). When\n\twe write to either `b` or `a`, the assignment will happen at the bottom of\n\tthe chain, in the empty array. Thus they share an original reference, but\n\tcan't clobber each other with writes.\n\n\tHow do we make a change that we know will affect the original `A`, even if\n\tshared by different references? We have to use chaining and tags.\n\n\t```(txt)\n\t\tb = a -\u003e []\n\n\tor\n\n\t```(txt)\n\t\tb \u003c- a\n\n\t!!!\n\t\t2018-04-26: Hmm... I don't like this. I think for reference semantics we\n\t\tshould have a different operator, like \u0026= that makes one object refer to\n\t\tanother. Think about it.\n\n\tNow if we make a change to `a`, it will be visible from `b`.\n\n\t```\n\t\ta.name = 'tom'\n\t\tb.name-\u003eprint()   // prints 'tom'\n\n\tUsing our \"pointer\" diagrams, this is what happens when we execute `b \u003c- a`:\n\n\t```\n\t\ta --\u003e A\n\t\tb --\u003e [] --\u003e A\n\n\tIn this case, `a` still points directly to `A` with no empty array sitting in\n\tbetween. So changes through `a` are visible in `b`, which was not the case\n\tabove. However, those changes are one-sided. If we make a change to `b` it\n\twill not affect `a`. To achieve that we need to use tags:\n\n\t```\n\t\tb \u003c- a#orig\n\t\tb@orig.name = 'tom'\n\n\t\tThis is what the pointers look like:\n\n\t```\n\t\ta --\u003e A\n\t\tb --\u003e [] --\u003e A#orig\n\n\tNow the badge on `b` jumps over the empty array and makes changes directly in\n\t`a`.\n\n\tThis is how we would, for example, maintain a shared counter.\n\n\t```\n\t\tcounter #base\n\t\t\tcount = 0\n\t\t\tfunc incrementCounter\n\t\t\t\t$0 ?= 1\n\t\t\t\t@base.count += $0\n\n\t\ta \u003c- counter\n\t\tb \u003c- counter\n\t\ta-\u003eincrementCounter(1)\n\t\tb-\u003eincrementCounter(5)\n\n\tTo make this work, we have to set an explicit tag on `counter`, and reference\n\tthat tag within the `incrementCounter` method.\n\n\tAs part of this, I think we need to be able to use references with tags and\n\tbadges. Maybe with parens:\n\n\t```\n\t\tcounter #(theBaseName)\n\n\t\t...   // inside the function\n\t\t@(theBaseName).count\n\n\t!!!\n\t\tActually, I'm thinking it should be wrapped in colons, which hints back at\n\t\tthe way of declaring arrays with evaluated keys.\n\t\t```\n\t\t\tmy_array = ( \u003csome expression\u003e : \u003csome value\u003e)\n\n\t\tHere, because of the colon, `some expression` can be a variable or an\n\t\texpression or what have you, something you can't do if you use an equal\n\t\tsign.\n\n\t\tSimilarly, we could evaluate an expression to determine the tag/badge by\n\t\twrapping it in colons:\n\n\t\t```\n\t\t\tcounter #:\u003csome expression\u003e:\n\n\t\t\t@:\u003csome expression\u003e:-\u003ecount()\n\n\t\tIt's maybe not as visible as the parens, but syntax highlighting could\n\t\thelp with that.\n\n\t\tWhat if the expression involved the ternary operator `?:`? It would\n\t\tprobably have higher precedence than a \"wrapping\" operator implemented\n\t\twith colons... but still, seems confusing. Maybe parens are best for this\n\t\tsituation because they are natural \"wrapping\" operators.\n\n\tWe should be able to put tags on items as they are declared, like we did\n\tabove with `counter` and also when composing. Those tags should not clobber\n\teach other. So maybe each chain keeps track of what tags have been added\n\t(meaning if we tag an item like `counter` above it will be automatically\n\tconverted into a chain with the tag) and then when composed later with other\n\tthings, those chains will have slots for additional tags.\n\n\tDo we need to distinguish between chains formed by the runtime (for example,\n\tfor copy-on-write and tagged objects as described above) and those formed\n\texplicitly by the user? Or does it matter?\n\nsection : Other things I thought of\n\n\tIt might be handy to have statement blocks for logical and, or and not (and\n\tmaybe, xor) so that conditions can be constructed in a clear fashion.\n\n\t```\n\t\tout_of_range = and\n\t\t\tval \u003c 32\n\t\t\tval \u003e 47\n\n\t\tor   // this is interesting but what are we doing with the result?\n\t\t\tval == 100\n\t\t\tand\n\t\t\t\tval \u003c 32\n\t\t\t\tval \u003e 47\n\t\timportant number = not or\n\t\t\tval == 100\n\t\t\tand\n\t\t\t\tval \u003c 32\n\t\t\t\tval \u003e 47\n\tWhat if you sugared the == operator to allow for epsilon testing of floats?\n\n\t```\n\t\tif x ==\u003c1e-6\u003e y\n\t\t\t...\n\n\t``` : Hashing\n\t\tCheck out details on SIP hashes.\n\nsection : Name lookup\n\n\tMembership lookup using `.` operator. The key must be a valid name.\n\n\t``` dot operator\n\t\tteam.boss.firstName\n\n\tSubscript lookup using `[]` operator. The key can be any string, doesn't have\n\tto be a valid name, and the expression inside brackets must evaluate to a\n\tstring (or an integer).\n\n\t```\n\t\tteam['boss']['firstName']\n\n\tKey path lookup using the `~` expansion operator. The idea here is we have a\n\tstring of arbitrary period-separated keys and the expansion operator will\n\tallow dynamic (run-time) resolution of the keys, drilling down into the\n\thierarchy.\n\n\t```\n\t\tkeypath = 'boss.firstName'\n\t\tteam~keypath\n\n\tThe syntax is `\u003cname\u003e \u003c~\u003e \u003cname\u003e` or `\u003cname\u003e \u003c~\u003e \u003c(\u003e \u003cexpression\u003e \u003c)\u003e`. This\n\tis a binary operator in contrast with the other cases of expansion with are\n\tunary.\n\n\tWhat type of code would you write to do this without a special syntax?\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwevre%2Fwry","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwevre%2Fwry","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwevre%2Fwry/lists"}