Putting a new function name in an internal macro - rust

trying to write a really basic macro in Rust. I'm trying to turn a multi-line declaration (using nom) into a single line as it's replicated a huge amount. The following is the macro I'm trying to define:
macro_rules! tag_parser {
($name:ident, $tag:expr, $ret:expr) => {
nom::named!(
$name<&str, AnsiSequence>,
nom::do_parse!(
nom::tag!($tag) >>
($ret)
)
);
}
}
And here is an example invocation:
tag_parser!(cursor_restore, "u", AnsiSequence::CursorRestore);
The error I'm getting is the following:
error: no rules expected the token `cursor_restore`
--> src/parsers.rs:95:13
|
95 | tag_parser!(cursor_restore, "u", AnsiSequence::CursorRestore);
| ^^^^^^^^^^^^^^^^ no rules expected this token in macro call
Really, the issue is focused around the first parameter. For some reason it will not let me place it the way I have inside the macro. I'm not sure if this is due to me calling another macro (named!) or something else. Any help would be greatly appreciated, thanks!

I can't tell why the macro expansion fails in the way it does. It does, however, get hung up on the full path to the nom-macro being called in the expansion. If you add use nom::*; to bring do_parse and named into scope beforehand and strip the two nom::-fragments (nom::named!... -> named!...) from the macro-body, it works.

Related

Chaining filters in JMESpath

I want to be able to chain together multiple filters using JMESpath but it appears you cannot filter on the output of a filter.
My working example is as follows:
// document:
{
pips: {
ancestors:[{
id: 'p01234567'
}],
episode: {
more: 'data',
goes: 'here
}
}
}
// working filter: `[pips][?ancestors[?pid=='p01234567'] && episode]`
But I would like to write my filter instead as follows, effectively to filter the output of another filter:
[pips][?ancestors[?pid=='p01234567']][?episode]
Any idea why this doesn't work?
I am building this in NodeJS using the following NPM package: https://www.npmjs.com/package/jmespath
Is there a mistake in the syntax I am using, is there a bug in the library I am using, or am I just trying to do something that is outside what JMESpath allows?
Thank you!
I found the reason why - projections are evaluated in two steps, with the left-hand-side creating a JSON array of initial values and the right-hand-side is the expression.
Solution: "Pipe expressions" which allow you to"operate on the result of a projection".
So instead of the incorrect expression from before: [pips][?ancestors[?pid=='p01234567']][?episode]
This instead should be written as: [pips][?ancestors[?pid=='p01234567']] | [?episode]
And to undo the conversion of the initial document into an array, we can convert this back to an object like this: [pips][?ancestors[?pid=='p01234567']] | [?episode] | [0]
As a side note, I observed that using parentheses () also works, but using pipes are a bit cleaner.

How can I emphasize an impex macro if it is part of a string?

How can I emphasize an impex macro if it is part of a string?
We can do something like this:
$prefix=alpha
$contentCatalog=$prefixContentCatalog
... and $contentCatalog will return "alphaContentCatalog".
Can I make the macro more explicit with something like:
$contentCatalog={$prefix}ContentCatalog
... so that I can immediately see that the macro is $prefix? Is there a syntax for this? (NOTE: The curly brace is just an example. This syntax/symbol doesn't exist for this purpose)
Another example: If I have something like below, it becomes confusing:
$prefix=electronics
$contentCatalog=$prefixContentCatalog
$contentCatalogFolderName=$contentCatalogFolder
But it can be easier to understand if it can be written as:
$prefix=electronics
$contentCatalog={$prefix}ContentCatalog
$contentCatalogFolderName={$contentCatalog}Folder
Hhmmm, unfortunately I don't think there is anything for this. I only see some workarounds like special naming for macro variables:
$_prefix_=electronics
$_contentCatalog_=$_prefix_ContentCatalog
$contentCatalogFolderName=$_contentCatalog_Folder
there is an alternate way to customize the micro via injecting property in local.properties and using ConfigPropertyImportProcessor.
UPDATE GenericItem[processor = de.hybris.platform.commerceservices.impex.impl.ConfigPropertyImportProcessor]; pk[unique = true]
$contentCatalog = $config-ly.br.content.catalog
$contentCV = catalogVersion(CatalogVersion.catalog(Catalog.id[default = $contentCatalog]), CatalogVersion.version[default = Staged])[default = $contentCatalog:Staged]
and entries should be added in local.properties.
ly.br.content.catalog=TestContentCatalog
Note:This is useful when we have multi-country.

PowerShell on CSV file - looking for string depending on string

I need your help regarding PowerShell programming on CSV file.
I've made some searches but cannot find what I'm looking for (or perhaps I don't know the technical terms). Basically, I have an Excel workbook with large amount of data (more or less 38 columns x 350.000 rows), and there are a couple of formulas that take hours to calculate.
I was first wondering if PowerShell could speed up a bit the calculation compared to Excel. The calculations taking most of my time are in fact not that complex (at least at first glance). My data is more or less constructed like this:
Ref Title
----- --------------------------
A/001 "free_text"
A/002 "free_text A/001 free_text"
... ...
A/005 "free_text A/004 free_text"
A/006 "free_text"
B/001 "free_text"
B/002 "free_text"
C/001 "free_text"
C/002 "free_text"
...
C/050 "free_text C/047 free_text"
... ...
C/103 "free_text"
D/001 "free_text"
D/002 "free_text D/001 free_text"
... ....
Basically the data is as follows:
the Ref field contains unique values, in {letter}/{incremental value} format.
In some rows, the Title field may call up one of the Ref data. For example, in line 2, the Title calls for the A/001 Ref. In the last row, the Title calls for the D/001 Ref, etc.
There is no logic pattern defining when this ref could be called up in a title. This is random.
However, what I'm 100% sure of is the following:
The Ref called in the Title is always belonging to the same {letter} block. For example: the string 'C/047' in the Title field can only be found in the block where the Ref {letter} is C.
The Ref called in the Title will always be located 'after' (or in a lower row) than the Ref it refers to. In other words, I cannot have a line with following pattern:
Ref Title
------------ -----------------------------------------
{letter/i} {free_text {letter/j} free_text} with j<i
→ This is not possible.
→ j is always > i
I've used these characteristics in Excel to minimize my lookup arrays. But it still takes an hour to calculate everything.
I've therefore looked into PowerShell, and started to 'play' a bit with the CSV, and looping with the ForEach-Object hoping I would have quicker results. Up to now I basically ended-up looping twice on my CSV file.
$CSV1 = myfile.csv
$CSV2 = myfile.csv
$CSV1 | ForEach-Object {
# find Title
$TitSearch = $_.$Ref
$CSV2 | ForEach-Object {
if ($_.$Title -eq $TitSearch) {
myinstructions
}
}
}
It works but it's really really really long. So I then tried the following instead of using the $CSV2 | ForEach...:
$CSV | where {$_.$Title -eq $TitleSearch} | % $Ref
In either case, it's too long and not efficient at all. Additionally with these 2 solutions, I'm not using above characteristics which could reduce the lookup array and as already stated, it seems I end up looping twice on the CSV file from its beginning up to the end.
Questions:
Is there a leaner way to do this?
Am I wasting my time with PowerShell?
I though about creating 1 file per Ref {letter} block (1 file for block A, 1 for B, etc...). However I have about 50.000 blocks to create. Or create them one by one, carry out the analysis, put the results in a new file, and delete them. Would that be quicker?
Note: this is for work, to be used by other colleagues, and Excel and PowerShell are really the only softwares we may use. I know VBA but ok... At the end I'm curious about how and if this can be solved in a simple manner using PowerShell.
As far as I can see your base algorithm do N^2 iteration (~120 billion). There is a standard way to make it efficient - you need to build a hashtable first. Hashtable is a key/value storage, and look up is pretty much instantaneous, so algorithm's time complexity will become ~N.
Powershell has built-in data type for that. In your case the key would be ref, and the value an array of cell data (assuming your table is smth like: ref, title, col1, ..., colN)
$hash = #{}
foreach($row in $table} {$hash.Add($row.ref, #($row.title, $row.col1, ...)}
#it will take 350K steps to generate it
#then you can iterate over it again
foreach($key in $hash.Keys) {
$key # access current ref
$rowData = $hash.$key # access to current row elements (by index)
$refRowData = $hash[$rowData[$j]] # lookup from other rows, assuming lookup reference is in some column
}
So it's a general idea how to solve the time issue. To be honest I don't believe you need to recreate a wheel and code it yourself. What you need is a relational database. Since you have excel, you should have MS ACCESS too. Just import your data in there, make ref and title an index, then all you need to do is self join. MS Access suck, but I'm sure it will handle 350K row just fine.
Ideally you'd need to get a database on some corporate MSSQL server (open a ticket, talk to your manger, etc). It will calculate all that in seconds, and then you can link the output to a spreadsheet as well.

application.run with variables set within run method function

I am trying to run a macro with variables as follows:
Range("A1").Application.Run (SetConditionalFormatingSub,ConditionB="=""O-BETTER-T-PRV""",ConditionW = "=""O-WORSE-T-PRV""",ConditionM = "=""O-MIXED-T-PRV""")
But I get an error as follows:
Compile error
Expected =
Still a novice in excel coding, can't figure out what seems to be the problem.
Hopefully you guys can help! Thanks in advance.
Try:
Range("A1").Application.Run "SetConditionalFormatingSub", "='O-BETTER-T-PRV'", "='O-WORSE-T-PRV'", "='O-MIXED-T-PRV'"
Notes:
don't use parentheses () when calling a sub;
you can't use names for sub parameters (like ConditionW) with Run method - it only accepts arguments by position (the same order they are defined in your macro);
you can't use quotes inside quotes. Instead, use single quotes, for example " 'Hello' ", instead of " "Hello" "
I don't think you need an Application.Run method for your task. You can set conditional formatting for your range directly using FormatConditions.Add method:
FormatConditions.Add Method

How to check if the first variable passed into a method is a string. Perl

I have no idea how to check for this. My method(if condition in method) should only work (execute) if the first argument passed in is a string. I know how to check other types, but I can't seem to find anything for checking for a string.
For a hash I would do something like;
if(ref eq 'HASH') {...}
If someone could provide a simple example I'm sure I would be able to apply it to what I'm doing. I will put up the code for the method and an explanation for the whole operational details of the method if needed.
Added Information
This is a method for handling different types of errors in the software, here are the 3 possible input formats:
$class->new("error string message")
$class->new("error string message", code => "UNABLE_TO_PING_SWITCH_ERROR")
$class->new("error string message", code => "UNABLE_TO_PING_SWITCH_ERROR", switch_ip => $ip3, timeout => $timeout)
There will always be an error message string first.
With the 1st case there is also a hashref to an error hash structure that is located in a library,
this method new will go into a template processing if the word "code" exists as an arg. where the longer detailed error message is constructed. (I already have the logic for this).
But I have to add logic so that the error message string is added to the hash, so the output is one hash, and not strings.
The second case is very similar to the first, where there are parameters eg. switch_ip , which are inserted into the string using a similar template processing logic, (already have this too).
So I think the first and second cases can be handled in the same way, but I'm not sure, so separated them in this question.
The last case is just can error message string by itself, which at the minute I just insert it into a one key message hash { message => "error string}.
So after all that how should I be checking or dividing up these error cases, At the minute my idea for the ones with code , is to dump the arguments into a hash and just use something like:
if(exists($param{code}) { doTemplateProcess()...}
I need to ensure that there is a string passed in first though. Which was my original question. Does any of my context information help? I hope I didn't go off the topic of my question, if so I'll open this a new question. Thanks.
Error hash - located in Type.pm
use constant ERROR_CODE => {
UNABLE_TO_PING_SWITCH_ERROR => {
category => 'Connection Error:',
template => 'Could not ping switch %s in %s minutes',
tt => {template => 'disabled'},
fatal => 1,
wiki_page => www.error-solution.com/,
},
}
From comments:
These will be called in the software's code like so
ASC::Builder::Error->new(
"Phase x this occured because y was happening:",
code => UNABLE_TO_PING_SWITCH_ERROR,
switch_ip => $ip3,
timeout => 30,
);
Putting the wisdom of your particular problem aside and channeling Jeff Foxworthy:
If you have a scalar and it's not a reference, you might have a string.
If your non-reference scalar doesn't look like a number, it might be a string.
If your non-reference scalar looks like a number, it can still be a string.
If your non-reference scalar has a different string and number value, it might be a dualvar.
You know that your argument list is just that: a list. A list is a collection of scalar values. A scalar can be a reference or not a reference. I think you're looking for the not a reference case:
die "You can't do that" if ref $first_argument;
Past that, you'd have to do fancier things to determine if it's the sort of value that you want. This might also mean that you reject objects that pretend to be strings through overloading and whatnot.
Perhaps you can make the first argument part of the key-value pairs that you pass. You can then access that key to extract the value and delete it before you use the remaining pairs.
You may easily check only whether the error string is a simple scalar value or a reference. You would do that with ref, but you must consider what you want to do if the first parameter isn't a string
You should write your constructor in the ASC::Builder::Error package along these lines
sub new {
my $class = shift;
my ($error, %options) = #_;
die if ref $error;
bless { string => $error }, $class;
}
This example simply dies, and so kills the program, if it is called with anything other than a simple string or number as the first parameter
You may call it as
ASC::Builder::Error->new('error')
or
ASC::Builder::Error->new(42)
and all will be well. If you try
ASC::Builder::Error->new('message', 'code')
then you will see a warning
Odd number of elements in hash assignment
And you may make that warning fatal
If there is anything more then you should explain
Supporting all of the following is simple:
$class->new("s")
$class->new("s", code => "s")
$class->new("s", code => "s", switch_ip => "s", timeout => "s")
All you need is the following:
sub new {
my ($class, $msg, %opts) = #_;
...
}
You can checks such as the following to examine what the called provided:
if (exists($opts{code}))
if (defined($opts{code}))
if ($opts{code})
Despite saying that the string will always be provided, you now ask how to check if was provided. As such, you are probably trying to perform validation rather than polymorphism. You shouldn't waste your time doing this.
Let's look at the hash reference example you gave. ref($arg) eq 'HASH' is wrong. That returns false for some hash references, and it returns false for some things that act like a reference to a hash. The following is a more proper check:
eval { %$arg; 1 }
The equivalent for strings would be the following:
eval { "$arg"; 1 }
Unfortunately, it will always return true! Every value can act as a string. That means the best thing you can do is simply to check if any argument is provided.
use Carp qw( croak );
croak("usage") if !#_;
It's rare for Perl subs to perform argument validation. Not only is it tricky, it's also expensive. It also provides very little benefits. Bad or missing arguments usually results in exceptions or warnings shortly after.
You might see suggestions to use croak("usage") if ref($arg); (or worse, die if ref($arg);), but keep in mind that those will cause the rejection of perfectly fine objects that overload stringification (which is somewhat common), and they will fail to detect the problem with ASC::Builder::Error->new(code => ...) because code produces a string. Again, performing type-based argument validation is an expensive and buggy practice in Perl.

Resources