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.
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
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:
- packages/babel-parser/babel.config.json
- packages/babel-parser/defaultvector.cjs
- packages/babel-parser/pruebas.js
- packages/babel-parser/src/parser/expression.js
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.
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
See a.concat(b) section for a discussion on the semantics of a.concat(b)
.
- Issue #22: Adrian-tfg branch: Semantics of array-else #22
- Issue #13: Move defaultvector to babel-plugin-helper-defaultvector as external plugin?
- Discussion #17: a.concat(b)
- Issue #23 a.concat(b): ULL-ESIT-PL/babel-tanhauhau#23