2007年5月12日土曜日

インタプリンタ電卓もどき

文字列で四則演算を行うプログラム。
動作はするが、ロジックはあまりきれいではない。時間をおいて作り直したい。

// コマンドライン電卓
#runtime "hsp3cl"
#module
// 文字列置換命令
//   v1 : 置換する文字型変数
//   s1 : 置換する文字列
//   i1 : 開始インデックス
//   i2 : 消去する文字列の数
#deffunc replace var v1, str s1, int i1, int i2, local s
    sdim s, 20
    if i1 : memcpy s, v1, i1, 00
    s += s1
    memcpy s, v1, strlen(v1) - i1 - i2, i1 + strlen(s1), i1 + i2
    v1 = s
    return

#deffunc doCalc var s1, int i1, local target, local right, local r, local left, local l, local type, local i
    target = s1
    type = peek(target, i1)
    result = ""
    left = 0
    right = 0
    l = 0
    r = 0
    repeat i1
        i = peek(target, i1 - cnt - 1) - '0'
        if (0 <= i)&(i <= 9) {
            repeat cnt
                i *= 10
            loop
            left += i
            l++
        } else {
            break
        }
    loop
    repeat strlen(target) - i1 - 1
        i = peek(target, i1 + cnt + 1) - '0'
        if (0 <= i)&(i <= 9) {
            r++
            right = right * 10 + i
        } else {
            break
        }
    loop

    if type != '-' {
        if (l == 0)|(r == 0) {
            error = "数式が不正です"
            return 0
        }
    }
    switch type
    case '+'
        result = str(left + right)
        swbreak
    case '-'
        if r == 0 {
            error = "数式が不正です"
            return 0
        }
        if l == 0 {
            result = "noExchange"
        } else {
            result = str(left - right)
        }
        swbreak
    case '*'
        result = str(left * right)
        swbreak
    case '/'
        if right {
            result = str(left / right)
        } else {
            error = "ゼロでは除算できません"
        }
        swbreak
    default
        error = "数式が不正です"
        swbreak
    swend
    if error != "" : return 0
    if result == "noExchange" : return 1
    replace s1, result, i1 - l, l+1+r
    return 0

// 内部で再帰的に用いる命令
// 括弧を判別して括弧内を対象に自らを呼び出す
#defcfunc subCalc str s1, local cmd, local i, local l, local s
    cmd = s1
    repeat
        i = instr(cmd, 0"(")
        if i >= 0 {
            l = instr(cmd, i+1")")
            s = strmid(cmd, i + 1, l)
            replace cmd, subCalc(s), i, l + 2 // ( と ) の分で+2
        } else {
            break
        }
    loop
    // 括弧がない場合
    // *, /を計算
    repeat
        i = instr(cmd, 0"*")
        l = instr(cmd, 0"/")
        if (i == -1)&(l == -1) : break
        if (i == -1)&(0 <= l) : i = l
        if 0 <= i {
            if (0 <= l)&(l < i) : i = l            // i にはiとlのうち小さい方が格納される
        }
        doCalc cmd, i       // インデックスiにある演算子で演算を行う
        if error != "" : break
    loop

    if error != "" : return error

    // +, -を計算
    k = 0
    repeat
        i = instr(cmd, k, "+")
        l = instr(cmd, k, "-")
        if (i == -1)&(l == -1) : break
        if (i == -1) : i = l
        if (0 <= l)&(0 <= i)&(l < i) : i = l
        i += k
        doCalc cmd, i       // インデックスiにある演算子で演算を行う
        if stat : k = i + 1
        if error != "" : break
    loop
    if error != "" : return error
    return cmd

// 外部から呼び出す命令 整数の数式を文字列として渡す
#deffunc calc str s1
    error = ""
    // 括弧の個数を調べる
    lt = 0 : gt = 0
    cmd = s1
    repeat strlen(cmd)
        tmp = peek(cmd, cnt)
        if tmp == '(' : lt++
        if tmp == ')' : gt++
    loop
    if lt != gt : error = "正しい数式ではありません"
    if error != "" : return error

    mes "Q:"+cmd
    return subCalc(s1)
#global

    calc "10*10+8/2*3"
    mes "  = " + refstr

0 件のコメント: