パーサー

 投稿者:しばっち  投稿日:2019年 7月 4日(木)21時54分12秒
  ライブラリーを使ってパーサー(math parser:数式解析器 ?)を作ってみました。
sampleフォルダ内にあるINTERPRE.basのDLL版 ?

vc++2012にてコンパイルしました。

EXPRTKライブラリー(ヘッダーライブラリー) http://partow.net/programming/exprtk/index.html

https://hp.vector.co.jp/authors/VA008683/QA_INPUT.htm

INPUT文でSQR(3)/2とは入力できませんが、SQRT(3)/2となら入力できます。(笑)
三角関数をはじめ指数関数、対数関数が使用できます。
パラメータ(変数)は使えません。

LET A$="SQRT(3)/2"
!!INPUT A$
PRINT PARSER(A$)
PRINT PARSER("SIN(PI/3)")
PRINT PARSER("2^PI")
PRINT PARSER("EXP(PI*LOG(2))")
FOR X=0 TO 180 STEP 15
   LET X$="COS("&STR$(X)&"*PI/180)"
   PRINT X$;":";PARSER(X$)
NEXT X
END

EXTERNAL  FUNCTION PARSER(X$)
OPTION CHARACTER BYTE
IF X$="" THEN
   PRINT "ERROR"
   STOP
END IF
LET PARSER=PARSER16(LCASE$(X$))

FUNCTION PARSER16(X$)
   ASSIGN ".\DLL\parser16.dll","parser16",FPU
END FUNCTION
END FUNCTION
-----------------------------------------------------------------
こちらはパラメータにu,v,w,x,y,zが使用できます。


LET EXPRESSION$="SIN(2*PI/X)"
FOR X=1 TO 10
   PRINT X;":";PARSER(EXPRESSION$,U,V,W,X,Y,Z);SIN(2*PI/X)
NEXT X
PRINT INTEGRAL("1/X",1,2,500);LOG(2)
END

EXTERNAL  FUNCTION INTEGRAL(A$,A,B,N)
DIM R(0 TO 3)
LET R(0)=3/8
LET R(1)=9/8
LET R(2)=9/8
LET R(3)=3/8
LET H=(B-A)/N/3
FOR K=0 TO N-1
   FOR J=0 TO 3
      LET X=A+H*(3*K+J)
      LET S=S+R(J)*H*PARSER(A$,U,V,W,X,Y,Z)
   NEXT J
NEXT K
LET INTEGRAL=S
END FUNCTION

EXTERNAL  FUNCTION PARSER(EXPRESSION$,U,V,W,X,Y,Z)
OPTION CHARACTER BYTE
LET U$=PACKDBL$(U)
LET V$=PACKDBL$(V)
LET W$=PACKDBL$(W)
LET X$=PACKDBL$(X)
LET Y$=PACKDBL$(Y)
LET Z$=PACKDBL$(Z)
IF EXPRESSION$="" THEN
   PRINT "ERROR"
   STOP
END IF
LET PARSER=PARSER16(LCASE$(EXPRESSION$),U$,V$,W$,X$,Y$,Z$)

FUNCTION PARSER16(INPUT$,U$,V$,W$,X$,Y$,Z$)
   ASSIGN ".\DLL\parser16_2.dll","parser16",FPU
END FUNCTION
END FUNCTION

                              parser16_2.cpp

#include <cstdio>
#include <cmath>
#include <string>
#include <exprtk.hpp>

using namespace std;

extern "C"  __declspec(dllexport)  double parser16(char *input,double *u,double *v,double *w,double *x,double *y,double *z)
{
    typedef exprtk::symbol_table<double> symbol_table_t;
    typedef exprtk::expression<double>     expression_t;
    typedef exprtk::parser<double>             parser_t;

    double val;
    string expression_string = input;

    symbol_table_t symbol_table;
    symbol_table.add_variable("u",*u);
    symbol_table.add_variable("v",*v);
    symbol_table.add_variable("w",*w);
    symbol_table.add_variable("x",*x);
    symbol_table.add_variable("y",*y);
    symbol_table.add_variable("z",*z);
    symbol_table.add_constants();

    expression_t expression;
    expression.register_symbol_table(symbol_table);

    parser_t parser;
    if (parser.compile(expression_string,expression)) {
        val = expression.value();
        return val;
    } else
        return -99999999.0;
}

-----------------------------------------------------------------
自前の関数を幾つか追加した強化版


PRINT PARSER("GAMMA(1.5)",U,V,W,X,Y,Z);SQR(PI)/2
LET U=4.125
LET V=3
PRINT PARSER("COMB(U,V)",U,V,W,X,Y,Z);COMB(U,V) !'パラメータは u,v,w,x,y,zのみ
PRINT PARSER("COMB(3.125,2.75)",U,V,W,X,Y,Z)
PRINT PARSER("PERM(5.375,3.125)",U,V,W,X,Y,Z)
END

EXTERNAL  FUNCTION PARSER(EXPRESSION$,U,V,W,X,Y,Z)
OPTION CHARACTER BYTE
LET U$=PACKDBL$(U)
LET V$=PACKDBL$(V)
LET W$=PACKDBL$(W)
LET X$=PACKDBL$(X)
LET Y$=PACKDBL$(Y)
LET Z$=PACKDBL$(Z)
IF EXPRESSION$="" THEN
   PRINT "ERROR"
   STOP
END IF
LET PARSER=PARSER16(LCASE$(EXPRESSION$),U$,V$,W$,X$,Y$,Z$)

FUNCTION PARSER16(INPUT$,U$,V$,W$,X$,Y$,Z$)
   ASSIGN ".\DLL\parser16_4.dll","parser16",FPU
END FUNCTION
END FUNCTION

実行結果

.886226925452758  .886226925452758
4.5654296875  4.5654296875
1.78944520280366
90.3737984203253
-----------------------------------------------------------------
FPARSERライブラリー  http://warp.povusers.org/FunctionParser/fparser.html

こちらはパラメーターを任意に指定できます。

OPTION BASE 0
DIM S(10)
PRINT PARSER("SIN(PI/3)","",1,S)
PRINT PARSER("SQRT(5)","",1,S)
PRINT PARSER("EXP(2*LOG(5))","",1,S)
PRINT PARSER("CBRT(2)","",1,S)
PRINT PARSER("LOG10(2)","",1,S)
FOR X=0 TO 180 STEP 15
   LET S(0)=X
   PRINT X;":";PARSER("SIN(X*PI/180)","X",1,S)
NEXT X
LET S(0)=4
LET S(1)=3
LET S(2)=-10
LET S(3)=6
LET S(4)=2
LET S(5)=10
LET S(6)=-5
LET S(7)=6
PRINT PARSER("3*A^3+2*B^2+C/5-D*3*E+F/G+H*3","A,B,C,D,E,F,G,H",8,S)
PRINT 3*S(0)^3+2*S(1)^2+S(2)/5-S(3)*3*S(4)+S(5)/S(6)+S(7)*3

INPUT T
LET XX=T
LET S$="(X*X-T)/(2*X)"
!LET S$="(X*X*X-T)/(3*X*X)"
DO
   LET X=XX
   LET S(0)=X
   LET S(1)=T
   LET XX=X-PARSER(S$,"X,T",2,S)
LOOP UNTIL ABS(XX-X)<1E-12
PRINT XX
END

EXTERNAL  FUNCTION PARSER(INPUT$,PARA$,N,A())
OPTION CHARACTER BYTE
IF INPUT$="" OR N=0 THEN
   PRINT "ERROR"
   STOP
END IF
LET A$=REPEAT$(CHR$(0),8*N)
FOR I=0 TO N-1
   LET A$(8*I+1:8*I+8)=PACKDBL$(A(I))
NEXT I
IF PARA$="" THEN LET PARA$="X"
LET PARSER=PARSER_(LCASE$(INPUT$),LCASE$(PARA$),A$)

FUNCTION PARSER_(INPUT$,PARA$,A$)
   ASSIGN ".\DLL\parser.dll","parser16",FPU
END FUNCTION
END FUNCTION

                              parser.cpp

#include "fparser.hh"

using namespace std;

extern "C"  __declspec(dllexport)  double parser16(char *input,char *ss,double *val)
{
    FunctionParser parser;

    parser.AddConstant("pi", 3.1415926535897932384626433);
    try {
        parser.Parse(input,ss);
        return parser.Eval(val);
    } catch(...) {
        return -99999999.0;
    }
}
-----------------------------------------------------------------
LEPTONライブラリー   https://simtk.org/projects/lepton

ナント十進BASIC上から導関数が求められるようになりました。
割と複雑な数式にも対応しているようです。

※くれぐれもバッファサイズを超えないように気を付けてください。
超えると多分落ちます。

https://hp.vector.co.jp/authors/VA008683/QA_Limitat.htm

OPTION CHARACTER BYTE
LET PARA$="X" ! Xで微分
DO
   READ IF MISSING THEN EXIT DO:F$
   DATA "4*X^4+3*X^3-2*X^2+X+1"
   DATA "SIN(X)/X"
   DATA "LOG(X)"
   DATA "X^X"
   DATA "SQRT(X)"
   DATA "COS(X*SIN(3*X))"
   DATA "X^2*EXP(X)"
   DATA "COS(1/X)"
   DATA "SINH(X)"
   DATA "ATAN(X)"
   CALL DIFF(F$,PARA$,DIFF$)
   PRINT "f(x)=";F$
   PRINT "f'(x)=";DIFF$
   CALL DIFF(DIFF$,PARA$,DIFF2$)
   PRINT "f''(x)=";DIFF2$
   PRINT
LOOP
END

EXTERNAL  SUB DIFF(INPUT$,PARA$,OUTPUT$)
OPTION CHARACTER BYTE
LET S$=REPEAT$(CHR$(0),5000) !バッファサイズ 5000文字分
CALL DIFF_(LCASE$(INPUT$),LCASE$(PARA$),S$)
FOR I=1 TO LEN(S$)
   IF ORD(S$(I:I))<32 THEN EXIT FOR
NEXT I
LET OUTPUT$=S$(1:I-1)

SUB DIFF_(INPUT$,PARA$,OUTPUT$)
   ASSIGN ".\DLL\parserdiff.dll","parserdiff"
END SUB
END SUB

                              parserdiff.cpp

#include <string>
#include <sstream>
#include <cmath>
#include "Lepton.h"

#pragma comment(lib, "lepton.lib")

using namespace std;

extern "C"  __declspec(dllexport)  void parserdiff(char *input,char *para,char *output)
{
    string s;
    ostringstream oss;
    try {
        oss << Lepton::Parser::parse(input).differentiate(para).optimize();
        s=oss.str();
        strcpy(output,s.c_str());
    } catch(...) {
        strcpy(output,"error");
    }
}

実行結果

f(x)=4*X^4+3*X^3-2*X^2+X+1
f'(x)=1+(((16*(cube(x)))+(9*(square(x))))-(4*(x)))
f''(x)=-4+((48*(square(x)))+(18*(x)))

f(x)=SIN(X)/X
f'(x)=(((x)*(cos(x)))-(sin(x)))/(square(x))
f''(x)=(((square(x))*(((cos(x))-((x)*(sin(x))))-(cos(x))))-((((x)*(cos(x)))-(sin(x)))*(2*(x))))/(square(square(x)))

f(x)=LOG(X)
f'(x)=recip(x)
f''(x)=-(recip(square(x)))

f(x)=X^X
f'(x)=((x)*((x)^(-1+(x))))+((log(x))*((x)^(x)))
f''(x)=(((x)*(((-1+(x))*((x)^(-1+(-1+(x)))))+((log(x))*((x)^(-1+(x))))))+((x)^(-1+(x))))+(((log(x))*(((x)*((x)^(-1+(x))))+((log(x))*((x)^(x)))))+(((x)^(x))/(x)))

f(x)=SQRT(X)
f'(x)=0.5*(recip(sqrt(x)))
f''(x)=0.5*((-0.5*(recip(sqrt(x))))/(square(sqrt(x))))

f(x)=COS(X*SIN(3*X))
f'(x)=-((sin((x)*(sin(3*(x)))))*(((x)*(3*(cos(3*(x)))))+(sin(3*(x)))))
f''(x)=-(((sin((x)*(sin(3*(x)))))*((((x)*(-9*(sin(3*(x)))))+(3*(cos(3*(x)))))+(3*(cos(3*(x))))))+((((x)*(3*(cos(3*(x)))))+(sin(3*(x))))*((cos((x)*(sin(3*(x)))))*(((x)*(3*(cos(3*(x)))))+(sin(3*(x)))))))

f(x)=X^2*EXP(X)
f'(x)=((square(x))*(exp(x)))+((exp(x))*(2*(x)))
f''(x)=(((square(x))*(exp(x)))+((exp(x))*(2*(x))))+((2*(exp(x)))+((2*(x))*(exp(x))))

f(x)=COS(1/X)
f'(x)=(sin(recip(x)))/(square(x))
f''(x)=((-((square(x))*((cos(recip(x)))/(square(x)))))-((sin(recip(x)))*(2*(x))))/(square(square(x)))

f(x)=SINH(X)
f'(x)=cosh(x)
f''(x)=sinh(x)

f(x)=ATAN(X)
f'(x)=recip(1+(square(x)))
f''(x)=(-2*(x))/(square(1+(square(x))))

もちろんニュートン法にも利用できます。

INPUT U
LET XX=U
LET S$="X^2-U"
!LET S$="X^3-U"
!LET S$="X^4-U"
CALL DIFF(S$,"X",DIFF$)
LET F$="("&S$&")/("&DIFF$&")"
PRINT F$
DO
   LET X=XX
   LET XX=X-PARSER(F$,U,V,W,X,Y,Z)
LOOP UNTIL ABS(XX-X)<1E-12
PRINT XX;SQR(U)
END

EXTERNAL  FUNCTION PARSER(INPUT$,U,V,W,X,Y,Z)
OPTION CHARACTER BYTE
LET U$=PACKDBL$(U)
LET V$=PACKDBL$(V)
LET W$=PACKDBL$(W)
LET X$=PACKDBL$(X)
LET Y$=PACKDBL$(Y)
LET Z$=PACKDBL$(Z)
IF INPUT$="" THEN
   PRINT "ERROR"
   STOP
END IF
LET PARSER=PARSER_(LCASE$(INPUT$),U$,V$,W$,X$,Y$,Z$)

FUNCTION PARSER_(INPUT$,U$,V$,W$,X$,Y$,Z$)
   ASSIGN ".\DLL\parser2.dll","parser",FPU
END FUNCTION
END FUNCTION

EXTERNAL  SUB DIFF(INPUT$,PARA$,OUTPUT$)
      中略
END SUB


BASファイル13本及びソース、dllファイル合わせて26本と使用したライブラリーツール7種を
下記URLにアップしておきました。(parser.zip)
ぜひ、お試しください。

https://13.gigafile.nu/0803-cea376ccc4abf8593dc499561d37001b7

なお、URL有効期限は2019年8月3日(土)までです。

P.S
お願い

数式処理ライブラリーginacというものがあるのですが
今現在のところvc++ビルドには対応していないようです。(MinGw + msys2 or Cygwin)

これを使用すると微分・積分・式の展開などの数式処理ができるようです。
どなたかこのライブラリーを十進BASIC上で(DLLファイルを通して)利用できるようにしてもらえないでしょうか?

Linux上でクロスコンパイル(ビルド)という方法もあるようですが...

https://www.ginac.de/
http://ankokudan.org/d/dl/pdf/pdf-ginac.pdf
https://osdn.net/projects/vc-ginac-cln/releases/
 

戻る