Convert sweet.js argument into string - sweet.js

How would you create a string from an argument to a sweet.js macro? For example:
let foo = macro {
rule {
$name
} => {
console.log('$name', $name);
}
}
var x = 42;
foo x
Will output:
console.log(x, x);
When I'd prefer it to output:
console.log('x', x);
So the first argument has quotes around it.

You can use a case macro:
let foo = macro {
case {_
$name
} => {
letstx $name_str = [makeValue(unwrapSyntax(#{$name}), #{here})];
return #{
console.log($name_str, $name);
}
}
}
var x = 42;
foo x
The basic idea is that you make a new string token (via makeValue) using the string value of the identifiers mached by $name (unwrapSyntax gives us the value of the given syntax objects, in the case of identifiers it is the identifier string). Then letstx allows us to bind our newly created syntax object for use inside the #{} template.

Related

How do I perform a replacement using a formatted string from a regex capture group?

I am doing multiple replacements at once using the regex crate:
extern crate regex;
use regex::{Captures, Regex};
fn transform(string: &str) {
let rgx = Regex::new(r"(\n)|(/\w+)").unwrap();
let res = rgx.replace_all(string, |caps: &Captures| {
if caps.get(1).is_some() {
return " ";
}
match caps.get(2).map(|m: regex::Match| m.as_str()) {
Some(z) => return "nope", // how to return formatted z instead?
None => (),
}
unreachable!();
});
println!("{}", res);
}
fn main() {
transform("no errors");
transform("big\nbad\n/string");
}
Output as expected:
no errors
big bad nope
Instead of "nope", I would like to return z formatted in some way instead. format! doesn't seem like it can be used here due to String / lifetime issues:
match caps.get(2).map(|m: regex::Match| m.as_str()) {
Some(z) => return format!("cmd: {}", z),
None => (),
}
error[E0308]: mismatched types
--> src/main.rs:12:31
|
12 | Some(z) => return format!("cmd: {}", z),
| ^^^^^^^^^^^^^^^^^^^^^ expected &str, found struct `std::string::String`
|
= note: expected type `&str`
found type `std::string::String`
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
What should be done instead?
Note in the error message:
expected &str
It expects a &str because that's the first type returned by your closure:
return " ";
A closure / function can have only one return type, not two.
The simplest fix is to return a String in both cases:
let res = rgx.replace_all(string, |caps: &Captures| {
if caps.get(1).is_some() {
return String::from(" ");
}
let m = caps.get(2).unwrap();
format!("cmd: {}", m.as_str())
});
To be slightly more efficient, you can avoid the String allocation for the space character:
use std::borrow::Cow;
let res = rgx.replace_all(string, |caps: &Captures| {
if caps.get(1).is_some() {
return Cow::from(" ");
}
let m = caps.get(2).unwrap();
Cow::from(format!("cmd: {}", m.as_str()))
});
playground
I've also replaced the match with the => () arm paired with the unreachable! with the shorter unwrap.
See also:
Cannot use `replace_all` from the regex crate: expected (), found String
Using str and String interchangably
Return local String as a slice (&str)

Accessing different properties in a typescript union type

I'm creating a function that handles objects from the database. I have two different data structures where the same property has a different name. I can't change that, so I have to handle it in JavaScript.
The objects have other differences, but that's not important to this function.
I want to use the same function for two different types of objects. Here's sample code demonstrating my problem:
interface TypeA {
itemName: string;
}
interface TypeB {
itemTitle: string;
}
function getItemName(item: TypeA | TypeB): string {
let name = '';
if (item.hasOwnProperty('itemName')) {
name = item.itemName;
} else {
name = item.itemTitle;
}
return name;
}
Of course, this code runs. But the IDE marks both the lines name = item.itemName; and name = item.itemTitle; as errors ("Property does not exist on type"), because both types do not have both properties.
So, what's the proper typescript way to do this?
You need to create a User Defined Type Guard, then you can use an if statement and get the correct typing.
function isTypeA(value: TypeA | TypeB): value is TypeA {
return value.hasOwnProperty('itemName');
}
Then you can get the typing much cleaner:
function getItemName(item: TypeA | TypeB): string {
return isTypeA(item) ? item.itemName : item.itemTitle;
}
Check it out here. Item is correctly cast to either TypeA or TypeB.
I might be a little bit late, but you could give this a try inside your function:
if ('itemName' in item) {
name = item.itemName;
} else {
name = item.itemTitle;
}
you can make a type assertion if you don't do this too often :
if (item.hasOwnProperty('itemName')) {
name = (item as TypeA).itemName;
} else {
name = (item as TypeB).itemTitle;
}
or
if (item.hasOwnProperty('itemName')) {
name = (<TypeA>item).itemName;
} else {
name = (<TypeB>item).itemTitle;
}
if you need to make this check more than once or twice, you'd better writing a type guard as #Daryl suggests.
interface TypeA {
a: string
}
interface TypeB {
b: string
}
const testFunction = (x: TypeA | TypeB): string => {
return (x as TypeA).a || (x as TypeB).b;
}
testFunction({ a: 'Hello' }); // 'Hello'
testFunction({ b: 'World' }); // 'World'
Intellij accepts this syntax:
function getItemName(item: TypeA): string;
function getItemName(item: TypeB): string;
function getItemName(item): string {
return item.hasOwnProperty('itemName') ? item.itemName : item.itemTitle;
}
the official way according to the typescript docs is this:
https://www.typescriptlang.org/docs/handbook/functions.html
I won't complicate things. If you're really sure that your object has either the one or the other property, a name = item['itemName'] || item['itemTitle'] or name = item.hasOwnProperty('itemName') ? item['itemName'] : item['itemTitle'] would be sufficient.
Note that TypeScript usually stops complaining if you access properties using the bracket notation instead of the dot notation. I would suggest adding a comment, though.
Use typeguards:
interface TypeA {
itemName: string;
}
interface TypeB {
itemTitle: string;
}
function isTypeA(val: any): val is TypeA
{
return val.hasOwnProperty('itemName');
}
function isTypeB(val: any): val is TypeB
{
return val.hasOwnProperty('itemTitle');
}
function getItemName(item: TypeA | TypeB): string
{
let name = '';
if (isTypeA(item))
{
name = item.itemName;
}
else
{
name = item.itemTitle;
}
return name;
}

How to get an arrow function's body as a string?

How to get code as string between {} of arrow function ?
var myFn=(arg1,..,argN)=>{
/**
*Want to parse
* ONLY which is between { and }
* of arrow function
*/
};
If it is easy to parse body of simple function : myFn.toString().match(/function[^{]+\{([\s\S]*)\}$/)[1]; is enough . However, Arrow function does not contains function keyword in its definition .
I came looking for a solution because I didn't feel like writing one, but I wasn't sold on the accepted answer. For anyone interested in an ES6 1-liner, I wrote this method, which handles all the cases I needed - both normal functions and arrow functions.
const getFunctionBody = method => method.toString().replace(/^\W*(function[^{]+\{([\s\S]*)\}|[^=]+=>[^{]*\{([\s\S]*)\}|[^=]+=>(.+))/i, '$2$3$4');
This is my attempt:
function getArrowFunctionBody(f) {
const matches = f.toString().match(/^(?:\s*\(?(?:\s*\w*\s*,?\s*)*\)?\s*?=>\s*){?([\s\S]*)}?$/);
if (!matches) {
return null;
}
const firstPass = matches[1];
// Needed because the RegExp doesn't handle the last '}'.
const secondPass =
(firstPass.match(/{/g) || []).length === (firstPass.match(/}/g) || []).length - 1 ?
firstPass.slice(0, firstPass.lastIndexOf('}')) :
firstPass
return secondPass;
}
const K = (x) => (y) => x;
const I = (x) => (x);
const V = (x) => (y) => (z) => z(x)(y);
const f = (a, b) => {
const c = a + b;
return c;
};
const empty = () => { return undefined; };
console.log(getArrowFunctionBody(K));
console.log(getArrowFunctionBody(I));
console.log(getArrowFunctionBody(V));
console.log(getArrowFunctionBody(f));
console.log(getArrowFunctionBody(empty));
It's probably more verbose than it needs to be because I tried to be generous about white space. Also, I'd be glad to hear if anyone knows how to skip the second pass. Finally, I decided not to do any trimming, leaving that to the caller.
Currently only handles simple function parameters. You'll also need a browser that natively supports arrow functions.

sweet.js: transforming occurrences of a repeated token

I want to define a sweet macro that transforms
{ a, b } # o
into
{ o.a, o.b }
My current attempt is
macro (#) {
case infix { { $prop:ident (,) ... } | _ $o } => {
return #{ { $prop: $o.$prop (,) ... } }
}
}
However, this give me
SyntaxError: [patterns] Ellipses level does not match in the template
I suspect I don't really understand how ... works, and may need to somehow loop over the values of $prop and build syntax objects for each and somehow concatenate them, but I'm at a loss as to how to do that.
The problem is the syntax expander thinks you're trying to expand $o.$prop instead of $prop: $o.$prop. Here's the solution:
macro (#) {
rule infix { { $prop:ident (,) ... } | $o:ident } => {
{ $($prop: $o.$prop) (,) ... }
}
}
Notice that I placed the unit of code in a $() block of its own to disambiguate the ellipse expansion.
Example: var x = { a, b } # o; becomes var x = { a: o.a, b: o.b };.

Is it possible for sweet.js macros to define other macros?

I have attempted to define a sweet.js macro that allows other repeating macros to be defined more easily, but I have found a syntax error here:
SyntaxError: [patterns] Ellipses level does not match in the template
11: { $($b...)... }
This is the macro that produced this syntax error:
macro repeatingMacro{
rule{
$a {
$b...
} {
$c...
}
} => {
//the output of this macro should be another macro with repeating patterns
macro $a {
rule{
{ $($b...)... }
} => {
{ $($c...)... }
}
}
}
}
If this macro were correctly defined, then it would allow other macros to be created, like this one:
repeatingMacro cond {
$a... { $b... }
}
{
if($a...){
$b...
}
}
var x = 1;
var y = 2;
cond {
(x > y) {
alert(x);
}
(x < y) {
alert(y)
}
}
This code can be edited online here.
In other words, is it possible to define a macro that will automatically transform this macro:
macro cond {
rule {
$x... { $y... }
} => {
if($x...){
$y...
}
}
}
...into this macro?
macro cond {
rule {
{ $($x... { $y... })... }
} => {
$(if($x...){
$y...
})...
}
}
The immediate problem you are running into is that if you need to emit literal ellipses ... in the template of a macro you need to escape it by doing $[...].
But at a higher level, are you sure you need to go to the effort of macro defining macros here? Sweet.js actually has a nice declarative feature called macroclass (documented here) that makes doing things like this really simple. In fact, cond is the example we use:
// define the cond_clause pattern class
macroclass cond_clause {
pattern {
rule { $check:expr => $body:expr }
}
}
macro cond {
rule { $first:cond_clause $rest:cond_clause ... } => {
// sub-pattern variables in the custom class are
// referenced by concatenation
if ($first$check) {
$first$body
} $(else if ($rest$check) {
$rest$body
}) ...
}
}
cond
x < 3 => console.log("less than 3")
x == 3 => console.log("3")
x > 3 => console.log("greater than 3")