Using PowerShell Parser for console application - powershell

My larger PowerShell/console application requires OOP, etc. for easier development.
I know about parsers such as NDesk.Options and CommandLineParser but I want something to simulate cmdlet parsing as close as possible. E.g. Support parsing for:
MyProject.exe do-thing ("computer1", "computer2") -p "parameter"
MyProject.exe do-thing -cn ("computer1", "computer2") -p "parameter" -verbose
Is it possible to use System.Management.Automation.Language.Parser or a another tool to simulate PowerShell parsing? Are there any examples and pitfalls to watch out for?

Sure, you could use Parser.ParseInput() to parse the args and then extract the individual elements from the resulting CommandAst:
using System.Management.Automation.Language;
public class Program
{
public static void Main(string[] args)
{
Token[] tokens;
ParseError[] errors;
Ast topAst = Parser.ParseInput(String.Join(" ", args), out tokens, out errors);
// Find the CommandAst object
CommandAst parsedCommand = topAst.Find(ast => ast is CommandAst, true);
// Grab the command name
string commandName = ((StringConstantExpressionAst)parsedCommand.CommandElements[0]).Value;
// Grab the remaining command arguments from CommandAst.CommandElements
}
}
I'd would probably store the arguments in a Dictionary<string,string[]>

Related

Two PowerShell methods: SetString() accept a string from the user and PrintString() print the string in upper case

What can I do so that I write the desired phrase in brackets and it will be accepted?
Where exactly to get the "basis" in order to make a return using the "PrintString" method?
# Example 1
$Object.SetString("gamarjoba")
# Example 2
$Object.PrintString()
# Returns
GAMARJOBA
Here is one of my attempts:
class Object {
[string]$gamarjoba
[string]SetString() {
return Write-Host "gamarjoba"
}
[string]PrintString() {
return Write-Host "gamarjoba".ToUpper()
}
}
I understand that this is a very basic question, but I have already spent too much time on it.
You're probably looking for this:
class MyObject {
[string] $gamarjoba = '' # instance variable
# Method has no return type; accepts a string parameter
# and assigns it to instance variable $this.gamarjoba
[void] SetString([string] $gamarjoba) {
$this.gamarjoba = $gamarjoba
}
# Returns the uppercased form of the $this.gamarjoba instance variable.
# Do NOT use Write-Host.
[string] PrintString() {
return $this.gamarjoba.ToUpper()
}
}
$obj = [MyObject]::new() # create an instance
$obj.SetString('aha') # set the string
$obj.PrintString() # print the uppercased string
Note that I've named the class MyObject, because Object would clash with the root class of the .NET type hierarchy.
As for what you tried:
return Write-Host "gamarjoba".ToUpper()
Fundamentally, do NOT use Write-Host to return or output data - it is meant for printing information to the display (console), and bypasses the success output stream and thereby the ability to send output to other commands, capture it in a variable, or redirect it to a file - see this answer for more information.
In the context of class definitions, use only return to return data from a method, passing it an expression or command as-is (e.g, return 'foo'.ToUpper() or return Get-Date -Format yyy)
PowerShell classes participate in the system of output streams only in a very limited way:
returning a value from a method writes it to the success output stream.
Notably, implicit output and use of Write-Output (i.e. what you would use in a script or function) are not supported and quietly ignored. (While you can use return Write-Output ..., there's no good reason to do so.)
throwing an error writes it to the error stream - but you'll only see that if you catch or silence such a fatal-by-default error.
Notably, using Write-Error to write non-terminating errors does not work and is quietly ignored.
However, you can write to all other output streams using their respective Write-* cmdlets, such as Write-Warning.

Powershell parsing of a C# file to get values of constants

I need to parse some C# files to get the values of some constant variables. I know I can do something like
$input = Get-Content C:\somefile.cs ...
then loop over each line and do some text matching.
...but was wondering whether I can utilize some sort of a C# DOM object to get the values of constants?
You can load the type dynamically from the powershell command line, and then evaluate the constant.
Example:
C:\somefile.cs contents:
public static class Foo
{
public const string SOME_CONSTANT = "StackOverflow";
}
Powershell command line:
Add-Type -Path C:\somefile.cs
$constantValue = [Foo]::SOME_CONSTANT

Drools: Get identifier from LHS pattern

I am using Drools 6.3.0 Final. Assuming I have a rule like this
rule "Child of Person over 18"
when
$person : Person(age > 18)
$child : from $person.children
then
end
Let us further assume I build my KieSession with this rule, add some facts and now I want to know the identifiers used in all rules / in all rules that matched my facts.
So what I want to get here is $person and $child.
I know I can get the rules, which fired using an AgendaEventListener and from the event I can get the name of the rule, as well as the objects for $person and $child. But I fail to find a way to get the identifiers $person and $child from my match. Using a debugger I can see the information is there... actually the Rule I get from the event, is a RuleImpl, which has a lhsRoot, in which I can find that information... but this sounds much more complicated than it should be and not the intended way.
So I was wondering if there is not a better way for this.
Your requirement can be easily achieved by using Drools' public API. You were looking at the right place (AgendaEventListener) but Match.getObjects() is not what you need. What you need is a combination of Match.getDeclarationIds() (to get the list of identifiers) and then Match.getDeclarationValue(String id) (to get the value for each identifier). As an example, this is how an AgendaEventListener that logs this information in the console will look like:
import org.kie.api.event.rule.BeforeMatchFiredEvent;
import org.kie.api.event.rule.DefaultAgendaEventListener;
...
ksession.addEventListener(new DefaultAgendaEventListener() {
#Override
public void beforeMatchFired(BeforeMatchFiredEvent event) {
String ruleName = event.getMatch().getRule().getName();
List<String> declarationIds = event.getMatch().getDeclarationIds();
System.out.println("\n\n\tRule: "+ruleName);
for (String declarationId : declarationIds) {
Object declarationValue = event.getMatch().getDeclarationValue(declarationId);
System.out.println("\t\tId: "+declarationId);
System.out.println("\t\tValue: "+declarationValue);
}
System.out.println("\n\n");
}
});
As #laune mentioned,you can also get an instance of the match that activated a rule in the RHS of the rules themselves. In this case, the Match object is accessible through drools.getMatch().
Hope it helps,

Inline::java STUDY configuration

The Inline docs aren't too helpful in learning how to use the STUDY config,
can anyone clarify the syntax involved in calling a simple void method that prints a method, say, Hello() ?
Also, in terms of the external java file, is there a specific directory i need to put it in, or does it go in the same directory of the perl script?
Let's start with the file /home/foo/java_src/Hello.java, which contains:
public class Hello {
public Hello() {}
public void instance_hello() { System.out.println("hello world"); }
public static void static_hello() { System.out.println("HELLO WORLD"); }
}
Tackling your second question first, the first argument after use Inline Java ... can be a filename, and so you can put your source file anywhere and refer to it by its file name in your perl code:
use Inline Java => '/home/foo/java_src/Hello.java';
$obj = Hello->new();
$obj->instance_hello(); # "hello world"
Hello->static_hello(); # "HELLO WORLD"
Note that you don't need STUDY so far. The Hello class is defined in source code that is read directly by the Inline::Java module, so the module automatically creates and populates the Hello namespace in Perl.
STUDY is for classes that aren't parsed directly by Inline::Java. So let's say instead that our Hello class has been compiled into a jar file called /home/foo/jars/hello.jar. Now to use the Hello class you would need to (1) include hello.jar in your CLASSPATH and (2) use STUDY to tell Inline::Java to create the Hello namespace:
use Inline Java => 'STUDY',
CLASSPATH => '/home/foo/jars/hello.jar',
STUDY => ['Hello'];
$obj = Hello->new;
Hello->static_hello; # "HELLO WORLD"
$obj->instance_hello; # "hello world"
We include the first argument STUDY to signal to the Inline::Java that we're not passing any source code directly to the module. We could have also passed valid source code or a valid source code filename.
use Inline Java => 'public class Nothing() { }',
CLASSPATH => '/home/foo/jars/hello.jar',
STUDY => ['Hello'];

Preventing PowerShell from wrapping value types in PSObjects

I have a .NET API that uses a lot of delegates. My API has a couple methods similar to the following:
public static class MyClass
{
public static void DoSomethingWithString(Func<object> myFunc)
{
string myStringValue = myFunc().ToString();
Console.WriteLine(myStringValue);
}
public static void DoSomethingWithDouble(Func<object> myFunc)
{
object unparsedValue = myFunc();
double parsedValue = Convert.ToDouble(unparsedValue);
Console.WriteLine(parsedValue);
}
}
Now in PowerShell I have the following:
[MyClass]::DoSomethingWithString({ "Hello" }); # No error here
[MyClass]::DoSomethingWithDouble({ 123.4 }); # InvalidCastException - can't convert a PSObject to double
The problem is that my PowerShell scriptblock is returning a PSObject instead of the actual double value. My .NET API doesn't know anything about PowerShell, and I don't want to add a reference to PowerShell DLLs just so I can add special handling for this particular scenario.
Is there a way to get my scriptblocks to return actual value types rather than PSObjects? Or is there a PowerShell-agnostic way for my .NET library to handle PSObjects?
PowerShell will wrap things in PSObject as it sees fit, there's no clean way to avoid that when accepting arbitrary script blocks. With some discipline, you can write script blocks that unwrap the PSObject, for example the following might work fine:
[MyClass]::DoSomethingWithDouble({ (123.4).PSObject.BaseObject })
If possible, a better option is to have your api take a delegate with a more specific return type.
public static void DoSomethingWithDouble(Func<double> myFunc)
In this case, PowerShell will convert the return value to the type expected by the delegate. When the return type is PSObject, PowerShell knows PSObject trivially converts to object so it doesn't unwrap, but if the return type is pretty much anything else, PowerShell is forced to do the conversion by unwrapping the PSObject.
For completeness, another option if you're using PowerShell V3 is to use the C# keyword dynamic, something like:
public static void DoSomethingWithDouble(Func<object> myFunc)
{
dynamic unparsedValue = myFunc();
double parsedValue = (double)(unparsedValue);
Console.WriteLine(parsedValue);
}
When unparsedValue is a PSObject, PowerShell will perform the conversion from to double on the second line even though you don't reference any PowerShell assemblies in your C# code.
Note these last two options may not fit well with your real API, but they are options that are worth understanding.