\ REQUIRE [IF] lib/include/tools.f REQUIRE CASE lib/ext/case.f ( port to SPF 10.06.2005 ~ygrek ) MODULE: OPG_Formula_Translation 0 [IF] If there are redefinitions of things you already have, ignore them for now (it won't hurt) and later comment them out. If there are redefinitions you can't tolerate, fix them and let me know. No logic changes since 1997, except: 2000-05-15 Use `|...|` for normal Forth. (Changed from `{...}` because in the FSL, `{` is used for arrays, in SwiftForth, `{...}` is used for commentary, and in MPE, `{...}` is used for local variables.) [THEN] 0 [IF] ======================================================= TEXT Wil Baden 1997-11-04 - 2000-05-15 This is a implementation of Formula Translation. It will translate Fortran-style assignments `varname=expr` and expressions `expr` to Forth. GLOSSARY ?? Accept-Char-for-Formula Apply-Operators Callable Code-Operation FAILURE Get-Formula Is+or- Is-D-or-E Is-a-Number Is-an-Identifier LET Memorable NEXT-CHAR Op-Code Op-Fetch Op-Literal Op-Pop Op-Push Op-Stack Op-Stack-Size Op-Store Op-Top Operator-Precedence Parenthesis-Count Replace-Last-Char SUCCESS Translate-Expression Translate-Formula Translate-Operand-Operator Translate-Operation Word-Holder /GLOSSARY There is just one end-user word `LET`. The formula is terminated by `:`. (`LET` and `:` have been adopted from Basic.) It can be used compiling or interpreting. It is not state-smart. An segment between |bars| will be treated as normal Forth. The resulting translations are the natural expansions. LET a-b-c-d: a F@ b F@ F- c F@ F- d F@ F- LET a*b-c*d: a F@ b F@ F* c F@ d F@ F* F- LET (a-b)*(c-d): a F@ b F@ F- c F@ d F@ F- F* LET x = -1: -1.E x F! LET x = (-b - SQRT (b * |FDUP| - 4*a*c)) / (2*a): b F@ FNEGATE b F@ FDUP F* 4.E a F@ F* c F@ F* F- FSQRT F- 2.E a F@ F* F/ x F! If a function doesn't begin with `F` it will first look for it with `F` prefixed. All numbers are floating point. Variables begin with a letter, continue with letters and digits, and are not followed by a left parenthesis mark. Function-calls have the same form but are followed by a left parenthesis mark. The operators are: + - * / ** or ^ Assignments are made with `=`. Multiple arguments of a function are separated by commas. Spaces are deleted before translation, except between `|` and `|`. Variable `DEBUG` on will show code being translated. This program uses Julian V. Noble's concept but not his implementat Thanks to Marcel Hendrix for his ideas for extending the system. Examples of Use Operator Precedence goes through the expression putting out operands as it reaches them and saving operators. Operators are put out when an operator of less or equal precedence is reached. Thus higher precedence is performed before lower precedence. See tests at the end of the file for examples. ------------------------------------------------------- [THEN] \ Formula Translation using Operator Precedence Grammar VARIABLE DEBUG 0 DEBUG ! ( This is a common name. ) ( `DEBUG` occurs in one place below. Change it here and there. ) 0 [IF] ======================================================= Elementary Tools FAILURE ( -- ) False exit. SUCCESS ( -- ) True exit. ?? ( x "aword" -- ) _x_ `IF` _aword_ `THEN` ------------------------------------------------------- [THEN] \ ~ygrek \ Я так понял что u /STRING используется для прохода по строке. \ сдвигая текущую позицию на u символов. \ Никаких проверок не делается. : /STRING ( str len n -- str+n len-n ) \ SWAP OVER - SWAP + SWAP >R SWAP R@ + SWAP R> - ; \ из toolbelt.f : SCAN ( str len char -- str+i len-i ) >R BEGIN DUP WHILE OVER C@ R@ - WHILE 1 /STRING REPEAT THEN R> DROP ; \ Common usage, especially with me. Comment out what you already have. : /SPLIT ( a m b n -- b n a m-n ) DUP >R 2SWAP R> - ; : ANDIF S" DUP IF DROP " EVALUATE ; IMMEDIATE : ORIF S" DUP 0= IF DROP " EVALUATE ; IMMEDIATE : BOUNDS ( str len -- str+len str ) OVER + SWAP ; : IS-ALPHA ( char -- flag ) 32 OR [CHAR] a - 26 U< ; : IS-DIGIT ( char -- flag ) [CHAR] 0 - 10 U< ; : IS-ALNUM ( char -- flag ) DUP IS-ALPHA ORIF DUP IS-DIGIT THEN NIP ; : NOT ( x -- flag ) S" 0= " EVALUATE ; IMMEDIATE : OFF 0 SWAP ! ; : ON TRUE SWAP ! ; : PLACE ( str len addr -- ) 2DUP 2>R CHAR+ SWAP CHARS MOVE 2R> C! ; : FAILURE S" FALSE EXIT " EVALUATE ; IMMEDIATE : SUCCESS S" TRUE EXIT " EVALUATE ; IMMEDIATE : ?? ( x "word" -- ) POSTPONE IF BL WORD COUNT EVALUATE POSTPONE THEN ; IMMEDIATE 0 [IF] ======================================================= Character Handling NEXT-CHAR ( -- char or 0 for EOL or negative for EOF ) Get character from input stream. Used in `Get-Formula`. Replace-Last-Char ( str len char -- str len ) Replace last character in a string. Used in `Op-Literal` and `Accept-Char-for-Formula`. Is+or- ( char -- flag ) Test _char_ for `+` or `-`. Used in `Is-a-Number`. `[+-]` Is-D-or-E ( char -- flag ) Test _char_ for `D`, `E`, `d`, or `e`. Used in `Is-a-Number` and `Op-Literal`. `[DEde]` ------------------------------------------------------- [THEN] : NEXT-CHAR ( -- char or 0 for EOL or negative for EOF ) SOURCE >IN @ > ( addr flag) IF >IN @ CHARS + C@ 1 >IN +! ELSE DROP REFILL 0= ( ) THEN ; : Replace-Last-Char ( str len char -- str len ) >R 2DUP CHARS + R> SWAP C! ; : Is+or- ( char -- flag ) DUP [CHAR] + = SWAP [CHAR] - = OR ; : Is-D-or-E ( char -- flag ) 32 OR [CHAR] d - 2 U< ; 0 [IF] ======================================================= Is-a-Number ( str len -- str' len' flag ) This awful-looking code walks through syntax for a number. Used in `Translate-Operand-Operator`. Regular Expression [+-]?[0-9]*([.][0-9]*)?([DEde](([-+][0-9])?[0-9]*)? ------------------------------------------------------- [THEN] : Is-a-Number ( str len -- str' len' flag ) DUP 0= ?? FAILURE \ [-+] Any sign. OVER C@ Is+or- IF 1 /STRING DUP 0= ?? FAILURE THEN \ [.]?[0-9] Begins with digit or decimal point and digit. OVER C@ IS-DIGIT ORIF OVER C@ [CHAR] . = THEN 0= ?? FAILURE OVER C@ [CHAR] . = IF DUP 1 = ?? FAILURE OVER CHAR+ C@ IS-DIGIT NOT ?? FAILURE THEN \ [0-9]* Any digits. BEGIN OVER C@ IS-DIGIT WHILE 1 /STRING DUP 0= ?? SUCCESS REPEAT \ [.][0-9]* Decimal point and any digits OVER C@ [CHAR] . = IF BEGIN 1 /STRING DUP 0= ?? SUCCESS OVER C@ IS-DIGIT NOT UNTIL THEN \ [DEde](([-+][0-9])?[0-9]*)? Exponent, sign and digits. OVER C@ Is-D-or-E IF 1 /STRING DUP 0= ?? SUCCESS OVER C@ Is+or- IF 1 /STRING DUP 0= ?? FAILURE OVER C@ IS-DIGIT NOT ?? FAILURE THEN \ [0-9]* BEGIN DUP 0= ?? SUCCESS OVER C@ IS-DIGIT WHILE 1 /STRING REPEAT THEN SUCCESS ; 0 [IF] ======================================================= Is-an-Identifier ( str len -- str' len' flag ) An identifier is a letter followed by letters and digits. Used in `Translate-Operand-Operator` and `Translate-Formula`. ------------------------------------------------------- [THEN] : Is-an-Identifier ( str len -- str' len' flag ) DUP 0= ?? FAILURE OVER C@ IS-ALPHA NOT ?? FAILURE BEGIN 1 /STRING DUP 0= ?? SUCCESS OVER C@ IS-ALNUM NOT UNTIL SUCCESS ; 0 [IF] ======================================================= Op-Stack Operations Op-Stack-Size ( -- n ) Maximum size of `Op-Stack`. Used in `Op-Push`. Op-Stack ( -- addr) Stack to hold operators. Op-Push ( op -- ) Push _op_ on top of `Op-Stack`. Op-Top ( -- op ) The operator on top of `Op-Stack`. Op-Pop ( -- ) Remove top of `Op-Stack`. ------------------------------------------------------- [THEN] 30 CONSTANT Op-Stack-Size CREATE Op-Stack Op-Stack-Size 1+ CELLS ALLOT : Op-Push ( op -- ) Op-Stack @ Op-Stack-Size CELLS < NOT ABORT" Too Many Elements -- Increase Op-Stack-Size " 1 CELLS Op-Stack +! Op-Stack DUP @ + ! ; : Op-Top ( -- op ) Op-Stack DUP @ + @ ; : Op-Pop ( -- ) -1 CELLS Op-Stack +! ; 0 [IF] ======================================================= Application Tools Parenthesis-Count ( -- addr ) Tally for parentheses. Word-Holder ( -- addr ) Buffer for name when modifying it. Memorable ( str len -- ) Look up variable. Used in `Op-Store` and `Op-Fetch`. Callable ( str len -- str' len' ) Look up function. Used in `Code-Operation`. Translate-Operation ( addr len -- ) Translate operation. [Can't think of better explanation.] Op-Store ( str len -- )( F: r -- ) Make assignment. Used in `Translate-Formula`. Op-Fetch ( str len -- )( F: -- r ) Pick up variable. Used in `Translate-Operand-Operator`. Op-Literal ( str len -- )( F: -- r ) Take care of literal. Used in `Translate-Operand-Operator`. ------------------------------------------------------- [THEN] VARIABLE Parenthesis-Count 1 CONSTANT Left-Paren 2 CONSTANT Right-Paren 8 CONSTANT Negation 9 CONSTANT Function-Call 10 CONSTANT Op-Dummy CREATE Word-Holder 32 CHARS ALLOT : Memorable ( str len -- ) 31 MIN Word-Holder PLACE ( ) Word-Holder FIND 0= IF COUNT TYPE SPACE TRUE ABORT" Not Found " THEN DROP ; : Callable ( str len -- str' len' ) OVER C@ [CHAR] F = NOT IF 2DUP 30 MIN DUP 1+ Word-Holder C! Word-Holder CHAR+ PLACE ( . .) [CHAR] F Word-Holder CHAR+ C! Word-Holder FIND NIP IF 2DROP Word-Holder COUNT THEN THEN ; : Translate-Operation ( addr len -- ) DEBUG @ IF 2DUP TYPE SPACE THEN EVALUATE ; : Op-Store ( str len -- )( F: r -- ) 2DUP Memorable Translate-Operation S" F! " Translate-Operation ; : Op-Fetch ( str len -- )( F: -- r ) 2DUP Memorable Translate-Operation S" F@ " Translate-Operation ; VARIABLE Literal-State : Op-Literal ( str len -- )( F: -- r ) Literal-State OFF Word-Holder 0 2SWAP CHARS BOUNDS ?DO I C@ Is-D-or-E IF Literal-State ON THEN I C@ Replace-Last-Char 1+ 1 CHARS +LOOP Literal-State @ 0= IF [CHAR] E Replace-Last-Char 1+ THEN Translate-Operation ; : Op-Code ( str len -- str len code ) DUP 0= IF 0 ELSE CASE OVER C@ [CHAR] ) OF 2 ENDOF [CHAR] + OF 3 ENDOF [CHAR] - OF 4 ENDOF [CHAR] * OF 5 ENDOF [CHAR] / OF 6 ENDOF [CHAR] ^ OF 7 ENDOF [CHAR] , OF 0 ENDOF DUP . EMIT TRUE ABORT" Illegal Operator " 0 ENDCASE THEN ; : Operator-Precedence ( code -- precedence ) CASE -1 OF -1 ENDOF \ Bottom Mark 0 OF 2 ENDOF \ Termination or Comma 1 OF 1 ENDOF \ Left Paren 2 OF 1 ENDOF \ Right Paren 3 OF 3 ENDOF \ Plus 4 OF 3 ENDOF \ Minus 5 OF 4 ENDOF \ Times 6 OF 4 ENDOF \ Divide 7 OF 5 ENDOF \ Power 8 OF 3 ENDOF \ Negation 9 OF 1 ENDOF \ Function-Call 10 OF 0 ENDOF \ Dummy DROP TRUE ABORT" Invalid Operation " 0 ENDCASE ; : Code-Operation ( code -- ) CASE 1 OF 0 -1 Parenthesis-Count +! ENDOF 2 OF 0 ENDOF 3 OF S" F+ " ENDOF 4 OF S" F- " ENDOF 5 OF S" F* " ENDOF 6 OF S" F/ " ENDOF 7 OF S" F** " ENDOF 8 OF S" FNEGATE " ENDOF 9 OF Op-Pop Op-Top Op-Pop Op-Top -1 Parenthesis-Count +! Callable ENDOF DROP TRUE ABORT" Invalid Operator " 0 ENDCASE ( addr k) ?DUP ?? Translate-Operation ; : Apply-Operators ( str len -- str' len' ) BEGIN Op-Code ( str len code) DUP 2SWAP 2>R ( code code)( R: str len) >R Operator-Precedence >R ( )( R: . . . precedence) BEGIN Op-Top Operator-Precedence R@ < NOT WHILE Op-Top Code-Operation Op-Pop REPEAT R> DROP R> 2R> ( code str len)( R: ) DUP IF 1 /STRING THEN ROT ( str len code) DUP Right-Paren = WHILE DROP Op-Pop REPEAT ?DUP ?? Op-Push ; : Translate-Operand-Operator ( str len -- str' len' ) \ Is it a variable or function-call? 2DUP Is-an-Identifier IF ( a n a+k n-k) DUP ANDIF OVER C@ [CHAR] ( = THEN IF \ It's a function-call. Op-Dummy Op-Push /SPLIT ( a+k n-k a k) Op-Push Op-Push Function-Call Op-Push ( a+k n-k) 1 Parenthesis-Count +! 1 /STRING ELSE \ It's a variable. 2>R R@ - Op-Fetch 2R> Apply-Operators THEN EXIT THEN 2DROP ( str len) \ Is it a number? 2DUP Is-a-Number IF ( a n a+k n-k) 2>R R@ - Op-Literal 2R> Apply-Operators EXIT THEN 2DROP ( str len) \ Is it a left paren? OVER C@ [CHAR] ( = IF \ ) Op-Dummy Op-Push Left-Paren Op-Push 1 Parenthesis-Count +! 1 /STRING EXIT THEN \ Is it a lonely minus sign? OVER C@ [CHAR] - = IF Negation Op-Push 1 /STRING EXIT THEN \ Is it a lonely plus sign? OVER C@ [CHAR] + = ANDIF DUP 1 > THEN IF 1 /STRING EXIT THEN \ Is it normal Forth? OVER C@ [CHAR] | = IF 1 /STRING 2DUP [CHAR] | SCAN /SPLIT 2SWAP 2>R Translate-Operation 2R> DUP IF 1 /STRING THEN Apply-Operators EXIT THEN \ Oops. CR TYPE CR TRUE ABORT" Illegal Operand " ; : Translate-Expression ( str len -- ) BEGIN DUP WHILE Translate-Operand-Operator REPEAT 2DROP Parenthesis-Count @ ABORT" Unmatched Parens " ; : Translate-Formula ( str len -- ) 0 Op-Stack ! 0 Parenthesis-Count ! 2DUP Is-an-Identifier ( str len str' len' flag) ANDIF DUP ANDIF OVER C@ [CHAR] = = THEN THEN IF ( str len str' len') /SPLIT Op-Push Op-Push -1 Op-Push ( str' len') 1 /STRING Translate-Expression ( ) Op-Top -1 = NOT ABORT" Invalid Expression " Op-Pop Op-Top Op-Pop Op-Top Op-Store ELSE 2DROP ( str len) -1 Op-Push Translate-Expression ( ) THEN Op-Stack @ 1 CELLS = NOT ABORT" Invalid Formula " ; 255 CONSTANT Formula-Length CREATE Formula Formula-Length 1+ CHARS ALLOT VARIABLE Keep-Spaces : Accept-Char-for-Formula ( str length char -- str length' ) OVER Formula-Length > ABORT" Formula Length Overflow " CASE [CHAR] | OF [CHAR] | Replace-Last-Char 1+ Keep-Spaces DUP @ NOT SWAP ! ENDOF [CHAR] * OF DUP ANDIF 2DUP 1- CHARS + C@ [CHAR] * = THEN IF 1- [CHAR] ^ ELSE [CHAR] * THEN Replace-Last-Char 1+ ENDOF Replace-Last-Char 1+ 0 ENDCASE ; : Get-Formula ( "multi-lines" -- addr len ) Keep-Spaces OFF Formula 0 ( addr len) BEGIN NEXT-CHAR ( addr len char) DUP 0< ABORT" End of File " DUP [CHAR] : = NOT WHILE DUP BL > ORIF DUP BL = Keep-Spaces @ AND THEN IF Accept-Char-for-Formula ELSE DROP THEN ( addr len) REPEAT DROP ; EXPORT : LET ( "formula:" -- )( F: -- | values ) Get-Formula Translate-Formula ; IMMEDIATE ;MODULE \ EOF \ ---------------------------------------------------------- \ TESTS \ ---------------------------------------------------------- REQUIRE F. lib/include/float2.f \ Это обязательно потому-что FVARIABLE создаёт 8-байтный флоат \ а F! F@ работают с 10-байтным : F! DF! ; : F@ DF@ ; FDOUBLE FVARIABLE a FVARIABLE b FVARIABLE c FVARIABLE x FVARIABLE w : TEST0 CR LET b+c: FE. CR LET b-c: FE. CR LET 10000000*(b-c)/(b+c): FE. ; LET b = 3: LET c = 4: .( TEST0) TEST0 : TEST1 LET a = b*c-3.17e-5/TANH(w)+ABS(x): CR LET a: F. ; CR .( TEST1) LET w = 1.e-3: LET x = -2.5: TEST1 FVARIABLE HALFPI LET HALFPI = 2*ATAN(1): .( PI=) LET HALFPI + |FDUP|: F. FVARIABLE disc ( Used for discriminant ) : QUADRATICROOT ( F: a b c -- r1 r2 ) c F! b F! a F! \ Pickup coefficients. LET disc = SQRT(b*b-4*a*c): \ Set discriminant. LET (-b+disc)/(2*a), (-b-disc)/(2*a): \ Put values on f-stack. ; CR .( Solve x*x-3*x+2 ) LET QUADRATICROOT (1,-3, 2) : F. SPACE F. CR .( Find goldenratio ) LET MAX(QUADRATICROOT (1,-1,-1)) : F. CR .( You can also write ) 1.E -1.E -1.E QUADRATICROOT FMAX F. : FACTORIAL ( n -- )( F: -- r ) LET w = 1: LET x = 1: 0 ?DO LET w = w * x: LET x = x + 1: LOOP LET w: ; CR .( Another way ) : FACTORIAL ( n -- )( F: -- r ) LET w = 1: 0 ?DO LET w = w * | I 1+ S>D D>F |: LOOP LET w: ; 6 FACTORIAL F. .( or ) LET FACTORIAL(|6|): F. CR CR .( Timing) CR REQUIRE Mark ~micro/lib/timer.f 4.9e a F! 5e b F! 37.2e c F! : time1 Timer::Mark 10000000 0 DO LET SQRT(ABS(COS(b+c)*SIN(b+a)+(c+a)*(c+b)))+LN(a+b)*EXP(a+c): FDROP LOOP Timer::ElapsedMs ." Elapsed : " . CR ; : time2 Timer::Mark 10000000 0 DO a F@ b F@ F+ FLN a F@ c F@ F+ FEXP F* b F@ c F@ F+ FCOS b F@ a F@ F+ FSIN F* F+ FABS FSQRT c F@ a F@ F+ c F@ b F@ F+ F* F+ FDROP LOOP Timer::ElapsedMs ." Elapsed : " . CR ; time1 time2 BYE