|
|
|||||||
Yesterday I've released SteelKix 0.2.2! Get it here: http://steelkix.codeplex.com/
Included is an example of a calculator that can parse and evaluate simple math expressions. How to run this example:
Code: imports $system = system imports $io = system.io global $tokens = { LeftPara=1, RightPara=2, Star=3, Plus=4, Min=5, Number=6, Div=7, Var=8 } ;creates context for scanner function scanner_create($expr) if $system.string.isnullorempty($expr) throw '$expr should not be null or empty' endif $scanner_create = { PeekToken=nothing, Reader = $io.StringReader($expr) } endfunction ;converts object to character function ToChar($c) $ToChar = $System.Convert.ToChar($c) endfunction ;reads token from scanner state function scanner_read($state) dim $c = 0 dim $builder = $System.Text.StringBuilder() dim $length = 0 if $state.PeekToken <> nothing dim $ret = $state.PeekToken $state.PeekToken = nothing $scanner_read = $ret exit 0 endif $c = $state.Reader.Read while $c > 0 dim $char = ToChar($c) select case $System.Char.IsWhitespace($char) $c = $state.Reader.Read continue case $System.Char.IsNumber($char) $length = $builder.Append($char) $c = $state.Reader.Peek while $c > 0 $char = ToChar($c) if $System.Char.IsNumber($char) Or $char = "." $length = $builder.Append($char) $length = $state.Reader.Read $c = $state.Reader.Peek else break endif loop $scanner_read = { Token = $tokens.Number, Value = $builder.ToString } case $System.Char.IsLetter($char) $scanner_read = { Token = $tokens.Var, Value = $char } case $char = "+" $scanner_read = { Token = $tokens.Plus, Value = $char } case $char = "-" $scanner_read = { Token = $tokens.Min, Value = $char } case $char = "*" $scanner_read = { Token = $tokens.Star, Value = $char } case $char = "/" $scanner_read = { Token = $tokens.Div, Value = $char } case $char = "(" $scanner_read = { Token = $tokens.LeftPara, Value = $char } case $char = ")" $scanner_read = { Token = $tokens.RightPara, Value = $char } endselect if $scanner_read = nothing throw 'Unsupported token' endif break loop endfunction ;peeks token from scanner context function scanner_peek($state) if $state.PeekToken = nothing $state.PeekToken = scanner_read($state) endif $scanner_peek = $state.PeekToken endfunction ;parses expression function parser_parseExpression($scanner) $parser_parseExpression = parser_parseMinPlus($scanner) endfunction ;parses final expression (atom/variable/parenthesis) function parser_parseFinal($scanner) dim $token = scanner_peek($scanner) dim $swallow select case $token.token = $tokens.Number Or $token.token = $tokens.Var $parser_parseFinal = { Token = $token, Left=nothing, Right=nothing } $swallow = scanner_read($scanner) case $token.token = $tokens.LeftPara $swallow = scanner_read($scanner) $parser_parseFinal = parser_parseExpression($scanner) $swallow = scanner_read($scanner) endselect endfunction ;parses multiplication and division function parser_parseMulDiv($scanner) dim $left = parser_parseFinal($scanner) dim $swallow dim $token = scanner_peek($scanner) while $token <> nothing and ( $token.token = $tokens.Star or $token.token = $tokens.Div ) $swallow = scanner_read($scanner) dim $expr = { Token = $token, Left = $left, Right = parser_parseFinal($scanner) } $left = $expr $token = scanner_peek($scanner) loop $parser_parseMulDiv = $left endfunction ;parses addition or subtraction function parser_parseMinPlus($scanner) dim $left = parser_parseMulDiv($scanner) dim $swallow dim $token = scanner_peek($scanner) while $token <> nothing and ( $token.token = $tokens.Plus or $token.token = $tokens.Min ) $swallow = scanner_read($scanner) dim $expr = { Token = $token, Left = $left, Right = parser_parseMulDiv($scanner) } $left = $expr $token = scanner_peek($scanner) loop $parser_parseMinPlus = $left endfunction ;prints expression tree function parser_print($expr, $ident) for $i = 1 to $ident " " next $expr.token.value ? if $expr.left <> nothing parser_print($expr.left, $ident+1) endif if $expr.right <> nothing parser_print($expr.right, $ident+1) endif endfunction ;evaluate expression tree function evaluator_eval($expr, optional $variables) dim $left, $right select case $expr.token.token = $tokens.Plus $left = evaluator_eval($expr.left, $variables) $right = evaluator_eval($expr.right, $variables) $evaluator_eval = $left + $right case $expr.token.token = $tokens.Min $left = evaluator_eval($expr.left, $variables) $right = evaluator_eval($expr.right, $variables) $evaluator_eval = $left - $right case $expr.token.token = $tokens.Star $left = evaluator_eval($expr.left, $variables) $right = evaluator_eval($expr.right, $variables) $evaluator_eval = $left * $right case $expr.token.token = $tokens.Div $left = evaluator_eval($expr.left, $variables) $right = evaluator_eval($expr.right, $variables) $evaluator_eval = $left / $right case $expr.token.token = $tokens.number $evaluator_eval = $System.Convert.ToDouble($expr.token.value, $System.Globalization.NumberFormatInfo() With { NumberDecimalSeparator = "." }) case $expr.token.token = $tokens.var if not $variables throw 'Variable referenced but not defined' endif $evaluator_eval = $System.Convert.ToDouble($variables[$expr.token.value]) endselect endfunction ;"1+10 * (1+2)" "Enter an expression. Only multiplication, addition, subtraction and division is supported. Parenthesis can be used." ? "Example: 1+10 * (1+2)" ? "Type exit to quit" ? dim $input gets $input while $input <> "exit" dim $scannerState = scanner_create($input) dim $expr = parser_parseExpression($scannerState) "Result: " evaluator_eval($expr, nothing) ? "Tree: " ? parser_print($expr, 0) gets $input loop |
||||||||
|
|
|||||||
Hi WvS, Great! I've just downloaded and tested the new release of SteelKix. Although I tested just a few items, I did found out that my macros do not work as expected. Example: Macro @DATE Code: Macro DATE Dim $dtn,$rc Imports $dtn = System.DateTime.Now Try $rc = $dtn.ToString("yyy/MM/dd") EndTry Catch $e EndCatch If $e $DATE = $e.ToString() Else $DATE = $rc EndIf EndMacro Result SteelKix 0.2 Code: 2009/10/06 Result SteelKix 0.2.2 Code: No typo here. The result with 0.2.2 is nothing. |
||||||||
|
|
|||||||
Jedi Bo (or is it Lord Force? ), for us less acclimated, would you mind explaining the use for Try and Catch, and specifically $e. This is not anything kix has and it would be great to understand it. Thanks. |
||||||||
|
|
|||||||
heh. this is one of the things kixtart is so awesome and sometimes little too idiot proof. |
||||||||
|
|
|||||||
OK, I confirm there is a bug with macro's. I will fix this tonight, I need to get a few macro's in my test suite.. |
||||||||
|
|
|||||||
Hi Allen, Well it's just BoForce. Nothing special. How to explain Try-EndTry Catch-EndCatch. I'm just starting to understand what this does. I could Try, but for you to Catch it you could read this http://msdn.microsoft.com/en-us/library/system.exception(VS.80).aspx The info helped me to get started. Please note that although this may look like @SERROR in Kix it isn't. In the example below I will use the function DelKey with a none existing key as the Exception is easy to trigger. Kix > Code: DelKey('HKEY_CURRENT_USER\Test\New\some\Folder\Task') ?@SERROR Result: Code: The system cannot find the file specified. SteelKix: Code: ? DelKey('HKEY_CURRENT_USER\Test\New\some\Folder\Task') Result: Code: Cannot delete a subkey tree because the subkey does not exist. SteelKix DelKey Function: Code: ;SteelKix Function: DelKey() ; ;Author: Boforce ; ;Contributors: None ; ;Action: Deletes the specified subkey from the registry ; ;Syntax: DelKey("key") ; ;Version: 1.0 ; ;Date: 2009-09-25 ; ;Date Revised: 2009-09-25 ; ;Parameters Key ; Required. A string that specifies the name of the subkey you want to delete ; ;Remarks: None ; ;Returns: Returns nothing if the key is deleted else an error message ; ;Dependencies: ; SteelKix version: 0.2 ; Tested with Dot Net 3.5.30729.01 ; ;Example(s): Delete some key: ; $DeleteKey = DelKey('HKEY_CURRENT_USER\Test\Some\New\Key') ; ? 'Deleted key : ' + $DeleteKey ; ;Comments : This has been successfully tested on Windows XP SP3 with DOT NET 3.5.30729.01. ; ;Source: Function DelKey($Key) Dim $Win32,$Hive,$Action Imports $Win32 = Microsoft.Win32 $Hive = $Key.ToString.Split("\".ToCharArray())[0] $Key = $Key.Remove($Hive+'\',$Hive.Length+1) Select Case $Hive = 'HKEY_CLASSES_ROOT' Or $Hive = 'HKCR' $Hive = $Win32.Registry.ClassesRoot Case $Hive = 'HKEY_CURRENT_USER' Or $Hive = 'HKCU' $Hive = $Win32.Registry.CurrentUser Case $Hive = 'HKEY_LOCAL_MACHINE' Or $Hive = 'HKLM' $Hive = $Win32.Registry.LocalMachine Case $Hive = 'HKEY_USERS' Or $Hive = 'HKU' $Hive = $Win32.Registry.Users Case 1 $atHive = 'Error' EndSelect If $atHive = 'Error' $AddKey = 'Unknown hive' Else Try $Action = $Hive.DeleteSubKey($Key,1) EndTry Catch $e EndCatch If $e $DelKey = $e.Message Else $DelKey = $Action EndIf EndIf EndFunction Note that in this case I use $e.Message because the $e.ToString() would result in: Code: System.ArgumentException: Cannot delete a subkey tree because the subkey does not exist. at Microsoft.Scripting.Interpreter.ThrowInstruction.Run(InterpretedFrame frame) at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame) |
||||||||
|
|
|||||||
Hi WvS, Looking forward to the next release. You mentioned that you need a few macro's in your test suite. Do you want me to post a few? |
||||||||
|
|
|||||||
I'll just copy/paste them from the other posts, but if you have more, then yes please do! |
||||||||
|
|
|||||||
Ok. Just a few more. Code: ;SteelKix Macro: @TIME ; ;Author: Boforce ; ;Contributors: Richard von Mallesch (It took my meds) ; ;Action: Retrieve current Time ; ;Syntax: @TIME ; ;Version: 1.0 ; ;Date: 2009-09-28 ; ;Date Revised: 2009-09-28 ; ;Parameters None ; ;Remarks: None ; ;Returns: Time (in the format HH:MM:SS) ; ;Dependencies: ; SteelKix version: 0.2 ; Tested with Dot Net 3.5.30729.01 ; ;Example(s): Get Current Time: ; ? @TIME ; Result > 10:23:15 ; ;Comments : This has been successfully tested on Windows XP SP3 with DOT NET 3.5.30729.01. ; ;Source: Macro TIME Dim $dtn,$rc Imports $dtn = System.DateTime.Now Try $rc = $dtn.ToString("HH:mm:ss") EndTry Catch $e EndCatch If $e $TIME = $e.Message Else $TIME = $rc EndIf EndMacro ;SteelKix Macro: @DAY ; ;Author: Boforce ; ;Contributors: Richard von Mallesch (It took my meds) ; ;Action: Retrieve day of the week ; ;Syntax: @DAY ; ;Version: 1.0 ; ;Date: 2009-09-28 ; ;Date Revised: 2009-09-28 ; ;Parameters None ; ;Remarks: None ; ;Returns: Day (Monday, Tuesday, etc.) ; ;Dependencies: ; SteelKix version: 0.2 ; Tested with Dot Net 3.5.30729.01 ; ;Example(s): Get day of the week: ; ? @DAY ; Result > Monday ; ;Comments : This has been successfully tested on Windows XP SP3 with DOT NET 3.5.30729.01. ; ;Source: Macro DAY Dim $dtn,$rc Imports $dtn = System.DateTime.Now Try $rc = $dtn.DayOfWeek EndTry Catch $e EndCatch If $e $DAY = $e.Message Else $DAY = $rc EndIf EndMacro ;SteelKix Macro: @MONTHNO ; ;Author: Boforce ; ;Contributors: Richard von Mallesch (It took my meds) ; ;Action: Retrieve the number of the current month of the year ; ;Syntax: @MONTHNO ; ;Version: 1.0 ; ;Date: 2009-09-28 ; ;Date Revised: 2009-09-28 ; ;Parameters None ; ;Remarks: None ; ;Returns: Month number, beginning with January (1-12) ; ;Dependencies: ; SteelKix version: 0.2 ; Tested with Dot Net 3.5.30729.01 ; ;Example(s): Get month number of the year: ; ? @MONTHNO ; Result > 10 (if month is October) ; ;Comments : This has been successfully tested on Windows XP SP3 with DOT NET 3.5.30729.01. ; ;Source: Macro MONTHNO Dim $dtn,$rc Imports $dtn = System.DateTime.Now Try $rc = $dtn.Month EndTry Catch $e EndCatch If $e $MONTHNO = $e.Message Else $MONTHNO = $rc EndIf EndMacro ;SteelKix Macro: @MONTH ; ;Author: Boforce ; ;Contributors: Richard von Mallesch (It took my meds) ; ;Action: Retrieve the name of the current month of the year ; ;Syntax: @MONTH ; ;Version: 1.0 ; ;Date: 2009-09-28 ; ;Date Revised: 2009-09-28 ; ;Parameters None ; ;Remarks: None ; ;Returns: Name of the Month ; ;Dependencies: ; SteelKix version: 0.2 ; Tested with Dot Net 3.5.30729.01 ; ;Example(s): Get month of the year: ; ? @MONTH ; Result > OCTOBER (if month is October) ; ;Comments : This has been successfully tested on Windows XP SP3 with DOT NET 3.5.30729.01. ; ;Source: Macro MONTH Dim $dtn,$glob,$rc Imports $dtn = System.DateTime.Now Imports $glob = System.Globalization Try $rc = $dtn.Month EndTry Catch $e EndCatch If $e $MONTH = $e.Message Else $MONTH = $glob.DateTimeFormatInfo.CurrentInfo.GetMonthName($rc).ToUpper() EndIf EndMacro |
||||||||
|
|
|||||||
Awesome. Thanks! |
||||||||
|
|
|||||||
Excellent! |
||||||||
|
|
|||||||
proForce, yes, the error coming out of kixtart is wrong. guess it's what windows api reports, versus dotnet gives to "correct" ones. but the power of kixtart is in the fact that it handles the exceptions automatically and internally and thus is n00b friendly. |
||||||||
|
|
|||||||
Yes, indeed the exception message from DotNet is better. But nothing is easier than just throwing an @ERROR or @SERROR after a line of Kix code to check the result. So I have to agree with you on the fact that Kix is very friendly to handle and since I've been playing arround with Steelkix, I started to appreciate Kix even more than I already did. After working with Kix for about ten years now, Steelkix is a new challenge. Learning new stuff is always nice. Uh proForce, Jedi Bo, Lord Force Just BoForce |
||||||||
|
|
|||||||
If error handling in kixtart is really considered to be better than the exception handling in steelkix, I could probably make some sort of setoption to catch all exceptions and set @error and @serror. COM exceptions are already handled in this way to be more backwards compatible with kixtart. |
||||||||
|
|
|||||||
I can't imagine 100% compatibility between the kixes, but would think this would be high on the list. |
||||||||
|
|
|||||||
I've committed a fix for the macro return value bug. I'll release a new binary tomorrow. |
||||||||
|
|
|||||||
wvs, not sure which one is better. kix way sure is lot easier for the scripter, but on the other hand having to catch the errors forces the user to learn some stuff. maybe default catcher on by default? what ya think, boForge? |
||||||||
|
|
|||||||
Thanks for the link above... For others wondering here is the meat of the article. Quote: This class is the base class for all exceptions. When an error occurs, either the system or the currently executing application reports it by throwing an exception containing information about the error. Once thrown, an exception is handled by the application or by the default exception handler. The common language runtime provides an exception handling model that is based on the representation of exceptions as objects, and the separation of program code and exception handling code into try blocks and catch blocks, respectively. There can be one or more catch blocks, each designed to handle a particular type of exception, or one block designed to catch a more specific exception than another block. If an application handles exceptions that occur during the execution of a block of application code, the code must be placed within a try statement. Application code within a try statement is a try block. Application code that handles exceptions thrown by a try block is placed within a catch statement, and is called a catch block. Zero or more catch blocks are associated with a try block, and each catch block includes a type filter that determines the types of exceptions it handles. When an exception occurs in a try block, the system searches the associated catch blocks in the order they appear in application code, until it locates a catch block that handles the exception. A catch block handles an exception of type T if the type filter of the catch block specifies T or any type that T derives from. The system stops searching after it finds the first catch block that handles the exception. For this reason, in application code, a catch block that handles a type must be specified before a catch block that handles its base types, as demonstrated in the example that follows this section. A catch block that handles System.Exception is specified last. If none of the catch blocks associated with the current try block handle the exception, and the current try block is nested within other try blocks in the current call, the catch blocks associated with the next enclosing try block are searched. If no catch block for the exception is found, the system searches previous nesting levels in the current call. If no catch block for the exception is found in the current call, the exception is passed up the call stack, and the previous stack frame is searched for a catch block that handles the exception. The search of the call stack continues until the exception is handled or until no more frames exist on the call stack. If the top of the call stack is reached without finding a catch block that handles the exception, the default exception handler handles it and the application terminates. What isn't so clear is the $e. Is that something that is just predefined in this syntax? What would happen if you dimmed $e and or had a var called $e, prior to doing a try/catch? |
||||||||
|
|
|||||||
$e is the exception that was thrown. Catch defines a new scope and $e is defined in that new scope. Any previous declarations of $e cannot be referenced. |
||||||||
|
|
|||||||
Indeed indeed. It increases the learning curve. Catcher on by default? You have my vote. |
||||||||
|
|
|||||||
A 'default catcher' does have some problems. For example, I cannot set @error with meaningful error codes, since .NET exceptions do not use any error codes. GetLastError is useless in .NET. Furthermore, I do not want it to handle exceptions like StackOverflowException and OutOfMemoryException. So it would be semi-catch all. |
||||||||
|
|
|||||||
I think an effort to keep a standard library of inbuilt functions and macros that mirror as identically as possible those found in KiXtart should continue. That must include proper numerical error codes. If that means catching exception types and exiting such functions with correct error numbers, then that is what we should do. Been able to turn it on an off (like case sensitivity) would be great - with the default being to automatically catch. I would love to see more KiXtart community members upload functions and macros. Even just one (for kix) to both give it a go as well as contribute to SteelKiX language becoming a full superset of KiXtart. It is a tall order order however, but if we make it, then maybe someone like iTripoli may feel like upgrading ASE to support SteelKiX with intellisense, and then I believe and hope this thing will REALLY take off! I lament that my time is pretty limited at the moment and I have an ongoing issue with RSI, but I would like to contribute more to this as I can. I hope others do to. Lastly, I don't see SteelKiX as diminishing KiXtart in anyway. Rather it is a tribute and adduct to KiXtart, this forum, and of course Ruud - to whom we all owe a debt of gratitude. |
||||||||
|
|
|||||||
Here we go, 0.2.2.1: http://steelkix.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=34154 And I really agree with it_took_my_meds, I need more help with this project. Please consider helping me out with building a library for SteelKix that provides functions currently present in kix. Codeplex provides a way to upload patches, it would be really nice if people would write some code in order to help SteelKix forward. |
||||||||
|
|
|||||||
Thanks for the new release. Macro's work again. No problem if you want more help, but I think you need to set up guidelines on how to assist you in a structured way. Something like: - Who is building what - Function requirements - Macro requirements - etc. |
||||||||
|
|
|||||||
Yes I agree, but first I need to know who/how many are willing to help, so I won't set those things up for nothing. |
||||||||
|
|
|||||||
nobody, none and no one, you can count in for sure. then maybe someone or somebody will join too. can't think of anyone else, as he is busy doing something else. something else is quite tired about that, so he might stop soon and start doing it... |
||||||||
|
|
|||||||
Nice! You have talent, any C# experience as well? |
||||||||
|
|
|||||||
C# experience is not that huge. mostly code in vb.net if you asked from me, that is... |
||||||||
|
|
|||||||
The more I am thinking about a new exception/error model being proposed for SteelKix, the more I oppose to it. At this point, it is possible for builtin SteelKix functions to swallow .NET exceptions and use the plain old kix error code model. At runtime, the SteelKix runtime swallows COM exceptions and sets the proper error codes. With these two points in mind, the whole vanilla kix error code model can be implemented for the current kix functionality without having to cripple the new functionality of steelkix that involves the use of .NET (libraries). There are a lot of new things in SteelKix, but most of those things are in place to make interop with .NET a lot easier, including exception handling. The whole point of SteelKix is the use of .NET libraries while keeping most of the simplicity of kix in place. Most of the features are actually additions to kix, not substitutions. The only problem now is that the exception models are not compatible, so the initial choice was a more or less 'hybrid' model, where all of the vanilla kix functionality operates in the 'error code' model and the new .NET features operate in the exception model. |
||||||||
|
|
|||||||
Sounds good to me |
||||||||
|
|
|||||||
hmm... it does have a point. but a setoption for default error handling might be needed. or once steelkix is mature enough, run all .kix in strict kix-compliant way... |
||||||||
|
|
|||||||
I'm no robot, please don't call me 'it' :-). Could you explain why default error handling is necessary? Like I said before, the .NET exception model and the kix error model are not compatible. Also, according to various posts on this board, people are actually asking for a new way of handling errors, see for example http://www.kixtart.org/ubbthreads/showflat.php?Cat=&Board=UBB4&Number=111799. Try/catch is not something I made up, it is used in many languages already, dynamic and static. |
||||||||
|
|
|||||||
sure. again, what I am after is kix-compliant language. hmm... if all internal functions and macros and commands are handled the same way as kixtart does nowadays, then it's fine to have .net differ. |