Browse Source

Initial commit. Extracted from Trapped.

Herbert Vojčík 9 years ago
commit
084a65a91b
12 changed files with 1059 additions and 0 deletions
  1. 7 0
      .gitignore
  2. 95 0
      Gruntfile.js
  3. 22 0
      LICENSE-MIT
  4. 85 0
      README.md
  5. 33 0
      bower.json
  6. 8 0
      deploy.js
  7. 9 0
      devel.js
  8. 23 0
      index.html
  9. 5 0
      local.amd.json
  10. 42 0
      package.json
  11. 628 0
      src/Lyst.js
  12. 102 0
      src/Lyst.st

+ 7 - 0
.gitignore

@@ -0,0 +1,7 @@
+/node_modules/
+/bower_components/
+
+/test_runner.js
+
+/config.js
+/the.js

+ 95 - 0
Gruntfile.js

@@ -0,0 +1,95 @@
+'use strict';
+
+module.exports = function (grunt) {
+    var path = require('path');
+
+    // These plugins provide necessary tasks.
+    grunt.loadNpmTasks('grunt-contrib-clean');
+    grunt.loadNpmTasks('grunt-contrib-requirejs');
+    grunt.loadNpmTasks('grunt-execute');
+    grunt.loadNpmTasks('amber-dev');
+
+    // Default task.
+    grunt.registerTask('default', ['amberc:all']);
+    grunt.registerTask('test', ['amberc:test_runner', 'execute:test_runner', 'clean:test_runner']);
+    grunt.registerTask('devel', ['amdconfig:app', 'requirejs:devel']);
+    grunt.registerTask('deploy', ['amdconfig:app', 'requirejs:deploy']);
+
+    // Project configuration.
+    grunt.initConfig({
+        // Metadata.
+        // pkg: grunt.file.readJSON(''),
+        banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
+        '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
+        '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' +
+        '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
+        ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n',
+        // task configuration
+        amberc: {
+            options: {
+                amber_dir: path.join(__dirname, "bower_components", "amber"),
+                library_dirs: ['src'],
+                closure_jar: ''
+            },
+            all: {
+                src: [
+                    'src/Lyst.st' // list all sources in dependency order
+                    // list all tests in dependency order
+                ],
+                amd_namespace: 'lyst',
+                libraries: ['SUnit', 'Web']
+            },
+            test_runner: {
+                src: ['node_modules/amber-dev/lib/Test.st'],
+                libraries: [
+                    /* add dependencies packages here */
+                    'Lyst', /* add other code-to-test packages here */
+                    'SUnit'
+                    /* add other test packages here */
+                ],
+                main_class: 'NodeTestRunner',
+                output_name: 'test_runner'
+            }
+        },
+
+        amdconfig: {app: {dest: 'config.js'}},
+
+        requirejs: {
+            deploy: {
+                options: {
+                    mainConfigFile: "config.js",
+                    onBuildWrite: function (moduleName, path, contents) {
+                        return moduleName === "config" ? contents + "\nrequire.config({map:{'*':{app:'deploy'}}});" : contents;
+                    },
+                    pragmas: {
+                        excludeIdeData: true,
+                        excludeDebugContexts: true
+                    },
+                    include: ['config', 'node_modules/requirejs/require', 'deploy'],
+                    out: "the.js"
+                }
+            },
+            devel: {
+                options: {
+                    mainConfigFile: "config.js",
+                    onBuildWrite: function (moduleName, path, contents) {
+                        return moduleName === "config" ? contents + "\nrequire.config({map:{'*':{app:'devel'}}});" : contents;
+                    },
+                    include: ['config', 'node_modules/requirejs/require'],
+                    out: "the.js"
+                }
+            }
+        },
+
+        execute: {
+            test_runner: {
+                src: ['test_runner.js']
+            }
+        },
+
+        clean: {
+            test_runner: ['test_runner.js']
+        }
+    });
+
+};

+ 22 - 0
LICENSE-MIT

@@ -0,0 +1,22 @@
+Copyright (c) 2015 Herbert Vojčík
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.

+ 85 - 0
README.md

@@ -0,0 +1,85 @@
+Lyst
+====
+Get / set hierarchical data using array-like indexes.
+
+The Lyst index (aka yndex) an array of elements: either strings, numbers
+or a sub-arrays. These are used to denote the (relative) location
+of a piece of data in a hierarchical object, and is used to read or write
+from / to this position.
+
+Elements of a path are equivalent to elements of paths in classic file systems:
+each elements is one step deeper in a tree hierarchy. Thus, to read a data denoted
+by a path, Lyst starts from actual position, reads the contents denoted by first element,
+use the result to read the contents denoted by second elements etc. until the end.
+To write the data, the algorithm is similar to reading one, byt the last element is used
+to write the data instead.
+
+ - if _string_ path element is read from _foo_, `foo at: aString` is performed;
+ - if _string_ path element is written to  _foo_, `foo at: aString put: value` is performed;
+ - if _number_ path element is read from _foo_, `foo at: aNumber` is performed;
+ - if _number_ path element is written to _foo_, `foo at: aNumber put: value` is performed;
+ - if _subarray_ path element `#(bar)` is read from _foo_, `foo bar` is performed;
+ - if _subarray_ path element `#(bar)` is written to _foo_, `foo bar: value` is performed.
+
+API
+----
+
+----
+
+```st
+Object >> atLyst: aCollection ifAbsent: aBlock
+```
+
+For example `container atLyst: #((todos) 1 done) ifAbsent: [...]'` essentially does
+
+	| x |
+	x := container todos at: 1.
+	^ x at: 'done'
+
+But, whenever:
+
+  - `container` fails to perform `todos`, or
+  - `container todos` fails to perform `at:ifAbsent:`, or
+  - `container todos` does not contain index 1, or
+  - `container todos at: 1` fails to perform `at:ifAbsent:`, or
+  - `container todos at: 1` does not contain index 'done',
+
+the `ifAbsent` block value is returned.
+
+----
+
+```st
+Object >> atLyst: aCollection ifAbsent: aBlock put: anObject
+```
+
+For example `container atLyst: #((todos) 1 done) ifAbsent: [...] put: 'foo'` essentially does
+
+	| x |
+	x := container todos at: 1.
+	^ x at: 'done' put: 'foo'
+
+But, whenever:
+
+  - `container` fails to perform `todos`, or
+  - `container todos` fails to perform `at:ifAbsent:`, or
+  - `container todos` does not contain index 1, or
+  - `container todos at: 1` fails to do `at:put:`,
+
+the `ifAbsent` block value is returned.
+
+----
+
+```st
+Lyst class >> parse: aString
+```
+
+Parses a string to get a proper array index to use with `atLyst:` API.
+
+The syntax is resembling Smalltalk literal array syntax very closely.
+For example `Lyst parse: '(value)'` and `Lyst parse: '(todos) 1 done'`
+produce `#((value))` and `#((todos) 1 done)` as results.
+
+Syntactic sugar: as `(foo)` happens often, to denote unary selector,
+it can be written equivalently as `~foo`, to improve readability.
+So above Lyst indexes' parseable string representation
+would likely be written `'~value'` and `'~todos 1 done'` instead.

+ 33 - 0
bower.json

@@ -0,0 +1,33 @@
+{
+  "name": "lyst",
+  "version": "0.1.0",
+  "homepage": "https://github.com/herby/trapped",
+  "authors": [
+    "Herbert Vojčík <herby@mailbox.sk>"
+  ],
+  "description": "Access hierarchical data with array-indexes. For Amber Smalltalk.",
+  "keywords": [
+    "amber",
+    "smalltalk",
+    "data",
+    "hierarchy"
+  ],
+  "license": "MIT",
+  "ignore": [
+    "**/.*",
+    "node_modules",
+    "bower_components",
+    "/*.js",
+    "/*.html",
+    "test",
+    "tests"
+  ],
+  "dependencies": {
+    "amber": ">=0.14.1"
+  },
+  "devDependencies": {
+    "amber-ide-starter-dialog": "^0.1.0",
+    "amber-attic": "^0.1.0",
+    "helios": "^0.3.4"
+  }
+}

+ 8 - 0
deploy.js

@@ -0,0 +1,8 @@
+define([
+    'amber/deploy',
+    // --- packages to be deployed begin here ---
+    "lyst/Lyst"
+    // --- packages to be deployed end here ---
+], function (amber) {
+    return amber;
+});

+ 9 - 0
devel.js

@@ -0,0 +1,9 @@
+define([
+    'amber/devel',
+    './deploy',
+    // --- packages used only during development begin here ---
+    'amber-attic/IDE'
+    // --- packages used only during development end here ---
+], function (amber) {
+    return amber;
+});

+ 23 - 0
index.html

@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+
+  <head>
+    <title>Lyst</title>
+    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+    <meta name="author" content="Herbert Vojčík" />
+    <script type='text/javascript' src='the.js'></script>
+  </head>
+
+  <body>
+  <script type='text/javascript'>
+      require(['app'], function (amber) {
+          amber.initialize({
+            //used for all new packages in IDE
+            'transport.defaultAmdNamespace': "lyst"
+          });
+          require(["amber-ide-starter-dialog"], function (dlg) { dlg.start(); });
+      });
+  </script>
+  </body>
+
+</html>

+ 5 - 0
local.amd.json

@@ -0,0 +1,5 @@
+{
+    "paths": {
+        "lyst": "src"
+    }
+}

+ 42 - 0
package.json

@@ -0,0 +1,42 @@
+{
+  "name": "lyst",
+  "title": "Lyst",
+  "description": "Access hierarchical data with array-indexes. For Amber Smalltalk.",
+  "version": "0.1.0",
+  "homepage": "https://github.com/herby/trapped",
+  "author": {
+    "name": "Herbert Vojčík",
+    "email": "herby@mailbox.sk"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/herby/trapped.git"
+  },
+  "bugs": {
+    "url": "https://github.com/herby/trapped/issues"
+  },
+  "licenses": [
+    {
+      "type": "MIT",
+      "url": "https://github.com/herby/trapped/blob/master/LICENSE-MIT"
+    }
+  ],
+  "engines": {
+    "node": ">= 0.8.0"
+  },
+  "scripts": {
+    "test": "grunt test"
+  },
+  "devDependencies": {
+    "amber-dev": "^0.3.0",
+    "grunt": "^0.4.5",
+    "grunt-contrib-clean": "^0.6.0",
+    "grunt-contrib-requirejs": "^0.4.4",
+    "grunt-execute": "^0.2.2",
+    "requirejs": "^2.1.15"
+  },
+  "keywords": [
+    "Amber",
+    "Smalltalk"
+  ]
+}

+ 628 - 0
src/Lyst.js

@@ -0,0 +1,628 @@
+define("lyst/Lyst", ["amber/boot", "amber_core/Kernel-Objects", "amber_core/Kernel-Collections"], function($boot){
+var $core=$boot.api,nil=$boot.nil,$recv=$boot.asReceiver,$globals=$boot.globals;
+$core.addPackage('Lyst');
+$core.packages["Lyst"].innerEval = function (expr) { return eval(expr); };
+$core.packages["Lyst"].transport = {"type":"amd","amdNamespace":"lyst"};
+
+$core.addClass('Lyst', $globals.Object, [], 'Lyst');
+
+$core.addMethod(
+$core.method({
+selector: "parse:",
+protocol: 'parsing',
+fn: function (message){
+var self=this;
+var result,stack,anArray;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1,$2,$3,$5,$4,$6,$7,$8,$9,$10,$11,$12;
+anArray=$recv(message)._tokenize_(" ");
+result=[];
+stack=[result];
+$recv(anArray)._do_((function(each){
+var asNum,inner,close;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+close=(0);
+close;
+inner=each;
+inner;
+$recv((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+$1=$recv(inner)._notEmpty();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx3.sendIdx["notEmpty"]=1;
+//>>excludeEnd("ctx");
+return $recv($1)._and_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx4) {
+//>>excludeEnd("ctx");
+$2=$recv(inner)._first();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx4.sendIdx["first"]=1;
+//>>excludeEnd("ctx");
+return $recv($2).__eq("(");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx4.sendIdx["="]=1;
+//>>excludeEnd("ctx");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx4) {$ctx4.fillBlock({},$ctx3,3)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx3.sendIdx["and:"]=1;
+//>>excludeEnd("ctx");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,2)});
+//>>excludeEnd("ctx");
+}))._whileTrue_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+inner=$recv(inner)._allButFirst();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx3.sendIdx["allButFirst"]=1;
+//>>excludeEnd("ctx");
+inner;
+$3=stack;
+$5=$recv(stack)._last();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx3.sendIdx["last"]=1;
+//>>excludeEnd("ctx");
+$4=$recv($5)._add_([]);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx3.sendIdx["add:"]=2;
+//>>excludeEnd("ctx");
+return $recv($3)._add_($4);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx3.sendIdx["add:"]=1;
+//>>excludeEnd("ctx");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,4)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["whileTrue:"]=1;
+//>>excludeEnd("ctx");
+$recv((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+$6=$recv(inner)._notEmpty();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx3.sendIdx["notEmpty"]=2;
+//>>excludeEnd("ctx");
+return $recv($6)._and_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx4) {
+//>>excludeEnd("ctx");
+$7=$recv(inner)._last();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx4.sendIdx["last"]=2;
+//>>excludeEnd("ctx");
+return $recv($7).__eq(")");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx4.sendIdx["="]=2;
+//>>excludeEnd("ctx");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx4) {$ctx4.fillBlock({},$ctx3,6)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx3.sendIdx["and:"]=2;
+//>>excludeEnd("ctx");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,5)});
+//>>excludeEnd("ctx");
+}))._whileTrue_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+inner=$recv(inner)._allButLast();
+inner;
+close=$recv(close).__plus((1));
+return close;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,7)});
+//>>excludeEnd("ctx");
+}));
+$8=$recv($recv(inner)._notEmpty())._and_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $recv($recv(inner)._first()).__eq("~");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx3.sendIdx["="]=3;
+//>>excludeEnd("ctx");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,8)});
+//>>excludeEnd("ctx");
+}));
+if($core.assert($8)){
+inner=[$recv(inner)._allButFirst()];
+inner;
+};
+$9=$recv(inner)._isString();
+if($core.assert($9)){
+asNum=$recv($recv(inner)._ifEmpty_((function(){
+return "NaN";
+
+})))._asNumber();
+} else {
+asNum=inner;
+};
+asNum;
+$10=$recv(asNum).__eq(asNum);
+if($core.assert($10)){
+$11=$recv(stack)._last();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["last"]=3;
+//>>excludeEnd("ctx");
+$recv($11)._add_(asNum);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["add:"]=3;
+//>>excludeEnd("ctx");
+} else {
+$recv(inner)._ifNotEmpty_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $recv($recv(stack)._last())._add_(inner);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,15)});
+//>>excludeEnd("ctx");
+}));
+};
+return $recv(close)._timesRepeat_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $recv(stack)._removeLast();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,16)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({each:each,asNum:asNum,inner:inner,close:close},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+$12=result;
+return $12;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"parse:",{message:message,result:result,stack:stack,anArray:anArray},$globals.Lyst.klass)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["message"],
+source: "parse: message\x0a\x09| result stack anArray |\x0a\x09anArray := message tokenize: ' '.\x0a\x09result := #().\x0a\x09stack := { result }.\x0a\x09anArray do: [ :each |\x0a\x09\x09| asNum inner close |\x0a\x09\x09close := 0.\x0a\x09\x09inner := each.\x0a\x09\x09[ inner notEmpty and: [ inner first = '(' ]] whileTrue: [ inner := inner allButFirst. stack add: (stack last add: #()) ].\x0a\x09\x09[ inner notEmpty and: [ inner last = ')' ]] whileTrue: [ inner := inner allButLast. close := close + 1 ].\x0a\x09\x09(inner notEmpty and: [ inner first = '~' ]) ifTrue: [ inner := { inner allButFirst } ].\x0a\x09\x09asNum := inner isString ifTrue: [ (inner ifEmpty: [ 'NaN' ]) asNumber ] ifFalse: [ inner ].\x0a\x09\x09asNum = asNum ifTrue: [ stack last add: asNum ] ifFalse: [\x0a\x09\x09\x09inner ifNotEmpty: [ stack last add: inner ] ].\x0a\x09\x09close timesRepeat: [ stack removeLast ] ].\x0a\x09^ result",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["tokenize:", "do:", "whileTrue:", "and:", "notEmpty", "=", "first", "allButFirst", "add:", "last", "allButLast", "+", "ifTrue:", "ifTrue:ifFalse:", "isString", "asNumber", "ifEmpty:", "ifNotEmpty:", "timesRepeat:", "removeLast"]
+}),
+$globals.Lyst.klass);
+
+$core.addMethod(
+$core.method({
+selector: "atYndexIn:ifAbsent:",
+protocol: '*Lyst',
+fn: function (anObject,aBlock){
+var self=this;
+var receiver,selector,result;
+function $MessageNotUnderstood(){return $globals.MessageNotUnderstood||(typeof MessageNotUnderstood=="undefined"?nil:MessageNotUnderstood)}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $5,$4,$3,$2,$1,$6,$7;
+var $early={};
+try {
+selector=self._first();
+receiver=$recv(anObject)._yourself();
+$recv((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+result=$recv(receiver)._perform_(selector);
+return result;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}))._on_do_($MessageNotUnderstood(),(function(mnu){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+$5=$recv(mnu)._message();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["message"]=1;
+//>>excludeEnd("ctx");
+$4=$recv($5)._selector();
+$3=$recv($4).__eq(selector);
+$2=$recv($3)._and_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $recv($recv(mnu)._receiver()).__eq_eq(receiver);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,3)});
+//>>excludeEnd("ctx");
+}));
+$1=$recv($2)._and_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $recv($recv($recv(mnu)._message())._arguments())._isEmpty();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,4)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["and:"]=1;
+//>>excludeEnd("ctx");
+if(!$core.assert($1)){
+$recv(mnu)._resignal();
+};
+$6=$recv(aBlock)._value();
+throw $early=[$6];
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({mnu:mnu},$ctx1,2)});
+//>>excludeEnd("ctx");
+}));
+$7=result;
+return $7;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"atYndexIn:ifAbsent:",{anObject:anObject,aBlock:aBlock,receiver:receiver,selector:selector,result:result},$globals.Array)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anObject", "aBlock"],
+source: "atYndexIn: anObject ifAbsent: aBlock\x0a\x09| receiver selector result |\x0a\x09selector := self first.\x0a\x09receiver := anObject yourself. \x22JSObjectProxy hack\x22\x0a\x09\x0a\x09[ result := receiver perform: selector ]\x0a\x09on: MessageNotUnderstood do: [ :mnu |\x0a\x09\x09((mnu message selector = selector\x0a\x09\x09\x09and: [ mnu receiver == receiver ])\x0a\x09\x09\x09and: [ mnu message arguments isEmpty ])\x0a\x09\x09\x09ifFalse: [ mnu resignal ].\x0a\x09\x09^ aBlock value ].\x0a\x09^ result",
+referencedClasses: ["MessageNotUnderstood"],
+//>>excludeEnd("ide");
+messageSends: ["first", "yourself", "on:do:", "perform:", "ifFalse:", "and:", "=", "selector", "message", "==", "receiver", "isEmpty", "arguments", "resignal", "value"]
+}),
+$globals.Array);
+
+$core.addMethod(
+$core.method({
+selector: "atYndexIn:ifAbsent:put:",
+protocol: '*Lyst',
+fn: function (anObject,aBlock,anotherObject){
+var self=this;
+var receiver,selector,arguments_,result;
+function $MessageNotUnderstood(){return $globals.MessageNotUnderstood||(typeof MessageNotUnderstood=="undefined"?nil:MessageNotUnderstood)}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $5,$4,$3,$2,$1,$6,$7;
+var $early={};
+try {
+selector=$recv(self._first())._asMutator();
+receiver=$recv(anObject)._yourself();
+arguments_=[anotherObject];
+$recv((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+result=$recv(receiver)._perform_withArguments_(selector,arguments_);
+return result;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}))._on_do_($MessageNotUnderstood(),(function(mnu){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+$5=$recv(mnu)._message();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["message"]=1;
+//>>excludeEnd("ctx");
+$4=$recv($5)._selector();
+$3=$recv($4).__eq(selector);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["="]=1;
+//>>excludeEnd("ctx");
+$2=$recv($3)._and_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $recv($recv(mnu)._receiver()).__eq_eq(receiver);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,3)});
+//>>excludeEnd("ctx");
+}));
+$1=$recv($2)._and_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+return $recv($recv($recv(mnu)._message())._arguments()).__eq(arguments_);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,4)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["and:"]=1;
+//>>excludeEnd("ctx");
+if(!$core.assert($1)){
+$recv(mnu)._resignal();
+};
+$6=$recv(aBlock)._value();
+throw $early=[$6];
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({mnu:mnu},$ctx1,2)});
+//>>excludeEnd("ctx");
+}));
+$7=result;
+return $7;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"atYndexIn:ifAbsent:put:",{anObject:anObject,aBlock:aBlock,anotherObject:anotherObject,receiver:receiver,selector:selector,arguments_:arguments_,result:result},$globals.Array)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anObject", "aBlock", "anotherObject"],
+source: "atYndexIn: anObject ifAbsent: aBlock put: anotherObject\x0a\x09| receiver selector arguments result |\x0a\x09selector := self first asMutator.\x0a\x09receiver := anObject yourself. \x22JSObjectProxy hack\x22\x0a\x09arguments := { anotherObject }.\x0a\x09\x0a\x09[ result := receiver perform: selector withArguments: arguments ]\x0a\x09on: MessageNotUnderstood do: [ :mnu |\x0a\x09\x09((mnu message selector = selector\x0a\x09\x09\x09and: [ mnu receiver == receiver ])\x0a\x09\x09\x09and: [ mnu message arguments = arguments ])\x0a\x09\x09\x09ifFalse: [ mnu resignal ].\x0a\x09\x09^ aBlock value ].\x0a\x09^ result",
+referencedClasses: ["MessageNotUnderstood"],
+//>>excludeEnd("ide");
+messageSends: ["asMutator", "first", "yourself", "on:do:", "perform:withArguments:", "ifFalse:", "and:", "=", "selector", "message", "==", "receiver", "arguments", "resignal", "value"]
+}),
+$globals.Array);
+
+$core.addMethod(
+$core.method({
+selector: "atYndexIn:ifAbsent:",
+protocol: '*Lyst',
+fn: function (anObject,aBlock){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1,$2;
+var $early={};
+try {
+$1=$recv(anObject)._respondsTo_("at:ifAbsent:");
+$recv($1)._ifTrue_ifFalse_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+$2=$recv(anObject)._at_ifAbsent_(self,aBlock);
+throw $early=[$2];
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}),aBlock);
+return self;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"atYndexIn:ifAbsent:",{anObject:anObject,aBlock:aBlock},$globals.Number)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anObject", "aBlock"],
+source: "atYndexIn: anObject ifAbsent: aBlock\x0a\x09(anObject respondsTo: #at:ifAbsent:)\x0a\x09\x09ifTrue: [ ^ anObject at: self ifAbsent: aBlock ]\x0a\x09\x09ifFalse: aBlock",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["ifTrue:ifFalse:", "respondsTo:", "at:ifAbsent:"]
+}),
+$globals.Number);
+
+$core.addMethod(
+$core.method({
+selector: "atYndexIn:ifAbsent:put:",
+protocol: '*Lyst',
+fn: function (anObject,aBlock,anotherObject){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1,$2;
+var $early={};
+try {
+$1=$recv(anObject)._respondsTo_("at:put:");
+$recv($1)._ifTrue_ifFalse_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+$2=$recv(anObject)._at_put_(self,anotherObject);
+throw $early=[$2];
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}),aBlock);
+return self;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"atYndexIn:ifAbsent:put:",{anObject:anObject,aBlock:aBlock,anotherObject:anotherObject},$globals.Number)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anObject", "aBlock", "anotherObject"],
+source: "atYndexIn: anObject ifAbsent: aBlock put: anotherObject\x0a\x09(anObject respondsTo: #at:put:)\x0a\x09\x09ifTrue: [ ^ anObject at: self put: anotherObject ]\x0a\x09\x09ifFalse: aBlock",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["ifTrue:ifFalse:", "respondsTo:", "at:put:"]
+}),
+$globals.Number);
+
+$core.addMethod(
+$core.method({
+selector: "atLyst:ifAbsent:",
+protocol: '*Lyst',
+fn: function (aCollection,aBlock){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $2,$1;
+var $early={};
+try {
+$1=$recv(aCollection)._inject_into_(self,(function(soFar,segment){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(segment)._atYndexIn_ifAbsent_(soFar,(function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx3) {
+//>>excludeEnd("ctx");
+$2=$recv(aBlock)._value();
+throw $early=[$2];
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,2)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({soFar:soFar,segment:segment},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+return $1;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"atLyst:ifAbsent:",{aCollection:aCollection,aBlock:aBlock},$globals.Object)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aCollection", "aBlock"],
+source: "atLyst: aCollection ifAbsent: aBlock\x0a\x09^ aCollection inject: self into: [ :soFar :segment |\x0a\x09\x09segment atYndexIn: soFar ifAbsent: [ ^ aBlock value ]]",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["inject:into:", "atYndexIn:ifAbsent:", "value"]
+}),
+$globals.Object);
+
+$core.addMethod(
+$core.method({
+selector: "atYndexIn:ifAbsent:",
+protocol: '*Lyst',
+fn: function (anObject,aBlock){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1;
+$1=$recv(aBlock)._value();
+return $1;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"atYndexIn:ifAbsent:",{anObject:anObject,aBlock:aBlock},$globals.Object)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anObject", "aBlock"],
+source: "atYndexIn: anObject ifAbsent: aBlock\x0a\x09^ aBlock value",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["value"]
+}),
+$globals.Object);
+
+$core.addMethod(
+$core.method({
+selector: "atYndexIn:ifAbsent:put:",
+protocol: '*Lyst',
+fn: function (anObject,aBlock,anotherObject){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1;
+$1=$recv(aBlock)._value();
+return $1;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"atYndexIn:ifAbsent:put:",{anObject:anObject,aBlock:aBlock,anotherObject:anotherObject},$globals.Object)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anObject", "aBlock", "anotherObject"],
+source: "atYndexIn: anObject ifAbsent: aBlock put: anotherObject\x0a\x09^ aBlock value",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["value"]
+}),
+$globals.Object);
+
+$core.addMethod(
+$core.method({
+selector: "atYndexIn:ifAbsent:",
+protocol: '*Lyst',
+fn: function (anObject,aBlock){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1,$2;
+var $early={};
+try {
+$1=$recv(anObject)._respondsTo_("at:ifAbsent:");
+$recv($1)._ifTrue_ifFalse_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+$2=$recv(anObject)._at_ifAbsent_(self,aBlock);
+throw $early=[$2];
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}),aBlock);
+return self;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"atYndexIn:ifAbsent:",{anObject:anObject,aBlock:aBlock},$globals.String)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anObject", "aBlock"],
+source: "atYndexIn: anObject ifAbsent: aBlock\x0a\x09(anObject respondsTo: #at:ifAbsent:)\x0a\x09\x09ifTrue: [ ^ anObject at: self ifAbsent: aBlock ]\x0a\x09\x09ifFalse: aBlock",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["ifTrue:ifFalse:", "respondsTo:", "at:ifAbsent:"]
+}),
+$globals.String);
+
+$core.addMethod(
+$core.method({
+selector: "atYndexIn:ifAbsent:put:",
+protocol: '*Lyst',
+fn: function (anObject,aBlock,anotherObject){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1,$2;
+var $early={};
+try {
+$1=$recv(anObject)._respondsTo_("at:put:");
+$recv($1)._ifTrue_ifFalse_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+$2=$recv(anObject)._at_put_(self,anotherObject);
+throw $early=[$2];
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}),aBlock);
+return self;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"atYndexIn:ifAbsent:put:",{anObject:anObject,aBlock:aBlock,anotherObject:anotherObject},$globals.String)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anObject", "aBlock", "anotherObject"],
+source: "atYndexIn: anObject ifAbsent: aBlock put: anotherObject\x0a\x09(anObject respondsTo: #at:put:)\x0a\x09\x09ifTrue: [ ^ anObject at: self put: anotherObject ]\x0a\x09\x09ifFalse: aBlock",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["ifTrue:ifFalse:", "respondsTo:", "at:put:"]
+}),
+$globals.String);
+
+});

+ 102 - 0
src/Lyst.st

@@ -0,0 +1,102 @@
+Smalltalk createPackage: 'Lyst'!
+Object subclass: #Lyst
+	instanceVariableNames: ''
+	package: 'Lyst'!
+
+!Lyst class methodsFor: 'parsing'!
+
+parse: message
+	| result stack anArray |
+	anArray := message tokenize: ' '.
+	result := #().
+	stack := { result }.
+	anArray do: [ :each |
+		| asNum inner close |
+		close := 0.
+		inner := each.
+		[ inner notEmpty and: [ inner first = '(' ]] whileTrue: [ inner := inner allButFirst. stack add: (stack last add: #()) ].
+		[ inner notEmpty and: [ inner last = ')' ]] whileTrue: [ inner := inner allButLast. close := close + 1 ].
+		(inner notEmpty and: [ inner first = '~' ]) ifTrue: [ inner := { inner allButFirst } ].
+		asNum := inner isString ifTrue: [ (inner ifEmpty: [ 'NaN' ]) asNumber ] ifFalse: [ inner ].
+		asNum = asNum ifTrue: [ stack last add: asNum ] ifFalse: [
+			inner ifNotEmpty: [ stack last add: inner ] ].
+		close timesRepeat: [ stack removeLast ] ].
+	^ result
+! !
+
+!Array methodsFor: '*Lyst'!
+
+atYndexIn: anObject ifAbsent: aBlock
+	| receiver selector result |
+	selector := self first.
+	receiver := anObject yourself. "JSObjectProxy hack"
+	
+	[ result := receiver perform: selector ]
+	on: MessageNotUnderstood do: [ :mnu |
+		((mnu message selector = selector
+			and: [ mnu receiver == receiver ])
+			and: [ mnu message arguments isEmpty ])
+			ifFalse: [ mnu resignal ].
+		^ aBlock value ].
+	^ result
+!
+
+atYndexIn: anObject ifAbsent: aBlock put: anotherObject
+	| receiver selector arguments result |
+	selector := self first asMutator.
+	receiver := anObject yourself. "JSObjectProxy hack"
+	arguments := { anotherObject }.
+	
+	[ result := receiver perform: selector withArguments: arguments ]
+	on: MessageNotUnderstood do: [ :mnu |
+		((mnu message selector = selector
+			and: [ mnu receiver == receiver ])
+			and: [ mnu message arguments = arguments ])
+			ifFalse: [ mnu resignal ].
+		^ aBlock value ].
+	^ result
+! !
+
+!Number methodsFor: '*Lyst'!
+
+atYndexIn: anObject ifAbsent: aBlock
+	(anObject respondsTo: #at:ifAbsent:)
+		ifTrue: [ ^ anObject at: self ifAbsent: aBlock ]
+		ifFalse: aBlock
+!
+
+atYndexIn: anObject ifAbsent: aBlock put: anotherObject
+	(anObject respondsTo: #at:put:)
+		ifTrue: [ ^ anObject at: self put: anotherObject ]
+		ifFalse: aBlock
+! !
+
+!Object methodsFor: '*Lyst'!
+
+atLyst: aCollection ifAbsent: aBlock
+	^ aCollection inject: self into: [ :soFar :segment |
+		segment atYndexIn: soFar ifAbsent: [ ^ aBlock value ]]
+!
+
+atYndexIn: anObject ifAbsent: aBlock
+	^ aBlock value
+!
+
+atYndexIn: anObject ifAbsent: aBlock put: anotherObject
+	^ aBlock value
+! !
+
+!String methodsFor: '*Lyst'!
+
+atYndexIn: anObject ifAbsent: aBlock
+	(anObject respondsTo: #at:ifAbsent:)
+		ifTrue: [ ^ anObject at: self ifAbsent: aBlock ]
+		ifFalse: aBlock
+!
+
+atYndexIn: anObject ifAbsent: aBlock put: anotherObject
+	(anObject respondsTo: #at:put:)
+		ifTrue: [ ^ anObject at: self put: anotherObject ]
+		ifFalse: aBlock
+! !
+