REVO2700$ExpressionParserDemonstrator"HH$o0 /******************************************************************************* * Copyright (c) 2009 Andreas Rozek * * * * Permission is hereby granted, free of charge, to any person obtaining a copy * * of this software and associated documentation files (the "Software"),to deal * * in the Software without restriction, including without limitation the rights * * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * * copies of the Software, and to permit persons to whom the Software is fur- * * nished to do so, subject to the following conditions: * * * * The above copyright notice and this permission notice shall be included in * * all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIA- * * BILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * * THE SOFTWARE. * * * * Additionally, any modifications to the original Software must be clearly * * marked in a way, that the original author will never be considered as the * * author of these modifications! * *******************************************************************************/ global ApplicationPath -------------------------------------------------------------------------------- -- preOpenStack prepares this stack for being opened -- -------------------------------------------------------------------------------- on preOpenStack choose browse tool switch (the platform) case "Win32" revSetStackProfile "Win32" break case "Linux" revSetStackProfile "Linux" break default # MacOS X is the "Master" revSetStackProfile "Master" end switch # **** "install" the actual expression parser **** start using stack "ExpressionParser" end preOpenStack 4ExpressionParser DemonstratorI/Users/andreas/Rozek/Runtime Revolution/ExpressionParser/FileInfo.bundle UArial UArial WArialWArialWArial ULucida Grande UCourier UVerdana U Courier New UTahoma W Courier New ULucida GrandecREVTempMaster-windowManagerPlacefalsemenubarlinkHiliteColor blendLevel0 cantAbortfalse maxWidth65535rect111,135,623,439 patterns 1003 colors255,255,255 decorationsdefaulticonicfalselinkVisitedColorshadowtrue cantDeletetrueid1059altId0 hcAddressingfalsestartUpIconicfalse windowShape0titleExpressionParser Demonstrator linkColor cantModifyfalse textStyleplain maxHeight65535underlineLinksstyletopleveldestroyWindowfalse liveresizingfalse passwordscroll0 behaviorpasskeyicon0nameExpressionParserDemonstrator resizablefalse alwaysBufferfalseformatForPrintingfalsevisibletruemetalfalse minWidth32 destroyStacktrue textSize12 textFontArial dynamicPathsfalse minHeight32 cREVMastercREVDivergedLinuxcREVDivergedWin32cREVDivergedWindowscREVGeometryCachestackID1059 cREVGeneral profileMaster breakpointsbreakpointstatesdebugParameters profileListdebugEntryHandlerM,preOpenStackbreakpointconditionsscripteditorvscroll0scripteditorselection2637stackfileversion2.7 masterNameMaster MainCardIP-------------------------------------------------------------------------------- -- openCard initializes this card -- -------------------------------------------------------------------------------- on openCard put empty into field "ExpressionEntry" set the enabled of button "EvaluationButton" to false put empty into field "ResultView" end openCard 0cREVTempMasterinksrcCopy dontSearchfalsemarkfalsethreeDtrue blendLevel0rect 0,0,512,304defaultButton patterns colors cantDeletetrueid1019altId0 textStyle behaviornameMainCardlayer1 borderWidth2 showBorderfalse textSize textFont cREVTableacellfalse cREVGeneralscripteditorvscroll0scripteditorselection853cREVGeometryCacheIDs 12543966241501010125439926221310291254398971405102112543963912321005125439736655410111254396546676100812543990310931023125440022192210371254399363107103112555402667301059cREVGeometrycachetotal10order $#  HeaderGroupvcREVTempMaster& traversalOnfalseinksrcCopy dontSearchfalse boundingRectscrollbarWidth20threeDfalse blendLevel0rect -4,-4,514,114 patterns colors showNamefalsehScroll0backgroundBehaviortrue cantDeletetrue vScrollbarfalse hScrollbarfalsetabGroupBehaviorfalseid1011altId0radioBehaviorfalse textStylelockLocfalselabel behaviorname HeaderGrouptoolTiplayer1 borderWidth2 cantSelectfalsevisibletruemargins4opaquefalse showBorderfalse disabledfalse textSize textFontvScroll0hilitedButton0 cREVGeneral revUniqueID 1254397366554  TitleLabel@@cREVTempMaster7 tabStops traversalOnfalse firstIndent0inksrcCopy dontSearchfalsescrollbarWidth20threeDfalsetoggleHilitesfalse blendLevel0multipleHilitesfalserect 0,0,320,28 textHeight24 patterns colors shadowfalsehScroll0 textAlignleft vScrollbarfalse hScrollbarfalseid1005altId0autoTabfalsehGridfalse textStylebold dontWraptruelockLocfalse autoHilitefalsestyle transparent sharedTexttrue showLinesfalsenoncontiguousHilitesfalse lockTexttruescroll0 behaviorname TitleLabeltoolTip listBehaviorfalsevGridfalselayer2 borderWidth2 cantSelectfalsevisibletruemargins4 htmlText$

ExpressionParser Demonstrator

opaquefalse threeDHilitefalse shadowOffset4 showBorderfalse disabledfalse textSize18fixedLineHeightfalse textFontArialvScroll0 hilitedLinesshowFocusBorderfalse cREVMasterlayer2rect 0,0,226,28cREVDivergedWin32layer3rect 0,2,226,30 cREVTable currentviewExpressionParser Demonstrator cREVGeneralprofileMaster revUniqueID 1254396391232 masterNameMaster profileListWin32 ExpressionParser Demonstrator TitleSeparator @cREVTempMaster7 tabStops traversalOnfalse firstIndent0inksrcCopy dontSearchfalsescrollbarWidth20threeDtruetoggleHilitesfalse blendLevel0multipleHilitesfalserect 2,26,510,28 textHeight patterns colors shadowfalsehScroll0 textAlignright vScrollbarfalse hScrollbarfalseid1008altId0autoTabfalsehGridfalse textStyle dontWraptruelockLocfalse autoHilitefalsestyle transparent sharedTexttrue showLinesfalsenoncontiguousHilitesfalse lockTexttruescroll0 behaviornameTitleSeparatortoolTip listBehaviorfalsevGridfalselayer3 borderWidth1 cantSelectfalsevisibletruemargins8 htmlText

opaquefalse threeDHilitefalse shadowOffset4 showBordertrue disabledfalse textSizefixedLineHeightfalse textFontvScroll0 hilitedLinesshowFocusBorderfalse cREVTable currentview cREVGeneral revUniqueID 1254396546676 InstructionView@ NcREVTempMaster7 tabStops traversalOnfalse firstIndent0inksrcCopy dontSearchfalsescrollbarWidth20threeDfalsetoggleHilitesfalse blendLevel0multipleHilitesfalserect 2,32,510,110 textHeight patterns colors shadowfalsehScroll0 textAlignleft vScrollbarfalse hScrollbarfalseid1010altId0autoTabfalsehGridfalse textStyle dontWrapfalselockLocfalse autoHilitefalsestyle transparent sharedTexttrue showLinesfalsenoncontiguousHilitesfalse lockTexttruescroll0 behaviornameInstructionViewtoolTip listBehaviorfalsevGridfalselayer4 borderWidth2 cantSelectfalsevisibletruemargins4 htmlTextN

This little application tests the "ExpressionParser", a small "library stack" which parses and evaluates numeric expressions. Please enter such an expression into the upper field and press "Evaluate". The lower field will then show either an error message or the outcome of the evaluation.

opaquefalse threeDHilitefalse shadowOffset4 showBorderfalse disabledfalse textSizefixedLineHeighttrue textFontvScroll0 hilitedLinesshowFocusBorderfalse cREVTable currentviewN

This little application tests the "ExpressionParser", a small "library stack" which parses and evaluates numeric expressions. Please enter such an expression into the upper field and press "Evaluate". The lower field will then show either an error message or the outcome of the evaluation.

 cREVGeneral revUniqueID 1254396624150 *This little application tests the "ExpressionParser", a small "library stack" which parses and evaluates numeric expressions. Please enter such an expression into the upper field and press "Evaluate". The lower field will then show either an error message or the outcome of the evaluation.  Label Field@lXcREVTempMaster7 tabStops traversalOnfalse firstIndent0inksrcCopy dontSearchfalsescrollbarWidth20threeDfalsetoggleHilitesfalse blendLevel0multipleHilitesfalserect 2,108,90,129 textHeight16 patterns colors shadowfalsehScroll0 textAlignleft vScrollbarfalse hScrollbarfalseid1021altId0autoTabfalsehGridfalse textStylebold dontWraptruelockLocfalse autoHilitefalsestyle transparent sharedTexttrue showLinesfalsenoncontiguousHilitesfalse lockTexttruescroll0 behaviorname Label FieldtoolTip listBehaviorfalsevGridfalselayer5 borderWidth2 cantSelectfalsevisibletruemargins4 htmlText

Expression:

opaquefalse threeDHilitefalse shadowOffset4 showBorderfalse disabledfalse textSize12fixedLineHeighttrue textFontArialvScroll0 hilitedLinesshowFocusBorderfalse cREVTable currentview Expression: cREVGeneral revUniqueID 1254398971405 Expression: ExpressionEntry)x}-------------------------------------------------------------------------------- -- various handlers checking if "FileSpecEntry" is empty -- -------------------------------------------------------------------------------- command checkContents set the enabled of button "EvaluationButton" to ( \ the text of me is not empty \ ) end checkContents on CutKey send "checkContents" to me in 0 seconds end CutKey on EnterInField checkContents if (the enabled of button "EvaluationButton" is true) then send "MouseUp 1" to button "EvaluationButton" end if end EnterInField on PasteKey send "checkContents" to me in 0 seconds end PasteKey on RawKeyUp checkContents end RawKeyUp on ReturnInField checkContents if (the enabled of button "EvaluationButton" is true) then send "MouseUp 1" to button "EvaluationButton" end if end ReturnInField PhHenter a numeric expressioncREVTempMaster7 tabStops traversalOntrue firstIndent0inksrcCopy dontSearchfalsescrollbarWidth20threeDtruetoggleHilitesfalse blendLevel0multipleHilitesfalserect80,104,504,176 textHeight16 patterns colors 0,0,0 shadowfalsehScroll0 textAlignleft vScrollbarfalse hScrollbarfalseid1023altId0autoTabfalsehGridfalse textStyleplain dontWrapfalselockLocfalse autoHilitetruestyle rectangle sharedTextfalse showLinesfalsenoncontiguousHilitesfalse lockTextfalsescroll0 behaviornameExpressionEntrytoolTipenter a numeric expression listBehaviorfalsevGridfalselayer6 borderWidth2 cantSelectfalsevisibletruemargins8 htmlText

1+(2+3*(4-3))+4

opaquetrue threeDHilitefalse shadowOffset4 showBordertrue disabledfalse textSize12fixedLineHeighttrue textFontArialvScroll0 hilitedLinesshowFocusBordertrue cREVTable currentview(n/a) cREVGeneralscripteditorvscroll0 revUniqueID 1254399031093scripteditorselection406  1+(2+3*(4-3))+4 EvaluationButtonewon mouseUp try put ValueOf(the text of field "ExpressionEntry") into field "ResultView" set the TextColor of field "ResultView" to "black" catch Signal put Signal into field "ResultView" set the TextColor of field "ResultView" to "red" end try end mouseUp R3press this button to evaluate the given expressionS EvaluatecREVTempMaster> traversalOntrueinksrcCopythreeDtrue blendLevel0 hiliteBordertrue mnemonic0rect215,188,297,211armFillfalse patterns visitedIcon0colors 0,0,0 showIconfalse showNametruehilitedfalseshadowfalse sharedHilitetrue textAligncenterarmedfalsemenuMouseButton1id1029altId0 labelWidth0 accelKey armedIcon0 textStylelockLocfalse autoHilitetruelabelEvaluate showHilitefalsevisitedfalse disabledIcon0style rectangle armBordertrue behavioricon0nameEvaluationButtontoolTip2press this button to evaluate the given expression accelTextlayer7 borderWidth2 cantSelectfalsevisibletrue menuLines5margins4 accelModsopaquetruedefaultfalse shadowOffset4text hiliteFilltrue menuName showBordertrue disabledfalse hiliteIcon0 textSizeautoArmfalsefamily0 menuMode textFont hoverIcon0 menuHistory1showFocusBordertrue cREVMastercolors 0,0,0 layer8cREVDivergedWin32colors0,0,0 128,128,128 layer5 cREVGeneralprofileMasterscripteditorvscroll0 revUniqueID 1254399262213scripteditorselection266 masterNameMaster profileListWin32  Label Field dcREVTempMaster7 tabStops traversalOnfalse firstIndent0inksrcCopy dontSearchfalsescrollbarWidth20threeDtruetoggleHilitesfalse blendLevel0multipleHilitesfalserect 2,226,102,247 textHeight16 patterns colors shadowfalsehScroll0 textAlignleft vScrollbarfalse hScrollbarfalseid1031altId0autoTabfalsehGridfalse textStylebold dontWraptruelockLocfalse autoHilitefalsestyle transparent sharedTexttrue showLinesfalsenoncontiguousHilitesfalse lockTexttruescroll0 behaviorname Label FieldtoolTip listBehaviorfalsevGridfalselayer8 borderWidth2 cantSelectfalsevisibletruemargins4 htmlText

Result:

opaquefalse threeDHilitefalse shadowOffset4 showBorderfalse disabledfalse textSize12fixedLineHeighttrue textFontArialvScroll0 hilitedLinesshowFocusBordertrue cREVTable currentviewResult: cREVGeneral revUniqueID 1254399363107 Result: # ResultView)x-------------------------------------------------------------------------------- -- various handlers checking if "FileSpecEntry" is empty -- -------------------------------------------------------------------------------- command checkContents set the enabled of button "EvaluationButton" to ( \ the text of me is not empty \ ) end checkContents on CutKey send "checkContents" to me in 0 seconds end CutKey on EnterInField checkContents if (the enabled of button "EvaluationButton" is true) then send "MouseDown 1" to button "EvaluationButton" end if end EnterInField on PasteKey send "checkContents" to me in 0 seconds end PasteKey on RawKeyUp checkContents end RawKeyUp on ReturnInField checkContents if (the enabled of button "EvaluationButton" is true) then send "MouseDown 1" to button "EvaluationButton" end if end ReturnInField blackPHPwill display either the outcome of an expression evaluation or an error messagecREVTempMaster7 tabStops traversalOntrue firstIndent0inksrcCopy dontSearchfalsescrollbarWidth20threeDtruetoggleHilitesfalse blendLevel0multipleHilitesfalserect80,222,504,294 textHeight16 patterns colors black shadowfalsehScroll0 textAlignleft vScrollbarfalse hScrollbarfalseid1059altId0autoTabfalsehGridfalse textStyleplain dontWrapfalselockLocfalse autoHilitetruestyle rectangle sharedTextfalse showLinesfalsenoncontiguousHilitesfalse lockTexttruescroll0 behaviorname ResultViewtoolTipOwill display either the outcome of an expression evaluation or an error message listBehaviorfalsevGridfalselayer10 borderWidth2 cantSelectfalsevisibletruemargins8 htmlText

10

opaquetrue threeDHilitefalse shadowOffset4 showBordertrue disabledfalse textSize12fixedLineHeighttrue textFontArialvScroll0 hilitedLinesshowFocusBordertrue cREVTable currentview(n/a) cREVGeneral revUniqueID 1255540266730scripteditorvscroll0scripteditorselection406  10  WhiteOverlay.pngpPPNG  IHDRpPL}sRGBbKGDC pHYs  tIME 4=<IDATx 01+ $= JOg@((P @@(P @@(PP @@(PP @@((P f\IENDB`cREVTempMaster!palindromeFramesfalse traversalOnfalseinksrcCopythreeDtrue blendLevel0rect200,140,312,220angle0 fileNamecolorsyHot1id1037altId0 frameCount0 constantMaskfalselockLocfalse repeatCount0size272 behaviorhotSpot1,1nameWhiteOverlay.png alwaysBuffertruetoolTip dontDitherfalselayer9 borderWidth2 cantSelectfalsevisiblefalseopaquefalse currentFrame1 showBorderfalse disabledfalsexHot1showFocusBordertrue cREVGeneral revUniqueID 1254400221922$BlackTexture.jpg@ppP ?JFIFHHC     C  Pp 7!1AQ"aq2#BR%3Drs ?#w`1^JFfk_Қ.PH׏ ;Lr &zXȡ%*Nd}-|UG"OO9OW}RYBz3N2y: m; DmՕ + qA}qۑxq΂B>8$$ ;u}*d4ܛ|=~nu]jj$v2=9~ {fz4m@X?B{:zV )"d؝be~H >?vyive`w$y>'A࠶_D5LO@{6aV5I O|ao"-GU,;@S ,JI?h4SOZX)0֯|&OpǤg,G*%gxTQy)   0mU[u%bF_$r~{|{d4's^ff?mXfU7ԃ(H؃SicH g'= ϼZHc,qHsJ]iIA xAkZPZ8) %pO՚Z}]% "(#OPO-ٞ9(nNp͌KxRGt1$HBvS39='eݽ7iZ4 NzI/<΃&ZZ0Yf,#>ܶh1EfIguz> Ih,XKr$(ӌt֕DŽJeFpASJ,uji8Cfo,x dۣ'7=Ɖ&OEAllAH(%):< 8}:beKD|h zUi#_Qg@z I<ҨYff C18imrdU)EcP@:]ݶ: G r%$LMqsѵZ_R!6#6%zG>0t$l)z~&ZL$ga:X8 ڙ vmw9etI"SUC.9=ҍEG %~ƂʹWڞI\ D|( qRjPWDWFq##y>GA-p7[5gRr*ܶy?R{h0+;8yUQ}}{lm#A^x|<: M'#o@*V+`B{$`c@6*mHLQ;<aR8tT{,qf-(3Ñϸ@5\qV[h `,\R2u8:t!4=%1#@^աZߪB FH/mE$}PJ6@>A}ƕ-jA:8s3y?)<@A!/l[zVUo98@љv"?%H(^rshFȰ2gPvcRV7lD1}hb:pXg Geg: %M)PEa±H RZ]&2M$1f1AZ&1_T* q  ScZX(Ե;E  UHםl4AAS"Ž0q469ܥ؉9+4z Ϗ Ry,=!՟ӵJOJwh*H>@~ oH>'= ϼ^'uÜ#EOSn3yzmQ #g H{ylչ2H gAh-CFVLtCu5H'%n0t nՍ2P#@լS訲49i1$ D;4{ {~)-aFzk:w}& lyrlFI3 d: n$TXg9$GWv4UĪg$giz׊wrP̫bI# jlpiG'^{=((1$g63֣,H_}+-mfpEehTFq': EKQz -H=6$E` {VI/3-~ci 0\T}/[,f> YۯEI#V%Ќ44fU @lo-5)RFˌ4 BnН*6h,c29; T9V{<)2ElʀdtƂPׅ$Ow$} )a? B&eYF3H]mj-TI=A89NͳܹfV^qhYlӤ"rL 0~s}Ҭ杢XbP"984SOY*ZG\q{qsM rhfʨ8o'<cWvM.< <n1nc1~y#THH ߮.R8hNA e,ȧ8[ k=K",Ř gg΃-=ZO "!v/(2G@5\5*I) thSH "eof") then throw "unexpected characters after expression at position " & \ curToken["Position"] end if return ValueOfNode(firstNode) end ValueOf /******************************************************************************* * * * Scanner * * * *******************************************************************************/ -------------------------------------------------------------------------------- -- atEOF has the end of the source code been reached? -- -------------------------------------------------------------------------------- private function atEOF return (SourcePosition > SourceLength) end atEOF -------------------------------------------------------------------------------- -- proceed proceeds to the next character in the source code -- -------------------------------------------------------------------------------- private command proceed if (atEOF()) then exit proceed add 1 to SourcePosition if (atEOF()) then put "" into curChar else put char SourcePosition of SourceCode into curChar end if end proceed -------------------------------------------------------------------------------- -- skipWhitespace skips all whitespace char.s from the current position on -- -------------------------------------------------------------------------------- private command skipWhitespace repeat while ( \ (curChar = " ") or (curChar = return) or (curChar = tab) \ ) # handles EOF properly proceed end repeat end skipWhitespace /******************************************************************************* * * * Tokenizer * * * *******************************************************************************/ -------------------------------------------------------------------------------- -- deferToken defers a given token for later delivery -- -------------------------------------------------------------------------------- private command deferToken newToken add 1 to pendingTokens put newToken into pendingToken[pendingTokens] end deferToken -------------------------------------------------------------------------------- -- newIdentifierToken creates (and delivers) a new identifier token -- -------------------------------------------------------------------------------- private function newIdentifierToken TokenPosition repeat until not matchText(curChar,"[a-zA-Z0-9]") proceed end repeat return newToken( \ "identifier", TokenPosition, \ char TokenPosition to SourcePosition-1 of SourceCode \ ) end newIdentifierToken -------------------------------------------------------------------------------- -- newNumberToken creates (and delivers) a new number token -- -------------------------------------------------------------------------------- private function newNumberToken TokenPosition repeat until (not matchText(curChar,"[0-9]")) # skip integral part proceed end repeat if (curChar = ".") then # skip fractional part proceed if (not matchText(curChar,"[0-9]")) then throw "invalid number literal at position " & SourcePosition end if repeat until (not matchText(curChar,"[0-9]")) proceed end repeat end if if ((curChar = "e") or (curChar = "E")) then # skip exponential part proceed if ((curChar = "+") or (curChar = "-")) then proceed end if if (not matchText(curChar,"[0-9]")) then throw "invalid number literal at position " & SourcePosition end if repeat until (not matchText(curChar,"[0-9]")) proceed end repeat end if return newToken( \ "number", TokenPosition, \ char TokenPosition to SourcePosition-1 of SourceCode \ ) end newNumberToken -------------------------------------------------------------------------------- -- newToken creates (and delivers) a new token "structure" -- -------------------------------------------------------------------------------- private function newToken TokenType, TokenPosition, TokenValue local freshToken put TokenType into freshToken["Type"] put TokenPosition into freshToken["Position"] put TokenValue into freshToken["Value"] return freshToken end newToken -------------------------------------------------------------------------------- -- nextToken yields the next pending token from the source code -- -------------------------------------------------------------------------------- private function nextToken TokenOffset if (pendingTokens > 0) then local firstToken; put pendingToken[1] into firstToken repeat with i = 2 to pendingTokens put pendingToken[i] into pendingToken[i-1] end repeat put empty into pendingToken[pendingTokens] subtract 1 from pendingTokens return firstToken end if # **** no pending tokens, create a new one **** skipWhitespace local auxChar; put curChar into auxChar switch case atEOF() return newToken("eof", SourcePosition, "") case (curChar is an integer) return newNumberToken(SourcePosition) case (curChar = "(") case (curChar = ")") case (curChar = ",") proceed return newToken(auxChar, SourcePosition-1, auxChar) case (curChar is among the items of "+,-,*,/,%,\") proceed return newToken("operator", SourcePosition-1, auxChar) case matchText(curChar,"[a-zA-Z]") return newIdentifierToken(SourcePosition) default throw "unrecognized character at position " & SourcePosition end switch end nextToken /******************************************************************************* * * * Parser * * * *******************************************************************************/ -------------------------------------------------------------------------------- -- newConstantNode creates a ParseNode for constants -- -------------------------------------------------------------------------------- private function newConstantNode Position, ConstantName local ParseNode put "constant" into ParseNode["Type"] put Position into ParseNode["Position"] put ConstantName into ParseNode["Name"] return ParseNode end newConstantNode -------------------------------------------------------------------------------- -- newFunctionNode creates a ParseNode for function invocations -- -------------------------------------------------------------------------------- private function newFunctionNode Position, FunctionName, ArgumentList local ParseNode put "function" into ParseNode["Type"] put Position into ParseNode["Position"] put FunctionName into ParseNode["Name"] put ArgumentList into ParseNode["ArgumentList"] return ParseNode end newFunctionNode -------------------------------------------------------------------------------- -- newGroupNode creates a ParseNode for grouped expressions -- -------------------------------------------------------------------------------- private function newGroupNode Position, Expression local ParseNode put "group" into ParseNode["Type"] put Position into ParseNode["Position"] put Expression into ParseNode["Expression"] return ParseNode end newGroupNode -------------------------------------------------------------------------------- -- newInfixNode creates a ParseNode for infix operator expressions -- -------------------------------------------------------------------------------- private function newInfixNode Position, Operator, leftOperand, rightOperand local ParseNode put "infix" into ParseNode["Type"] put Position into ParseNode["Position"] put Operator into ParseNode["Operator"] put leftOperand into ParseNode["leftOperand"] put rightOperand into ParseNode["rightOperand"] return ParseNode end newInfixNode -------------------------------------------------------------------------------- -- newLiteralNode creates a ParseNode for literal values -- -------------------------------------------------------------------------------- private function newLiteralNode Position, LiteralValue local ParseNode put "literal" into ParseNode["Type"] put Position into ParseNode["Position"] put LiteralValue into ParseNode["Value"] return ParseNode end newLiteralNode -------------------------------------------------------------------------------- -- newPrefixNode creates a ParseNode for prefix operator expressions -- -------------------------------------------------------------------------------- private function newPrefixNode Position, Operator, Operand local ParseNode put "prefix" into ParseNode["Type"] put Position into ParseNode["Position"] put Operator into ParseNode["Operator"] put Operand into ParseNode["Operand"] return ParseNode end newPrefixNode -------------------------------------------------------------------------------- -- OperatorPriority determines the priority of a given operator -- -------------------------------------------------------------------------------- private function OperatorPriority Operator switch Operator case "+"; case "-" return 1 case "*"; case "/"; case "%"; case "\" return 2 default return 0 end switch end OperatorPriority -------------------------------------------------------------------------------- -- parsedExpression parses (and yields) a given (sub-)expression -- -------------------------------------------------------------------------------- private function parsedExpression leftOperand, Operator, OperatorPosition local minPriority, curPriority if (leftOperand is not an array) then put parsedOperand() into leftOperand local curToken; put nextToken() into curToken if (curToken["Type"] <> "operator") then deferToken(curToken) return leftOperand end if put curToken["Value"] into Operator put curToken["Position"] into OperatorPosition put 0 into minPriority put OperatorPriority(Operator) into curPriority else put OperatorPriority(Operator) into curPriority put curPriority into minPriority end if local rightOperand; put parsedOperand() into rightOperand repeat forever put nextToken() into curToken if (curToken["Type"] <> "operator") then deferToken(curToken) return newInfixNode(OperatorPosition, Operator, leftOperand, rightOperand) end if local newPriority; put OperatorPriority(curToken["Value"]) into newPriority if (newPriority < minPriority) then deferToken(curToken) return newInfixNode(OperatorPosition, Operator, leftOperand, rightOperand) end if if (newPriority <= curPriority) then put newInfixNode(OperatorPosition, Operator, leftOperand, rightOperand) \ into leftOperand put curToken["Value"] into Operator put curToken["Position"] into OperatorPosition put OperatorPriority(Operator) into curPriority put parsedOperand() into rightOperand else # newPriority > curPriority put parsedExpression(rightOperand, curToken["Value"], curToken["Position"]) \ into rightOperand end if end repeat end parsedExpression -------------------------------------------------------------------------------- -- parsedOperand parses (and yields) a single operand -- -------------------------------------------------------------------------------- private function parsedOperand local curToken; put nextToken() into curToken switch curToken["Type"] case "eof" throw "unexpected end-of-input" case "number" return newLiteralNode(curToken["Position"],curToken["Value"]) case "(" local SubExpression; put parsedExpression() into SubExpression local futureToken; put nextToken() into futureToken if (futureToken["Type"] <> ")") then throw "')' expected (to end a grouped expression at position " & \ curToken["Position"] & ")" end if return newGroupNode(curToken["Position"],SubExpression) case "operator" if (curToken["Value"] = "+") or (curToken["Value"] = "-") then put nextToken() into futureToken if (futureToken["Type"] = "number") then # apply sign to numbers if (curToken["Value"] = "-") then put -futureToken["Value"] into futureToken["Value"] end if return newLiteralNode(curToken["Position"],futureToken["Value"]) end if deferToken futureToken if (curToken["Value"] = "+") then return parsedOperand() else return newPrefixNode(curToken["Position"],"-",parsedOperand()) end if else throw "prefix operator expected at position " & curToken["Position"] end if case "identifier" put nextToken() into futureToken if (futureToken["Type"] = "(") then return newFunctionNode( \ curToken["Position"], curToken["Value"], \ parsedArgumentList() \ ) else deferToken futureToken return newConstantNode(curToken["Position"],curToken["Value"]) end if default throw "operand expected at position " & curToken["Position"] end switch end parsedOperand -------------------------------------------------------------------------------- -- parsedArgumentList parses the argument list for a function invocation -- -------------------------------------------------------------------------------- private function parsedArgumentList local ArgumentList, ArgumentCount; put 0 into ArgumentCount local curToken; put nextToken() into curToken if (curToken["Type"] = ")") then return ArgumentList deferToken(curToken) repeat forever add 1 to ArgumentCount put parsedExpression() into ArgumentList[ArgumentCount] put nextToken() into curToken switch curToken["Type"] case "eof" throw "unexpected end-of-input within function invocation" case ")" return ArgumentList case "," break default throw "',' or ')' expected at position " & curToken["Position"] end switch end repeat # return ArgumentList end parsedArgumentList /******************************************************************************* * * * Evaluator * * * *******************************************************************************/ -------------------------------------------------------------------------------- -- ConstantValue delivers the value of a given constant -- -------------------------------------------------------------------------------- private function ConstantValue ConstantName switch ConstantName case "pi" return pi case "e" return exp(1) # # <<<< additional constants may be added here <<<< # default throw "unknown constant '" & ConstantName & "'" end switch end ConstantValue -------------------------------------------------------------------------------- -- expectNArguments verifies the presence of all required arguments -- -------------------------------------------------------------------------------- private command expectNArguments ArgumentList, nMin, nMax, Position if (ArgumentList is not an array) then if (nMin <> 0) then throw "no arguments given (in function call at position " & Position & ")" end if else local ArgumentCount; put the number of elements of ArgumentList into ArgumentCount switch case (ArgumentCount < nMin) throw "insufficient arguments given (in function call at position " & \ Position & ")" case (nMax is not empty) and (ArgumentCount > nMax) throw "too many arguments given (in function call at position " & \ Position & ")" end switch end if end expectNArguments -------------------------------------------------------------------------------- -- FunctionValue invokes a given function and delivers the result -- -------------------------------------------------------------------------------- private function FunctionValue FunctionName, Position, ArgumentList switch FunctionName case "sin" expectNArguments ArgumentList, 1,1, Position return sin(ValueOfNode(ArgumentList[1])) case "cos" expectNArguments ArgumentList, 1,1, Position return cos(ValueOfNode(ArgumentList[1])) case "tan" expectNArguments ArgumentList, 1,1, Position return tan(ValueOfNode(ArgumentList[1])) case "deg2rad" expectNArguments ArgumentList, 1,1, Position return ValueOfNode(ArgumentList[1])*pi/180 case "rad2deg" expectNArguments ArgumentList, 1,1, Position return ValueOfNode(ArgumentList[1])*180/pi case "asin" expectNArguments ArgumentList, 1,1, Position return asin(ValueOfNode(ArgumentList[1])) case "acos" expectNArguments ArgumentList, 1,1, Position return acos(ValueOfNode(ArgumentList[1])) case "atan" expectNArguments ArgumentList, 1,1, Position return atan(ValueOfNode(ArgumentList[1])) case "exp" expectNArguments ArgumentList, 1,1, Position return exp(ValueOfNode(ArgumentList[1])) case "exp2" expectNArguments ArgumentList, 1,1, Position return exp2(ValueOfNode(ArgumentList[1])) case "exp10" expectNArguments ArgumentList, 1,1, Position return exp10(ValueOfNode(ArgumentList[1])) case "ln" expectNArguments ArgumentList, 1,1, Position return ln(ValueOfNode(ArgumentList[1])) case "log2" expectNArguments ArgumentList, 1,1, Position return log2(ValueOfNode(ArgumentList[1])) case "log10" expectNArguments ArgumentList, 1,1, Position return log10(ValueOfNode(ArgumentList[1])) case "sum" expectNArguments ArgumentList, 1,, Position local Outcome; put ValueOfNode(ArgumentList[1]) into Outcome repeat with i = 2 to the number of elements of ArgumentList add ValueOfNode(ArgumentList[i]) to Outcome end repeat return Outcome case "min" expectNArguments ArgumentList, 1,, Position put ValueOfNode(ArgumentList[1]) into Outcome repeat with i = 2 to the number of elements of ArgumentList local newValue; put ValueOfNode(ArgumentList[i]) into newValue if (newValue < Outcome) then put newValue into Outcome end repeat return Outcome case "max" expectNArguments ArgumentList, 1,, Position put ValueOfNode(ArgumentList[1]) into Outcome repeat with i = 2 to the number of elements of ArgumentList put ValueOfNode(ArgumentList[i]) into newValue if (newValue > Outcome) then put newValue into Outcome end repeat return Outcome # # <<<< additional functions may be added here <<<< # default throw "unknown function '" & FunctionName & "'" end switch end FunctionValue -------------------------------------------------------------------------------- -- ValueOfNode evaluates a given node and yields the result -- -------------------------------------------------------------------------------- private function ValueOfNode ParseNode switch ParseNode["Type"] case "literal" return ParseNode["Value"] case "constant" return ConstantValue(ParseNode["Name"]) case "function" return FunctionValue(ParseNode["Name"], ParseNode["Position"], ParseNode["ArgumentList"]) case "group" return ValueOfNode(ParseNode["Expression"]) case "prefix" if (ParseNode["Operator"] = "+") then # not really necessary return ValueOfNode(ParseNode["operand"]) else return -ValueOfNode(ParseNode["operand"]) end if case "infix" local leftOperand; put ValueOfNode(ParseNode["leftOperand"]) into leftOperand local rightOperand; put ValueOfNode(ParseNode["rightOperand"]) into rightOperand switch ParseNode["Operator"] case "+" return leftOperand + rightOperand case "-" return leftOperand - rightOperand case "*" return leftOperand * rightOperand case "/" return leftOperand / rightOperand # may throw an error case "%" return leftOperand mod rightOperand # may throw an error case "\" return leftOperand div rightOperand # may throw an error default throw "internal error: unknown infix operator '" & ParseNode["Operator"] & "'" end switch default throw "internal error: unknown node type '" & ParseNode["Type"] & "'" end switch end ValueOfNode 4ExpressionParser Library Stack  ULucida Grande ULucida GrandecREVTempMaster-windowManagerPlacefalsemenubarlinkHiliteColor blendLevel0 cantAbortfalse maxWidth65535rect194,114,594,514 patterns colors decorationsdefaulticonicfalselinkVisitedColorshadowtrue cantDeletetrueid1002altId0 hcAddressingfalsestartUpIconicfalse windowShape0titleExpressionParser Library Stack linkColor cantModifyfalse textStyle maxHeight65535underlineLinksstyletopleveldestroyWindowfalse liveresizingfalse passwordscroll0 behaviorpasskeyicon0nameExpressionParser resizabletrue alwaysBufferfalseformatForPrintingfalsevisibletruemetalfalse minWidth32 destroyStacktrue textSize textFont dynamicPathsfalse minHeight32cREVGeometryCachestackID1002 cREVGeneralbreakpointconditionsscripteditorvscroll8249scripteditorselection20467 breakpointsbreakpointstates @cREVTempMasterinksrcCopy dontSearchfalsemarkfalsethreeDtrue blendLevel0rect 0,0,400,400 behaviordefaultButton patterns name card id 1002colors layer1 borderWidth2 cantDeletefalseid1002altId0 showBorderfalse textSize textFont textStylecREVGeometryCachetotal0order