How to match keywords with pest? - rust

I'm trying to parse a line like this
MyTupleComponent str, str
with grammar
cname = _{ (ASCII_ALPHANUMERIC | "_")+ }
ints = { "i8" | "i16" | "i32" | "i64" | "i128" | "isize" }
uints = { "u8" | "u16" | "u32" | "u64" | "u128" | "usize" }
strings = { "str" | "String" }
types = { strings | ints | uints }
tuple_component = { cname ~ (types ~ ("," ~ types)?)+ }
But end up with
Err(Error { variant: ParsingError { positives: [types], negatives: [] }, location: Pos(20), line_col: Pos((1, 21)), path: None, line: "MyTupleComponent str, str", continued_line: None })
Anyone know why the rule don't matches correctly?

You can take two roads:
As #ZachThompson pointed out, define WHITESPACE. If you do, make cname atomic, to prevent it from capturing alphanumerics AND spaces.
This test grammar seems fine:
WHITESPACE = _{ " " }
cname = #{ (ASCII_ALPHANUMERIC | "_")+ }
ints = { "i8" | "i16" | "i32" | "i64" | "i128" | "isize" }
uints = { "u8" | "u16" | "u32" | "u64" | "u128" | "usize" }
strings = { "str" | "String" }
types = { strings | ints | uints }
tuple_component = { cname ~ (types ~ ("," ~ types)?)+ }
file = { tuple_component ~ EOI }
Otherwise, you can account for spaces manually. This approach would work too, but it's not scalable with a growth of a grammar.
P.S. Is your intention to parse expressions like MyTupleComponent str, str str, str, without the comma to separate a second tuple from the first? It currently parses fine. You may want to simplify the rule to
tuple_component = { cname ~ types ~ ("," ~ types)* }

Related

Retrieving Parents and Children - Self Referencing Entity gorm

I have a table that have a hierarchy structure with a parent having many children and a children having many parents.
As an example the following struct:
type User struct {
gorm.Model
Name string
SubUsers []*User `gorm:"many2many:user_sub_users;constraint:OnDelete:CASCADE"`
SuperUsers []*User `gorm:"many2many:user_sub_users.......` // no idea what to fill here
}
What's the gorm configuration I need to add to being able to retrieve the super users (parents) for one entity?
So, as an example, imagine that I have the following
Table users
| ID | name |
--------------=
| 1 | Alice |
| 2 | Bob |
| 3 | Joe |
| 4 | Manuel |
---------------
Table users_sub_users
| ID | user_id | sub_user_id |
-------------------------------
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 4 | 1 |
-------------------------------
So if I retrieve the user Alice, I want to get the following:
Alice ->
SubUsers: [Bob, Joe]
SuperUsers: [Manuel]
You can specify the fields in the table user_sub_users with joinForeignKey and joinReferences.
Instead of:
SubUsers []*User `gorm:"many2many:user_sub_users"`
it would be more explicit:
SubUsers []*User `gorm:"many2many:user_sub_users;joinForeignKey:sub_user_id;joinReferences:user_id;"`
For the SuperUsers just swap the fields:
SuperUsers []*User `gorm:"many2many:user_sub_users;joinForeignKey:user_id;joinReferences:sub_user_id;"`
Minimal example:
package main
import (
"fmt"
"github.com/glebarez/sqlite"
"gorm.io/gorm"
)
type User struct {
gorm.Model
Name string
SubUsers []*User `gorm:"many2many:user_sub_users;joinForeignKey:sub_user_id;joinReferences:user_id;"`
SuperUsers []*User `gorm:"many2many:user_sub_users;joinForeignKey:user_id;joinReferences:sub_user_id;"`
}
func main() {
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
if err != nil {
panic(err)
}
db.AutoMigrate(&User{})
alice := &User{Name: "Alice"}
db.Create(alice)
bob := &User{Name: "Bob"}
db.Create(bob)
joe := &User{Name: "Joe"}
db.Create(joe)
manuel := &User{Name: "Manuel"}
db.Create(manuel)
err = db.Model(alice).Association("SubUsers").Append(bob, joe)
if err != nil {
panic(err)
}
err = db.Model(manuel).Association("SubUsers").Append(alice)
if err != nil {
panic(err)
}
var user User
err = db.Preload("SubUsers").Preload("SuperUsers").First(&user, alice.ID).Error
if err != nil {
panic(err)
}
for _, u := range user.SubUsers {
fmt.Println("Subuser", u.Name)
}
for _, u := range user.SuperUsers {
fmt.Println("Superuser", u.Name)
}
}

How to count number of times the primary key of a row is referenced in a specific table with Objection.js

(Using PostgreSQL)
So, I have these (User and Vote) Objection.js models:
const { Model } = require('objection');
class User extends Model {
static get tableName() {
return 'users';
}
static get relationMappings() {
return {
posts: {
relation: Model.HasManyRelation,
modelClass: require('./Post'),
join: {
from: 'users.id',
to: 'posts.userId',
},
},
comments: {
relation: Model.HasManyRelation,
modelClass: require('./Comment'),
join: {
from: 'users.id',
to: 'comments.userId'
}
},
votes: {
relation: Model.HasManyRelation,
modelClass: require('./Vote'),
join: {
from: 'users.id',
to: 'votes.userId'
}
}
};
}
}
module.exports = User;
const { Model } = require('objection');
class Vote extends Model {
static get tableName () { return 'votes' }
static get relationalMappings () {
return {
user: {
relation: Model.BelongsToOneRelation,
modelClass: require('./User'),
join: {
from: 'votes.userId',
to: 'users.id'
}
},
post: {
relation: Model.BelongsToOneRelation,
modelClass: require('./Post'),
join: {
from: 'votes.postId',
to: 'posts.id'
}
}
}
}
}
module.exports = Vote;
The psql command \d users returns:
Table "public.users"
Column | Type | Collation | Nullable | Default
-------------+------------------------+-----------+----------+-------------------
id | uuid | | not null | gen_random_uuid()
username | character varying(128) | | not null |
displayname | character varying(128) | | not null |
email | character varying(256) | | not null |
description | character varying(256) | | |
password | character varying(512) | | not null |
Indexes:
"users_pkey" PRIMARY KEY, btree (id)
"users_id_index" btree (id)
Referenced by:
TABLE "comments" CONSTRAINT "comments_userid_foreign" FOREIGN KEY ("userId") REFERENCES users(id)
TABLE "posts" CONSTRAINT "posts_userid_foreign" FOREIGN KEY ("userId") REFERENCES users(id)
TABLE "votes" CONSTRAINT "votes_userid_foreign" FOREIGN KEY ("userId") REFERENCES users(id)
And the psql command \d votes returns:
Table "public.votes"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+-------------------
id | uuid | | not null | gen_random_uuid()
userId | uuid | | not null |
postId | uuid | | not null |
up | boolean | | not null |
down | boolean | | not null |
Indexes:
"votes_pkey" PRIMARY KEY, btree (id)
"votes_id_index" btree (id)
Foreign-key constraints:
"votes_postid_foreign" FOREIGN KEY ("postId") REFERENCES posts(id)
"votes_userid_foreign" FOREIGN KEY ("userId") REFERENCES users(id)
What i would like to do is use some sort of Model class method (on the class User) to set the properties upvotes (number of Votes with up set to true), downvotes (number of Votes with down set to true) and balance (upvotes - downvotes).

Can I replace values in a table with SpecFlow.Assist?

I currently have a step to verify a payment
Then I have these payments:
| Payment Type | Amount |
| Cash | 1.20 |
I would like to replace the 'Amount' with a variable, such as bill total which would be in the TestContext.
Then I have these payments:
| Payment Type | Amount |
| Cash | <billTotal> |
I've attempted to pre-process the table before creating my set, but I cannot assign to the TableRow value. Is there a standard way to achieve this? Is there a different approach I should be taking?
I ended up using something like this before creating my set:
public void AdjustTable(Table table)
{
foreach (var row in table.Rows)
{
foreach (var key in row.Keys)
{
if (row[key] == "<userFirstName>")
{
row[key] = this.testContext.CustomerProfile.Customer.Name.First;
}
else if (row[key] == "<userLastName>")
{
row[key] = this.testContext.CustomerProfile.Customer.Name.Last;
}
}
}
}
Still open to suggestions!!

Specify order of alternatives in happy parser

I am working on a Happy parser for a language with the following types, and many more.
type :: { ... }
type :
'void' { ... }
| type '*' { ... } {- pointer -}
| type '(' types ')' { ... } {- function -}
| ... {- many more! -}
types :: { ... }
{- empty -} { ... }
| types ',' type { ... }
The language has apparently ambiguous syntax for calls.
callable :: { ... }
callable :
type operand { ... } {- return type -}
| type '(' types ')' '*' operand { ... } {- return and argument types -}
The second rule does not have the same meaning as the first when type takes on the type of a function pointer.
The ambiguity can be removed by adding a special rule for a type that isn't a function pointer. Barring doing so and duplicating all of the type definitions to produce something like
callable :: { ... }
callable :
typeThatIsNotAFunctionPointer operand { ... }
| type '(' types ')' '*' operand { ... }
How can I specify that the alternative type operand is only legal when the type '(' types ')' '*' operand alternative fails?
There are many questions on stack overflow about why a grammar has ambiguities (I found at least 7), and some about how to remove an ambiguity, but none about how to specify how to resolve an ambiguity.
Undesirable Solution
I'm aware that I can refactor the grammar for types to a giant convoluted mess.
neverConstrainedType :: { ... }
neverConstrainedType :
'int' { ... }
| ... {- many more! -}
voidType :: { ... }
voidType :
'void'
pointerType :: { ... }
pointerType :
type '*' { ... } {- pointer -}
functionType :: { ... }
type '(' types ')' { ... } {- function -}
type :: { ... }
type :
neverConstrainedType { ... }
| voidType { ... }
| pointerType { ... }
| functionType { ... }
typeNonVoid :: { ... } {- this already exists -}
typeNonVoid :
neverConstrainedType { ... }
| pointerType { ... }
| functionType { ... }
typeNonPointer :: { ... }
typeNonPointer :
neverConstrainedType { ... }
| voidType { ... }
| functionType { ... }
typeNonFunction :: { ... }
typeNonFunction :
neverConstrainedType { ... }
| voidType { ... }
| functionType { ... }
typeNonFunctionPointer :: { ... }
typeNonFunctionPointer :
typeNonPointer { ... }
| typeNonFunction '*' { ... }
And then define callable as
callable :: { ... }
callable :
typeNonFunctionPointer operand { ... }
| type '(' types ')' '*' operand { ... }
Basically you have what's called a shift/reduce conflict. You can google "resolve shift/reduce conflict" for more info and resources.
The basic idea in resolving shift/reduce conflicts is to refactor the grammar. For instance, this grammar is ambiguous:
%token id comma int
A : B comma int
B : id
| id comma B
The shift/reduce conflict can be eliminated by refactoring it as:
A : B int
B : id comma
| id comma B
In your case you could try something like this:
type : simple {0}
| func {0}
| funcptr {0}
simple : 'void' {0}
| simple '*' {0}
| funcptr '*' {0}
func : type '(' type ')' {0}
funcptr : func '*' {0}
The idea is this:
simple matches any type that is not a function or function pointer
func matches any function type
funcptr matches any function pointer type
That said, many of the things I've attempted to do in grammars I've found are better accomplished by analyzing the parse tree after it's been created.

Syntax error on my Groovy script?

I am using GroovyShell (2.1.7) to dynamically evaluate some Groovy code that I have stored off as a string.
GroovyShell shell = magicallyInstantiateAndBindGroovyShell();
The above method takes care of instantiating the shell, and binding all the required variables to it. Since I believe this is a syntax error, I won't clutter this question with all the variables the shell is being bound with, and what the code I'm trying to evaluate is actually doing. If it turns out that I need to add any more info to the question to help solve my problem, I'll happily oblige!
I then have a string of Groovy code that I am trying to evaluate:
com.me.myorg.myapp.ExpressionUtils.metaClass.filterMetadata = {
com.me.myorg.myapp.model.WidgetVO widget, List<String> properties ->
WidgetVO toReturn = new WidgetVO();
toReturn.setFizz(widget.getFizz());
if(widget.getBuzz().equalsIgnoreCase("BIMDER")) {
toReturn.setMode(widget.getMode());
}
for(String property : properties) {
if("some.prop".equals(property)) {
Preconditions.checkNotNull(widget.getDescriptions());
toReturn.setDescriptions(new ArrayList<DescriptionVO>());
DescriptionVO description = widget.getDescriptions().get(0);
toReturn.getDescriptions().add(description);
} else if("another.prop".equals(property)) {
Preconditions.checkNotNull(widget.getTitles().get(0));
toReturn.setTitles(new ArrayList<TitleVO>());
TitleVO title = widget.getTitles().get(0);
toReturn.getTitles().add(title);
}
}
return toReturn;
};
Which I actually have stored off as a string variable:
String code = "com.me.myorg.myapp.ExpressionUtils.metaClass.filterMetadata = { com.me.myorg.myapp.model.WidgetVO widget, List<String> properties -> WidgetVO toReturn = new WidgetVO(); toReturn.setFizz(widget.getFizz()); if(widget.getBuzz().equalsIgnoreCase(\"BIMDER\")) { toReturn.setMode(widget.getMode()); } for(String property : properties) { if(\"some.prop\".equals(property)) { Preconditions.checkNotNull(widget.getDescriptions()); toReturn.setDescriptions(new ArrayList<DescriptionVO>()); DescriptionVO description = widget.getDescriptions().get(0); toReturn.getDescriptions().add(description); } else if(\"another.prop\".equals(property)) { Preconditions.checkNotNull(widget.getTitles().get(0)); toReturn.setTitles(new ArrayList<TitleVO>()); TitleVO title = widget.getTitles().get(0); toReturn.getTitles().add(title); } } return toReturn; };
When I run:
shell.evaluate(code);
I get the following exception:
startup failed, Script1.groovy: 1: unexpected token: for # line 1, column 294.
1 error
No signature of method: com.me.myorg.myapp.ExpressionUtils.metaClass.filterMetadata() is applicable for argument types: (com.me.myorg.myapp.model.WidgetVO, java.util.ArrayList) values: {com.me.myorg.myapp.model.WidgetVO#9427908c, ["some.prop", "another.prop"]}
Column 294 is the beginning of the for-loop... but to me, this seems like perfectly fine code. Am I forgetting a closing bracket anywhere? Some other syntax error? Where am I going awry? Thanks in advance!
You have:
if(widget.getBuzz().equalsIgnoreCase(\"BIMDER\")) { toReturn.setMode(widget.getMode()); } for(String property : properties)
You need a semicolon before the for...
Why not use a multi-line string?
String code = """com.me.myorg.myapp.ExpressionUtils.metaClass.filterMetadata = { com.me.myorg.myapp.model.WidgetVO widget, List<String> properties ->
| WidgetVO toReturn = new WidgetVO()
| toReturn.setFizz(widget.getFizz())
| if( widget.getBuzz().equalsIgnoreCase( "BIMDER" ) ) {
| toReturn.setMode(widget.getMode())
| }
| for( String property : properties ) {
| if( "some.prop" == property ) {
| Preconditions.checkNotNull( widget.descriptions )
| toReturn.descriptions = [ widget.descriptions[ 0 ] ]
| }
| else if( "another.prop" == property ) {
| Preconditions.checkNotNull( widget.titles[ 0 ] )
| toReturn.titles = [ widget.titles[ 0 ] ]
| }
| }
| toReturn
|}""".stripMargin()

Resources