Skip to content

Latest commit

 

History

History
239 lines (179 loc) · 9.28 KB

README.md

File metadata and controls

239 lines (179 loc) · 9.28 KB

Adrian's else clause for Arrays and Objects

You can find Adrian's current (2024-12-03) implementation of the else clause for Arrays and Objects in the Adrian-tfg branch of the ULL-ESIT-PL/babel-tanhauhau repo.

Important

Warning: This is work in progress. These comments can be outdated.

Place yourself in the array-else directory:

➜  array-else git:(main) ✗ pwd -P
/Users/casianorodriguezleon/campus-virtual/2324/learning/babel-learning/src/array-else

Here I am using the adrianparser and adrianbabel links to compile the code:

➜  array-else git:(main) ls -l ../../node_modules/.bin/adrian*
lrwxr-xr-x@ 1 casianorodriguezleon  staff  129  2 dic 09:30 ../../node_modules/.bin/adrianbabel -> /Users/casianorodriguezleon/campus-virtual/2425/learning/compiler-learning/babel-tanhauhau-adrian/packages/babel-cli/bin/babel.js
lrwxr-xr-x@ 1 casianorodriguezleon  staff  135  2 dic 09:20 ../../node_modules/.bin/adrianbabelcompiled -> /Users/casianorodriguezleon/campus-virtual/2425/learning/compiler-learning/babel-tanhauhau-adrian/packages/babel-cli/lib/babel/index.js
lrwxr-xr-x@ 1 casianorodriguezleon  staff  129  2 dic 09:28 ../../node_modules/.bin/adrianbabelsrc -> /Users/casianorodriguezleon/campus-virtual/2425/learning/compiler-learning/babel-tanhauhau-adrian/packages/babel-cli/bin/babel.js
lrwxr-xr-x@ 1 casianorodriguezleon  staff  139  2 dic 08:33 ../../node_modules/.bin/adrianparser -> /Users/casianorodriguezleon/campus-virtual/2122/learning/compiler-learning/babel-tanhauhau-adrian/packages/babel-parser/bin/babel-parser.js

Important

I have noticed that if you re-build the Adrian-tfg branch the links no longer work and you have to recreate them.

Extending Arrays and Objects with an else clause

Given the following code:

➜ babel-learning git:(main) ✗ cat src/array-else/array-else.js

let a = [1, 2, 3, else x => x * x];

console.log(a[2]);  // 3
console.log(a[5]);  // 25 (porque 5 * 5 = 25)

When compiled with Adrian's parser we get an AST like:

➜  array-else git:(main) npx adrianparser array-else.js | jq '[.program.body[0].declarations[0].init.elements[] | .type ]'      
[
  "NumericLiteral",
  "NumericLiteral",
  "NumericLiteral",
  "ElseExpression"
]

To use Adrian's babel transpiler we write a babel.config.json linking to the support plugin (currently 2024-11-07 in the wrong place)

➜ babel-learning git:(main) ✗ cat src/array-else/babel.config.json

{
  "plugins": [ 
    "../../../babel-tanhauhau-adrian/packages/babel-parser/defaultvector.cjs"
  ]
}

Assuming we are in the root of the project and compile calling adrianbabel we get an error (2024/12/02):

➜  babel-learning git:(main) ✗ npx adrianbabel src/array-else/array-else.js                                         
ReferenceError: /Users/casianorodriguezleon/campus-virtual/2324/learning/babel-learning/src/array-else/array-else.js: unknown node of type "ElseExpression" with constructor "Object"

We have to specify the babel.config.json file:

➜  babel-learning git:(main) ✗ ln -s /Users/casianorodriguezleon/campus-virtual/2425/learning/compiler-learning/babel-tanhauhau-adrian/packages/babel-cli/bin/babel.js node_modules/.bin/adrianbabel

and now we can transpile the code:

➜ babel-learning git:(main) ✗ npx adrianbabel src/array-else/array-else.js --config-file $(pwd)/src/array-else/babel.config.json

function isNumeric(n) {
  return !isNaN(n) && isFinite(n);
}

let a = new Proxy([1, 2, 3], {
  [Symbol.isConcatSpreadable]: true,
  length: 3,
  get: function (target, prop) {
    if (typeof prop === "symbol" && prop === Symbol.isConcatSpreadable) return true;
    if (typeof target[prop] === "function") return function (...args) {
      return target[prop].apply(target, args);
    };
    if (typeof target[prop] === "string") return target[prop];
    if (isNumeric(prop) && prop < target.length && prop >= 0) return target[prop];
    return (x => x * x)(prop);
  }
});
console.log(a[2]); // 3

console.log(a[5]); // 25 (porque 5 * 5 = 25)

and we can execute it:

➜  babel-learning git:(main) ✗ npx adrianbabel src/array-else/array-else.js --config-file $(pwd)/src/array-else/babel.config.json | node -
3
25

Links to Adrian's compiler for the else clause on Arrays and Objects

Context:

➜  babel-parser git:(adrian) ✗ date
lunes,  2 de diciembre de 2024, 09:38:22 WET
➜  babel-parser git:(adrian) ✗ pwd -P
/Users/casianorodriguezleon/campus-virtual/2122/learning/compiler-learning/babel-tanhauhau-adrian/packages/babel-parser
➜  babel-parser git:(adrian) ✗ git lg | head -n 1
5226ff943 - (HEAD -> adrian, origin/Adrian-tfg) Now the concat doesn´t give an error when trying to concat a normal vector with a else vector (hace 13 horas Adrián Mora)

Involved files in branch Adrian-tfg:

Semantics of the else clause for Arrays

Warning! the final semantic of the else clause for Arrays is not yet defined. Under discussion.

Assume the following input:

➜  array-else git:(main) ✗ pwd -P
/Users/casianorodriguezleon/campus-virtual/2324/learning/babel-learning/src/array-else
➜  array-else git:(main) ✗ cat array-else-undefined.js 
let a = [1, undefined, 3, else x => x * x];

console.log(a[0]);  // 1
console.log(a[1]);  // undefined
console.log(a[5]);  // 25 (porque 5 * 5 = 25)

When compiled with Adrian's parser we get:

                                                                    
  array-else git:(main)  npx adrianbabel array-undefined-else.js 
babel:
  array-undefined-else.js does not exist
  array-else git:(main)  npx adrianbabel array-else-undefined.js 
function isNumeric(n) {
  return !isNaN(n) && isFinite(n);
}

let a = new Proxy([1, undefined, 3], {
  [Symbol.isConcatSpreadable]: true,
  length: 3,
  get: function (target, prop) {
    if (typeof prop === "symbol" && prop === Symbol.isConcatSpreadable) return true;
    if (typeof target[prop] === "function") return function (...args) {
      return target[prop].apply(target, args);
    };
    if (typeof target[prop] === "string") return target[prop];
    if (isNumeric(prop) && prop < target.length && prop >= 0) return target[prop];
    return (x => x * x)(prop);
  }
});
console.log(a[0]); // 1

console.log(a[1]); // undefined

console.log(a[5]); // 25 (porque 5 * 5 = 25)

Execution:

➜  array-else git:(main) ✗ npx adrianbabel array-else-undefined.js | node
1
undefined
25
➜  array-else git:(main) ✗ 

Warning

An opinion is that a[1] should return 1 instead of undefined because the else clause must be executed for any undefined value. This is not a limitation since we can always have a specify an else function that will return undefined for undefined values inside the array.

Family of functions supporting different semantics

Asume we want to make negative indexation available:

  array-else git:(main)  cat array-else-negative-access.js
let a = [1, 2, 3, else x => { if (x < 0) return a[a.length+x]; else return a[x]; }];

console.log(a[2]);   // 3
console.log(a[-1]);  // undefined but a[3+-1] = a[2] = 3

Execution:

➜  array-else git:(main) ✗ node babel.js array-else-negative-access.js | node
3
undefined

Seems to be convenient to pass as second argument the object/array so that sets of functions like

const neg = (x , a) => { if (x < 0) return a[a.length+x]; else return a[x]; }

can be provided so that several common semantics are easily available (negative-indices, throw-by-default, etc.)

We can imagine a family of functions supporting different semantics like neg above, throw (throw an exception if out of bounds), wrap (wrap around), etc

Concatenation and Symbols

See a.concat(b) section for a discussion on the semantics of a.concat(b).

Issues