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 };.
Related
I am trying to parse a string so that I can easily identify terms that are separated by " OR ".
I currently have the following rules and parser class setup:
class Parser < Parslet::Parser
rule(:space) { str(' ').repeat(1) }
rule(:word) { match['^\s"'].repeat(1) }
rule(:or_op) { space >> str('OR') >> space }
rule(:term) { word.as(:term) >> or_op.absent? }
rule(:or_terms) { (word.maybe >> or_op >> word).repeat(1).as(:or_terms) }
rule(:clause) { (or_terms | term).as(:clause) }
rule(:query) { (clause >> space.maybe).repeat.as(:query) }
root(:query)
def self.parse_tree_for(query)
new.parse(query)
end
end
This currently allows me to do:
Parser.parse_tree_for('wow -bob')
=> {:query=>[{:clause=>{:term=>"wow"#0}}]}
Parser.parse_tree_for('wow OR lol')
=> {:query=>[{:clause=>{:or_terms=>"wow OR lol"#0}}]}
Parser.parse_tree_for('wow OR lol OR omg')
=> {:query=>[{:clause=>{:or_terms=>"wow OR lol OR omg"#0}}]}
Which works ok, but ideally I would like for something that would give me those terms individually but with an or flag like: {:query=>[{:clause=>{:term=>"wow",:or=>true}},{:clause=>{:term=>"lol",:or=>true},{:clause=>{:term=>"omg",:or=>true}}]}
Is this something that should be done with a transformer? like, just set a rule in a transformer to do split(' OR ') or is there a better way to setup my rules?
You need an as on each thing you want to explicitly capture.
Your or_term logic is a little flakey. Always put the required stuff first then the optional stuff.
Try this...
require 'parslet'
class Parser < Parslet::Parser
rule(:space) { str(' ').repeat(1) }
rule(:word) { match['^\s"'].repeat(1).as(:word) }
rule(:or_op) { space >> str('OR') >> space }
rule(:term) { word.as(:term) >> or_op.absent? }
rule(:or_terms) { (word >> (or_op >> word).repeat(0)).as(:or_terms) }
rule(:clause) { (term | or_terms).as(:clause) }
rule(:query) { (clause >> space.maybe).repeat.as(:query) }
root(:query)
def self.parse_tree_for(query)
new.parse(query)
end
end
puts Parser.parse_tree_for('wow OR lol OR omg')
# {:query=>[{:clause=>{:or_terms=>[{:word=>"wow"#0}, {:word=>"lol"#7}, {:word=>"omg"#14}]}}]}
puts Parser.parse_tree_for('wow')
# {:query=>[{:clause=>{:term=>{:word=>"wow"#0}}}]}
I added as to word so they always get captured explicitly.
It is better to capture more than you want up front, then later flatten it out with the transformer.
Assuming you are going to extend this to cover AND aswell... you will find that making the AND and OR expressions required will make Operator precedence easier to deal with.
require 'parslet'
class Parser < Parslet::Parser
rule(:space) { str(' ').repeat(1) }
rule(:word) { match['^\s"'].repeat(1) }
rule(:or_op) { space >> str('OR') >> space }
rule(:and_op) { space >> str('AND') >> space }
rule(:term) { word.as(:term) }
rule(:or_terms) { (and_terms >> (or_op >> and_terms).repeat(0)).as(:or_terms) }
rule(:and_terms) { (term >> (and_op >> term).repeat()).as(:and_terms) }
rule(:clause) { (or_terms).as(:clause) }
rule(:query) { (clause >> space.maybe).repeat.as(:query) }
root(:query)
def self.parse_tree_for(query)
new.parse(query)
end
end
pp Parser.parse_tree_for('wow OR lol OR omg')
# {:query=>
# [{:clause=>
# {:or_terms=>
# [{:and_terms=>{:term=>"wow"#0}},
# {:and_terms=>{:term=>"lol"#7}},
# {:and_terms=>{:term=>"omg"#14}}]}}]}
pp Parser.parse_tree_for('wow')
# {:query=>[{:clause=>{:or_terms=>{:and_terms=>{:term=>"wow"#0}}}}]}
pp Parser.parse_tree_for('wow OR lol AND omg OR bob')
# {:query=>
# [{:clause=>
# {:or_terms=>
# [{:and_terms=>{:term=>"wow"#0}},
# {:and_terms=>[{:term=>"lol"#7}, {:term=>"omg"#15}]},
# {:and_terms=>{:term=>"bob"#22}}]}}]}
In answer to your full question... In transformers you have to match an entire hash at a time. To get around this you can match 'subtree' but that is usually a hack.
require 'parslet'
class Parser < Parslet::Parser
rule(:space) { str(' ').repeat(1) }
rule(:word) { match['^\s"'].repeat(1) }
rule(:or_op) { space >> str('OR') >> space }
rule(:and_op) { space >> str('AND') >> space }
rule(:term) { word.as(:term) }
rule(:or_terms) { (term >> (or_op >> term).repeat(0)).as(:or_terms) }
rule(:clause) { (or_terms).as(:clause) }
rule(:query) { (clause >> space.maybe).repeat.as(:query) }
root(:query)
def self.parse_tree_for(query)
new.parse(query)
end
end
class MyTransform < Parslet::Transform
rule(:term => simple(:t)) {t}
rule(:or_terms => sequence(:terms)){
terms.map{|t| {term:{word:t, or:true}}}
}
rule(:or_terms => simple(:cs)){ [{term:{word:cs}}] } # so a single hash looks like a list.
rule(:query => subtree(:cs)){ {:query => cs.map{|c| c[:clause]}.flatten.map{|c| {clause:c}}}}
end
pp MyTransform.new.apply(Parser.parse_tree_for('foo bar OR baz'))
This example outputs:
{:query=>
[{:clause=>{:term=>{:word=>"foo"#0}}},
{:clause=>{:term=>{:word=>"bar"#4, :or=>true}}},
{:clause=>{:term=>{:word=>"baz"#11, :or=>true}}}]}
I'm using the fact that all expressions are or_terms ... and catching the case where there is only a single term to not set or to true.
I'm also using the or_terms match to make a single term act like a collection too... so all clauses map to a list. Then when matching the subtree I can flatten the list to get all the terms and wrap them in 'clause' hashes again... Yuk! ;)
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;
}
I'm trying to create a recursive descent parser. So far I have all the foundation set, I just need to properly implement a few functions to enforce the grammar. I thought everything was right, it looks it, but I guess my Aop, Expr, or Term function is doing something wrong. Sometimes the input stream gets cut off and things aren't recognized. I don't see how though.
Is there any site or source that explains this more in depth, with code examples? Everything I've seen is very generic, which is fine, but I'm stuck on implementation.
NOTE: Edit April 17 2016: My functions were pretty much alright and well structured for the context of my program. The problem I was having, and didn't realize, was that at certain instances when I called getToken I "ate up" characters from the input stream. Sometimes this is fine, other times it wasn't and the input stream needed to be reset. So I simply add a small loop in cases where I needed to put back strings char by char. E.G:
if(t.getType() !=Token::EQOP)
{
//cout<<"You know it" << endl;
int size = t.getLexeme().size();
while(size>0)
{
br->putback(t.getLexeme().at(size-1));
size--;
}
return ex;
}
So with that being said, I pretty much was able to edit my program accordingly and everything worked out once I saw what was eating up the characters.
This is the grammar :
Program::= StmtList
StmtList::= Stmt | StmtList
Stmt::= PRINTKW Aop SC | INTKW VAR SC | STRKW VAR SC | Aop SC
Expr::= Expr PLUSOP Term | Expr MINUSOP Term | Term
Term::= Term STAROP Primary | Primary
Primary::= SCONST | ICONST | VAR | LPAREN Aop RPAREN
Here's the main program with all of the functions: http://pastebin.com/qMB8h8vE
The functions that I seem to be having the most trouble with is AssignmentOperator(Aop), Expression(Expr), and Term. I'll list them here.
ParseTree* Aop(istream *br)
{
ParseTree * element = Expr(br);
if(element!=0)
{
if(element->isVariable())
{
Token t= getToken(br);
if(t==Token::EQOP)
{
cout<<"No" << endl;
ParseTree * rhs = Aop(br);
if(rhs==0)
return 0;
else
{
return new AssignOp(element, rhs);
}
}
else
{
return element;
}
}
}
return 0;
}
ParseTree* Expr(istream *br)
{
ParseTree * element = Term(br);
if(element!=0)
{
Token t=getToken(br);
if(t==Token::MINUSOP || t==Token::PLUSOP)
{
if(t==Token::PLUSOP)
{
ParseTree* rhs = Expr(br);
if(rhs==0)
return 0;
else
{
return new AddOp(element, rhs);
}
}
if(t==Token::MINUSOP)
{
ParseTree* rhs = Expr(br);
if(rhs==0)
return 0;
else
{
return new SubtractOp(element, rhs); //or switch the inputs idk
}
}
}
else
{
return element;
}
}
return 0;
}
ParseTree* Term(istream *br)
{
ParseTree *element = Primary(br);
if(element!=0)
{
Token t=getToken(br);
if(t==Token::STAROP)
{
ParseTree* rhs =Term(br);
if(rhs==0)
return 0;
else
{
return new MultiplyOp(element, rhs);
}
}
else
{
return element;
}
}
return 0;
}
In order to write a recusrive descent parser, you need to convert your grammar into LL form, getting rid of left recursion. For the rules
Term::= Term STAROP Primary | Primary
you'll get something like:
Term ::= Primary Term'
Term' ::= epsilon | STAROP Primary Term'
this then turns into a function something like:
ParseTree *Term(istream *br) {
ParseTree *element = Primary(br);
while (element && peekToken(br) == Token::STAROP) {
Token t = getToken(br);
ParseTree *rhs = Primary(br);
if (!rhs) return 0;
element = new MulOp(element, rhs); }
return element;
}
Note that you're going to need a peekToken function to look ahead at the next token without consuming it. Its also possible to use getToken + ungetToken to do the same thing.
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")
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.