.NET Forum / Languages / C# / January 2008
Creating a script interpreter using C#
|
|
Thread rating:  |
jkimbler - 11 Jan 2008 18:41 GMT As part of our QA of hardware and firmware for the company I work for, we need to automate some testing of devices and firmware. Since not everybody here knows C#, I'm looking to create a new scripting language that makes writing automated tests simpler. Really, I'm looking to kind of abstract the power of the C# language into a simpler language that's easier to learn. The script files would be interpreted by a script interpreter written in C#. This interpreter would do the background work of connecting to the devices, storing variables, comparing values, hitting the database, etc. and return a log that proves pass or fail of the test.
I know that it's beyond the scope of explaining this in a newgroup.
I'm wondering if anyone can point me to examples, articles, books, etc. on creating a new scripting language/script interpreter in .NET? I've been all over the Internet looking for examples of this and haven't found anything really useful.
Thank you.
-Joe
Nicholas Paldino [.NET/C# MVP] - 11 Jan 2008 19:39 GMT Joe,
You could do this, but honestly, why? There are many languages for .NET, even dynamic/scripting languages (python and javascript can be used by .NET as well as a few others I am sure I am forgetting).
And even if you didn't want to use a .NET language, you could use the Active Scripting engine to use VBScript or Javascript, or create a new language for Active Scripting.
There is a whole bunch of work out there that is done already, and writing something like a script interpreter is not a trivial task. Instead of getting bogged down in the details, I would analyze the dynamic/scripting languages that the runtime currently supports, and use one of them.
 Signature - Nicholas Paldino [.NET/C# MVP] - mvp@spam.guard.caspershouse.com
> As part of our QA of hardware and firmware for the company I work for, > we need to automate some testing of devices and firmware. Since not [quoted text clipped - 17 lines] > > -Joe jkimbler - 11 Jan 2008 20:11 GMT On Jan 11, 1:39 pm, "Nicholas Paldino [.NET/C# MVP]" <m...@spam.guard.caspershouse.com> wrote:
> Joe, > [quoted text clipped - 38 lines] > > - Show quoted text - Nicholas:
At some point, someone was really fed up with all the alternatives out there and created TCl because none of the alternatives fit what they wanted to do. Then, after TCl, someone looked at that and said, "That's not for me either." They went on to create one of the many other languages you speak of.
I don't mean to sound like a jerk...and seriously, thank you for the reply. Unfortunately, this is the pad answer I always see in forums and Usenet and isn't very helpful. The fact that I'm asking the question would denote that I have a reason for wanting or needing to do it...and that I hopefully have weighed the alternatives and found this to be the best avenue to complete the work assigned to me.
Respectfully,
Joe
Chris Shepherd - 11 Jan 2008 20:21 GMT > and Usenet and isn't very helpful. *The fact that I'm asking the > question would denote that I have a reason for wanting or needing to > do it...and that I hopefully have weighed the alternatives* and found > this to be the best avenue to complete the work assigned to me. To be fair, using my arbitrary subjective math, about 95% of the time this is absolutely untrue of posters, and it may be the reason you get the answers you get. The vast majority of the time people would respond to Nicholas' post with surprise that these framework features were present.
Chris.
Nicholas Paldino [.NET/C# MVP] - 11 Jan 2008 20:46 GMT While you state that it means you have a reason (which I am not questioning), you said that it implies that you "hopefully" have weighed the alternatives. I really can't make any assumptions about what you have or have not done.
However, I do know that there is a very large list of languages (static, dynamic, and scripted) that currently target the .NET runtime. Doing a google search on "clr languages" brings me to this (which is a link posted in the first result):
http://blogs.ugidotnet.org/nettools/articles/8060.aspx
And here is another:
http://www.dotnetpowered.com/languages.aspx
I count 56 different languages in the first link alone. That's a whole bunch of languages, with a whole bunch of features, and with all due respect, what you are doing probably is not so special or unique that it wouldn't be facilitated by at least ONE of these languages.
In the event that there isn't any option out there which suits your needs, then you should probably check out Joe Pobar's blog, specifically, this post, which gives a primer on targeting the CLR for a dynamic language of your own:
http://blogs.msdn.com/joelpob/archive/2005/07/01/434728.aspx
You should also check out Jim HuuUnin's blog as well, which has a number of entries on targeting the CLR with dynamic languages:
http://blogs.msdn.com/hugunin/default.aspx
 Signature - Nicholas Paldino [.NET/C# MVP] - mvp@spam.guard.caspershouse.com
On Jan 11, 1:39 pm, "Nicholas Paldino [.NET/C# MVP]" <m...@spam.guard.caspershouse.com> wrote:
> Joe, > [quoted text clipped - 44 lines] > > - Show quoted text - Nicholas:
At some point, someone was really fed up with all the alternatives out there and created TCl because none of the alternatives fit what they wanted to do. Then, after TCl, someone looked at that and said, "That's not for me either." They went on to create one of the many other languages you speak of.
I don't mean to sound like a jerk...and seriously, thank you for the reply. Unfortunately, this is the pad answer I always see in forums and Usenet and isn't very helpful. The fact that I'm asking the question would denote that I have a reason for wanting or needing to do it...and that I hopefully have weighed the alternatives and found this to be the best avenue to complete the work assigned to me.
Respectfully,
Joe
jkimbler - 11 Jan 2008 21:50 GMT Nicholas:
> ...and with all due > respect, what you are doing probably is not so special or unique that it > wouldn't be facilitated by at least ONE of these languages. Fair enough statement. I'd buy that. However, please understand what I'm trying to do. We want to be able to automate testing. Very few of our testers are also coders. As such OOP is probably out of their league without significant training. What I'm trying to do is to take all the code needed to open up an optical connection to our device and boil it down to:
ConnectToDevice;
All the properties, settings, ect to configure the script engine would be in an XML file that they really never have to touch. The tester doesn't need to know that objects are instanced, properties are set, methods are called, and events are raised. They just know that if they put "ConnectToDevice;" in the script file, when the engine gets to that line it will open the device. If they want to write a value to the device:
WriteToDevice MaximumRPM, 14;
The XML file would map "MaximumRPM" to a specific address in the device. If they want to get the current value for the temp out of the device and write it in a test log:
var myOutput; ReadFromDevice CurrentTemp, myOutput WriteToLog "Current Temp: " + myOutput Sleep 5 # wait 5 minutes ReadFromDevice CurrentTemp, myOutput WriteToLog "Current Temp: " + myOutput
That kind of stuff. It's not about creating a MORE POWERFUL language. It's about creating a simpler language as a gateway to a more powerful language. It's about abstracting the C# language so that writing and executing test plans is quicker, and the learning curve for a new employee is very fast.
I also know that this can be done with scores of "switch" statements, if/else, and such. I'm looking for information to make sure that if we go this route, I want to learn from those who have been down this road so I can start with the most stable foundation as I can. That way...6 months to a year down the road I'm not saying, "If I had it to do over again I would have done it this way..."
Again...I didn't mean to insult you. I did look at the link in your original post. I just think those options still require more programming knowledge then the solution I'm looking at creating. We want the testers to focus on coming up with new test senarios, writing plans against them, and executing them as quickly as possible, and allowing regression to be much faster and automated.
I will look closer at the CLR targeted dynamic languages as you recomend. Thanks again for your time.
-Joe
Nicholas Paldino [.NET/C# MVP] - 12 Jan 2008 06:15 GMT For what you described, I think you really should just expose your .NET objects as COM objects which would wrap up all the calls (for example, have a method which will connect to the device) and then just call it through VB script. You could easily get away with this, I think using that approach. VB script isn't really that hard.
The issue here is more about consolidating your code in a manner which reduces the number of operations for the people scripting. That's easy enough to do, and it's not really a scripting issue, right? You just have to package it all up in a way that is consumable through an already existing scripting language.
 Signature - Nicholas Paldino [.NET/C# MVP] - mvp@spam.guard.caspershouse.com
Nicholas:
> ...and with all due > respect, what you are doing probably is not so special or unique that it > wouldn't be facilitated by at least ONE of these languages. Fair enough statement. I'd buy that. However, please understand what I'm trying to do. We want to be able to automate testing. Very few of our testers are also coders. As such OOP is probably out of their league without significant training. What I'm trying to do is to take all the code needed to open up an optical connection to our device and boil it down to:
ConnectToDevice;
All the properties, settings, ect to configure the script engine would be in an XML file that they really never have to touch. The tester doesn't need to know that objects are instanced, properties are set, methods are called, and events are raised. They just know that if they put "ConnectToDevice;" in the script file, when the engine gets to that line it will open the device. If they want to write a value to the device:
WriteToDevice MaximumRPM, 14;
The XML file would map "MaximumRPM" to a specific address in the device. If they want to get the current value for the temp out of the device and write it in a test log:
var myOutput; ReadFromDevice CurrentTemp, myOutput WriteToLog "Current Temp: " + myOutput Sleep 5 # wait 5 minutes ReadFromDevice CurrentTemp, myOutput WriteToLog "Current Temp: " + myOutput
That kind of stuff. It's not about creating a MORE POWERFUL language. It's about creating a simpler language as a gateway to a more powerful language. It's about abstracting the C# language so that writing and executing test plans is quicker, and the learning curve for a new employee is very fast.
I also know that this can be done with scores of "switch" statements, if/else, and such. I'm looking for information to make sure that if we go this route, I want to learn from those who have been down this road so I can start with the most stable foundation as I can. That way...6 months to a year down the road I'm not saying, "If I had it to do over again I would have done it this way..."
Again...I didn't mean to insult you. I did look at the link in your original post. I just think those options still require more programming knowledge then the solution I'm looking at creating. We want the testers to focus on coming up with new test senarios, writing plans against them, and executing them as quickly as possible, and allowing regression to be much faster and automated.
I will look closer at the CLR targeted dynamic languages as you recomend. Thanks again for your time.
-Joe
henon - 12 Jan 2008 21:49 GMT > Nicholas: > [quoted text clipped - 10 lines] > > ConnectToDevice; I'd go for IronPython or IronRuby, the latter beeing not complete yet but would be the better choice if it were complete. You can build your own DSL very easily in those languages, because they do not require pedantic syntax. For instance in Ruby you can skip parentheses and semicolons for a method call like this:
DoSomething param1, param2
I like this simplicity and there are enough examples out there where people used Ruby for DSLs for non-programmers. The most prominent one is a submarine robot control API for submarine biologists.
hth, -- henon www.eqqon.com
Nicholas Paldino [.NET/C# MVP] - 12 Jan 2008 21:52 GMT Indeed, this would simplify things even more. It's just a matter of packaging everything into succinct calls that will simplify the whole process for those doing the coding.
 Signature - Nicholas Paldino [.NET/C# MVP] - mvp@spam.guard.caspershouse.com
>> Nicholas: >> [quoted text clipped - 28 lines] > -- henon > www.eqqon.com Chris Dunaway - 11 Jan 2008 21:34 GMT > As part of our QA of hardware and firmware for the company I work for, > we need to automate some testing of devices and firmware. Since not [quoted text clipped - 17 lines] > > -Joe If you can lay your hands on a copy of the February 2008 issue of MSDN magazine, there is an article in there about creating your own language compiler for .Net.
It was written by Joel Pobar.
Chris
jkimbler - 11 Jan 2008 22:34 GMT > > As part of our QA of hardware and firmware for the company I work for, > > we need to automate some testing of devices and firmware. Since not [quoted text clipped - 27 lines] > > - Show quoted text - Read it yesterday. It's a possibility, but I really don't need a "compiler" as such. It is interesting. Another article in there about test Windows Forms with the UI Automation Library (pg. 115-121) which will be good for testing the GUI side of things.
-Joe
j1mb0jay - 11 Jan 2008 22:42 GMT >>> As part of our QA of hardware and firmware for the company I work for, >>> we need to automate some testing of devices and firmware. Since not [quoted text clipped - 13 lines] >>> Thank you. >>> -Joe The winners of last years Microsoft Imagine Cup wrote a program for young children that does what you want it to do. Maybe you should get in touch with them.
http://www.microsoft.com/uk/academia/imaginecup/2007/default.mspx
Arne Vajhøj - 12 Jan 2008 03:20 GMT > As part of our QA of hardware and firmware for the company I work for, > we need to automate some testing of devices and firmware. Since not [quoted text clipped - 13 lines] > I've been all over the Internet looking for examples of this and > haven't found anything really useful. I think the only easy way of doing that is to use a scanner and parser generator.
It is used for compilers but are even easier to use for interpreters.
Arne
Barry Kelly - 12 Jan 2008 07:46 GMT > As part of our QA of hardware and firmware for the company I work for, > we need to automate some testing of devices and firmware. Since not [quoted text clipped - 6 lines] > variables, comparing values, hitting the database, etc. and return a > log that proves pass or fail of the test. I've read your other replies in this thread that predate my reply. I know you've read about writing a compiler for .NET. Writing a modern scripting language (as opposed to something like classic BASIC with line numbers, or DOS-style batch scripting) is just like writing a compiler for the lexical analysis and parsing stages. There's lots of other references online for those bits, and they're pretty simple once you get the ideas right, so I won't go into them in detail. The difference comes in what you do with the parse tree when you're done.
There's two simple approaches:
1) Create a visitor, acting much like a code generator, which recurses over the tree and executes the syntax tree's nodes using program state.
So if you have source that looks like this (completely hypothetical language but hopefully easily understood):
read a if a == 42 then print "you picked the magic number" fi for i = 1 to a do print "blah" od
... you might then create a tree which looks a bit like this (using Lisp s-exprs to denote trees as lists of lists):
(statement-list (read a) (if (= a 42) // predicate part (statement-list // true part (print "you picked the magic number")) (null-statement) // false part ) (for i 1 a // variable from to (statement-list // body (print "blah")) ) )
If you then have a visitor (perhaps following the visitor pattern, maybe the nodes in the above tree are different classes), you should perform different actions on each node. Imagine a general 'interpret' method which took a node of the above tree as input, and returned the 'return value', i.e. the evaluated value of the tree, as output (also assuming the interpret method has access to world / stack scopes, maybe as a Stack<Dictionary<string,string>> or whatever you like, to map variable names to values):
case node of read: Console.ReadLine() store return value in variable named in node return null (or maybe the value read, depending on language semantics) if: recurse into predicate and save return value if return value is true then recurse into true-part otherwise recurse into false-part statement-list: iterate through each statement and recurse into each in turn null-statement: return null (do nothing) for: check 'to' >= 'from' value, if not then return set loop variable to 'from' value 1. recurse into body 2. increment loop variable 3. check loop variable <= 'to' value; if not then return 4. go to step 1 print: recurse into argument and Console.WriteLine return value =: evaluate (recurse) first and second arguments and return result of comparison <literal>: return value of literal (like '42', etc.) <variable>: return value of variable, looking it up in scope stack // etc. esac // end of 'case'
2. A higher-performance approach, and actually simpler in some cases, is to generate code for a stack machine while recursing through the tree. It's usually simpler when you need multiple return-value semantics; consider how you'd need to interpret a 'break out of loop' command in the 'for' node using the tree evaluation above.
With System.Reflection.Emit, it's pretty trivial to encode most logic. However, writing a simple stack machine which works with more general, 'scripting style' variant variables is very easy.
The code generator looks pretty similar to the evaluation visitor pattern above, except this time you're not evaluating and returning values, but instead writing out stack commands to perform the work. So, the above method from the evaluator visitor might look a bit like this instead for code generation:
case node of read: push appropriate scope variable onto stack push name of variable generate call to Console.ReadLine() call the 'SetValue' method on the scope (following the normal IL technique for calling instance methods) if: recurse into predicate create label IfFalse (e.g. see ILGenerator.DefineLabel in MSDN docs) generate 'jump if false' to IfFalse label recurse into true-part create label AfterIf generate unconditional jump to AfterIf mark label IfFalse (e.g. see ILGenerator.MarkLabel) recurse into false-part mark label AfterIf statement-list: iterate through each statement and recurse into each in turn null-statement: return (do nothing) for: create labels LoopTop, LoopCheck and AfterLoop create a 'loop frame', with references to these labels, and push it onto a 'loop stack' available to the code generator, if you want to support 'continue' and 'break' statements; these guys would be implemented with jumps to the above labels. generate code to set loop variable to initial value generate jump to LoopCheck mark label LoopTop recurse into body mark label LoopCheck generate code to jump to LoopTop if variable still <= 'to' value mark label AfterLoop pop loop frame (if any) print: recurse into argument generate call to Console.WriteLine =: recurse into first recurse into second generate call to your comparison method <literal>: generate code to push literal onto stack <variable>: generate code to load variable value onto stack // etc. esc // end of 'case'
I hope the above sketch gives you some ideas to work with.
-- Barry
 Signature http://barrkel.blogspot.com/
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|