Open kphillisjr opened 9 years ago
This should be a relatively straight forward addition. The key Features for this is that it works a lot like the current strings do, but with a few clear differences. Most of the lexical information is found in Section "11.8.6 Template Literal Lexical Components".
All Template strings start and end with the Grave accent ( ``` ) character.
var exampleTemplate1 = `My Example Template`;
printf(exampleTemplate1);
This code will output the following...
My Example Template
Template strings can span lines. For example...
var exampleTemplate2 = `Line 1.
Line 2
Line 3
Line 4`;
print(exampleTemplate2);
This code will output the following...
Line 1.
Line 2
Line 3
Line 4
Template strings can span lines. For example...
var exampleTemplate2 = `Line 1.
Line 2
Line 3
Line 4`;
print(exampleTemplate2);
This code will output the following...
Line 1.
Line 2
Line 3
Line 4
Template strings variable access is done using ${
to begin the script code, and then it ends the script code with }
.
For example...
var myLineNumber = [1, 2, "Green" , 4]
var exampleTemplate3 = `Line ${myLineNumber[0]}
Line ${myLineNumber[1]}
Line ${myLineNumber[2]}
Line ${myLineNumber[3]}`;
print(exampleTemplate3);
This code will output the following...
Line 1
Line 2
Line Green
Line 4
I think this would be interesting feature, however after some consideration I'm not sure if this is most important? It is worth noting that there is also Tagged Template Literals, as explained here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
Currently you can get those features into Duktape using transpiler (Babel) and they are part of the workflow of many developers. Furthermore, If you have Template literals, you also expect to have Tagged template literals and to use them efficiently, one might also expect to have support for rest parameters in functions to work.
Having said that, even the basic support for multiline strings without the template syntax could be useful in cases where users Duktape is used as a standalone plugin allowing users to write JS plugins creating for example multiline XML strings, which are processed by libxml. Actually I just did that yesterday, so that's why I'm commenting here :) But I'm still unsure if that's really needed, because in many cases you can just run the code first through transpiler and get also other ES6/ES7 features, which the writers of the plugins might expect to have.
Transpiling code through Babel means using node.js, which kind of defeats the purpose. My current use case is exactly what you mentioned, executing js plugins at runtime - no possibility of a compilation step there.
Supporting multiline strings without interpolation would be very simple. Interpolation is a bit trickier because it creates a more complicated dependency between lexing and compilation (essentially, expressions need to be compiled within a literal). That may take bit more effort to work in.
Hmm, the syntax is defined in parts (TemplateHead, TemplateMiddle, TemplateTail) so maybe if that approach was used it could be worked in a bit easier. The compiler would see a TemplateHead (or NoSubstitutionTemplate), parse 0-N TemplateMiddles, and finish with a TemplateTail to complete the expression.
Semantically, `foo ${bar} quux`
should be exactly equivalent to "foo " + bar + " quux"
. I don't think that would be too difficult to parse?
I'm not sure about this, but to keep future implementation simpler, I think there should be a some kind of "template function" -pointer, which has the default implementation for a function which concatenates all the arguments to a simple string. Something like
function internalTemplateString(list_of_strings, listOfValues) {
var list_or_values = Array.prototype.slice.call(arguments, 1)
var s = ""
for(var i=0; i<list_of_strings.length;i++) {
s += list_of_strings[i]
if( i < list_or_values.length) {
s+= list_or_values[i]
}
}
return s
}
EDIT: Corrected the implementation, it was not that simple function, I created perhaps closer to working example in here. Template tag
can return anything, also an object, so thinking about it as a function call could be close to correct
https://jsfiddle.net/e671rrce/
Then if the lexer it encounters a string literal it could parse it as a function call with N arguments, where the arguments are then passed to either to internalTemplateString
or to the function specified by the tag
before the string template.
The idea would be to avoid duplicate work in future, when considering how to implement also the custom interpolation support with tags.
Semantically,
foo ${bar} quux
should be exactly equivalent to "foo " + bar + " quux". I don't think that would be too difficult to parse?
There's nothing conceptually difficult about it for sure. But the current lexer generates a stream of tokens in a mostly stateless way. The lexer doesn't recurse back into the compiler at present to handle that ${bar}
part, so this would need to be parsed as three tokens:
foo ${
(TemplateHead): generate a string load for "foo "
to Rn} quux
: (TemplateTail): generate a string load for " quux"
to Rn+2, and since the template is done, concat Rn, Rn+1, Rn+2 to target registerThe difficulty -- with current lexer/compiler structure, not conceptually -- is that the lexer needs to remember it's in the middle of parsing a template when continuing with the TemplateTail. It doesn't currently have that state; the state can be added but my point was simply that it's not a simple addition of a token to a list of existing ones.
@kphillisjr Because string coercion and concatenation is a common feature it should probably be supported directly. There's an internal call to do that reasonably efficiently: duk_concat()
.
To be clear, there's nothing conceptually difficult with about any of the ES6 syntax constructs as such, they're pretty standard syntax constructs. The concrete issue for implementing them is that the lexer/compiler in master is designed for ES5.1. While it can be tweaked to parse many ES6 syntax changes, it needs structural changes to become fully ES6 capable.
So some changes are easier than others. For example, computer property names ({ [1+2]: 'three' }
) were very easy to add. These template literals are harder but still probably doable with limited changes.
To be clear, there's nothing conceptually difficult with about any of the ES6 syntax constructs as such, they're pretty standard syntax constructs.
I agree for the most part, with the exception of anything related to destructuring. That feels a bit "exotic" to me. The only case I can think of that's similar, interestingly enough, is Duktape's inspiration, Lua. :)
Destructuring and pattern matching for LHS is quite common in e.g. ML-inspired languages. I have fond memories of using Standard ML of New Jersey for protocol writing for example, because you can pattern match entire messages (on the LHS side) very conveniently.
Maybe my solution was not very good, but here is the problem I have as code:
ctx.test_js_fn = function(listOfRows) {
return `<View background-color="#333333" padding="5px" >${
listOfRows.map((row)=>{
return `<Label color="#ffffff" text="${row[4]}"></Label>`
})
}
</View>`
}
String literals in neat in that way they allow almost React JSX -like syntax for representing XML templates. The C-code is quite capable of parsing or consuming the XML but creating the content dynamically is much harder there. It is very useful for creating user interface templates, images, layers of videos or screens etc.
Hi! It's 2021 now. Hasn't this function been realized yet? It's really easy to use!
Any plan to add this?
It is now 2024. In 2022, I used Webpack to bundle with Babel, which I thought was a perfect solution. Later on, I started developing embedded systems using vscode just like web frontend development. Below is my configuration file for reference:
the package.json
{
"name": "your-app",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"build": "webpack --mode=production",
"dev": "webpack --mode=development"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.16.7",
"@babel/preset-env": "^7.16.8",
"babel-loader": "^8.2.3",
"webpack": "^4.46.0",
"webpack-cli": "^4.9.1"
},
"dependencies": {
}
}
the webpack.config.js
const path = require('path');
var config = {
entry: './app/main.js',
output: {
path: path.resolve(__dirname, 'dist/app'),
filename: '[name].js',
// libraryTarget: 'umd',
globalObject: 'this'
},
module: {
rules: [{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
}]
}
};
module.exports = (env, argv) => {
if (argv.mode === 'development') {
config.devtool = 'source-map';
}
if (argv.mode === 'production') {
// ...
}
return config;
};
the .babelrc
{
"presets": [
["@babel/preset-env", {
"exclude": ["@babel/plugin-transform-new-target"]
}]
],
"plugins": []
}
Using the above files, you can create a project. After running 'npm run build', you can package the JavaScript files that can be used by Duktape. It's worth noting that webpack version 4.x must be used. I've tried using version 5.x, but the packaged JavaScript files still contain ES6 syntax, which Duktape cannot recognize.
This is yet another generalized break down of features new to ES6. ( You can find this, and others at this website: http://es6-features.org/ )
[ ] String Interpolation
[ ] Custom Interpolation
[ ] Raw String Access