[Top Page][Edit][Discussion][Edit History][All Pages][Recent Changes][->Japanese]

数式の文字列を計算するモジュール(一度逆ポーランド記法に変換)

スクリーンショット

[image]

calc.hsp

#module c
        // 演算子の優先順序
        // 低い
        #enum UNKNOWN = -1
        #enum PAREN   // 括弧は例外
        #enum XOR_
        #enum OR_
        #enum AND_
        #enum LESS
        #enum GRTER
        #enum EQUAL
        #enum ADD
        #enum SUB
        #enum MULT
        #enum DIV
        #enum POW
        // 高い

        #define stack(%1,%2) sdim %1,20,%2:%1_i=0
        #define push(%1,%2) %1(%1_i)=%2:%1_i++
        #define ctype pop(%1) _pop@c(%1(%1_i-1),%1_i)
        #defcfunc local _pop var val, var index
                index--
        return val
        
        #define logstack(%1) _tmp="%1:":repeat %1_i:_tmp+=%1(cnt)+",":loop:logmes _tmp

        // 計算を行う
        #defcfunc calc str f

                // 逆ポーランド記法でスタックpriに計算式を積む
                import f
        
                // 浮動小数点数で計算するかどうか
                is_double = 0

                repeat pri_i
                        token = pri(cnt)
        
                        // 小数点が含まれていればそれ以降浮動小数点として扱う
                        is_double |= (instr(token, 1, ".") != -1)
        
                        if ( to_ope(token) == UNKNOWN ) { // 数値なら
                                push sec, token
                        } else { // 演算子なら
        
                                // 左辺、右辺を取得
                                right = pop(sec)
                                left  = pop(sec)
        
                                // 文字列から変換
                                if (is_double) {
                                        right = double(right)
                                        left  = double(left)
                                } else {
                                        right = int(right)
                                        left  = int(left)
                                }
        
                                // 計算処理
                                switch(to_ope(token))
                                        case XOR_
                                                push sec, str(int(left) ^ int(right))
                                                swbreak
                                        case OR_
                                                push sec, str(int(left) | int(right))
                                                swbreak
                                        case AND_
                                                push sec, str(int(left) & int(right))
                                                swbreak
                                        case LESS
                                                push sec, str(left < right)
                                                swbreak
                                        case GRTER
                                                push sec, str(left > right)
                                                swbreak
                                        case EQUAL
                                                push sec, str(left == right)
                                                swbreak
                                        case ADD
                                                push sec, str(left + right)
                                                swbreak
                                        case SUB
                                                push sec, str(left - right)
                                                swbreak
                                        case MULT
                                                push sec, str(left * right)
                                                swbreak
                                        case DIV
                                                is_double = 1
                                                push sec, str(double(left) / double(right))
                                                swbreak
                                        case POW
                                                push sec, str(powf(left, right))
                                                swbreak
                                swend
                        }
                loop

                if (is_double) {
                        return double(sec(0))
                } else {
                        return int(sec(0))
                }

        return

        #deffunc local import str f
        
                stack pri, 100
                stack sec, 50
        
                target  = f
        
                val_tmp = ""
        
                is_val  = 0 // トークンが数値であるかどうか
        
                // 文字列の解析
                repeat strlen(target)
                        token = strmid(target,cnt,1)

                        // 数字の場合
                        token_c = peek(token, 0)
                        if ('0' <= token_c && token_c <= '9' || token_c == '.') {
                                val_tmp += token // val_tmpに数値リテラルを作成する
                                
                                is_val = 1
                                continue
                        }
        
                        // 数字と演算子の境で、val_tmpを積む
                        if (is_val) {
                                push pri, val_tmp
                                val_tmp = ""
                        }
        
                        is_val = 0 // 演算子
        
                        // 括弧は積むだけ
                        if (token == "(") {
                                push sec, token
                                continue
                        }
        
                        // 対応する括弧までの演算子を移動する
                        if (token == ")") {
                                while(sec(sec_i-1) != "(")
                                        push pri, pop(sec)
                                wend
                                sec_i--
                                continue
                        }
        
                        // 演算子の優先順序の処理
                        if (sec_i > 0) {
                                if (to_ope(sec(sec_i-1)) > to_ope(token)) {
                                        repeat sec_i
                                                if (sec(sec_i-1) == "(") :break
                                                push pri, pop(sec)
                                        loop
                                }
                        }
        
                        // 積む
                        push sec, token
                loop
        
                // 数値リテラルが余っていれば積んどく
                if (is_val) {
                        push pri, val_tmp
                }
        
                // 残りの演算子を移動する
                repeat sec_i
                        push pri, pop(sec)
                loop

        return

        #defcfunc local to_ope str ope
                // 演算子の判定
                switch(ope)
                        case "~"
                                return XOR_
                        case "|"
                                return OR_
                        case "&"
                                return AND_
                        case "<"
                                return LESS
                        case ">"
                                return GRTER
                        case "="
                                return EQUAL
                        case "+"
                                return ADD
                        case "-"
                                return SUB
                        case "*"
                                return MULT
                        case "/"
                                return DIV
                        case "^"
                                return POW
                        case "("
                        case ")"
                                return PAREN
                                swbreak
                swend
        return UNKNOWN

#global

sample.hsp

pi = calc("4*(1-1/3+1/5-1/7+1/9-1/11+1/13-1/15+1/17-1/19+1/21-1/23+1/25)")
mes "PI="+pi

mes

e = calc("1+1/1+1/(1*2)+1/(1*2*3)+1/(1*2*3*4)+1/(1*2*3*4)+1/(1*2*3*4*5)")
mes "E="+e

mes

cos1 = calc("1-1/(1*2)+1/(1*2*3*4)-1/(1*2*3*4*5*6)+1/(1*2*3*4*5*6*7*8)")
mes "COS(1)="+cos1
sin1 = calc("1-1/(1*2*3)+1/(1*2*3*4*5)-1/(1*2*3*4*5*6*7)+1/(1*2*3*4*5*6*7*8*9)")
mes "SIN(1)="+sin1

mes "COS^2(1)+SIN^2(1)="+calc(strf("%f^2+%f^2", cos1, sin1))

mes
mes "2^2^2^2="+calc("2^2^2^2")

mes
mes "(2^4=4^2)&(5^3<3^5)="+calc("(2^4=4^2)&(5^3<3^5)")

mes
mes "(1~(1&0))=((1~1)|(1~0))="+calc("(1~(1&0))=((1~1)|(1~0))")