openlang

TSV 안의 평범한 문자열 때문에 invalid command name "Dana"가 떠요, 왜죠?

하, 이거 때문에 계속 헛돌고 있어요. Arch에서 Tcl 9.0, tclsh main.tcl로 돌리고 있고, 고객사 하드웨어 자재 사이트용 작은 주문 리포트예요.

config.tcl에 규칙이 하나(total >= 200)만 있을 땐 다 잘 돌았어요. 그러다 고객 메모가 있는 주문도 표시되게 두 번째 규칙을 추가했어요:

set ::flag_rules {
    {total >= 200}
    {note ne {}}
}

이제 실행하다가 중간에 invalid command name "Dana"로 죽어요. 트레이스는 rule_match의 expr 줄을 가리켜요:

proc rule_match {row rule} {
    lassign $rule field op value
    set have [dict get $row $field]
    return [expr "\"$have\" $op \"$value\""]
}

"Dana"는 제 코드 어디에도 명령으로 없어요. 데이터에 있어요. 금액이 큰 주문 중 하나의 메모가 orders.tsv에 이렇게 들어 있어요:

1003	Petrakis Plumbing	1250.75	call [Dana] before shipping

다른 메모(예: "leave at side door")는 문제없이 통과해요. 크래시 직전에 row dict를 출력해 봤는데 완전히 정상으로 보여요. 탭도 제대로 나뉘었고, 필드 네 개 다 제자리에 있고, 메모도 그대로예요. 그래서 한 시간째 load_orders만 뒤지고 있었어요. 고객사 export에 이상한 인코딩 문제나 stray byte가 있는 줄 알았거든요. Neovim에서 :set list로 확인했는데 탭은 탭으로 보여요. (참고로 메모가 빈 행에는 끝에 탭이 붙어 있는데 1005번만 안 붙어 있는 건 왜일까요? 이건 다른 문제니까 다음에 볼게요.)

도저히 모르겠는 건 dict 안에 있는 문자열이 어떻게 명령 호출이 되느냐예요. 아무것도 eval하지 않는데요. 왜 이 행만 그럴까요?

영어에서 번역원문 보기

이어지는 대화 1

서로의 근거를 따라
  1. 고유 링크

    제 글에 이어서 씁니다. 찾았는데, 처음부터 끝까지 제 잘못이었어요.

    데이터 문제도 TSV 문제도 아니었어요. 문제는 expr "..." 줄이에요. 표현식을 큰따옴표로 만들었기 때문에 Tcl이 $have를 먼저 치환해요. 그다음에 expr이 그 결과를 파싱하는데, 표현식 안의 따옴표 문자열 속 [Dana]가 명령 치환돼 버려요. 그러니까 메모 텍스트가 코드로 평가된 거예요. "leave at side door"는 치환할 게 아무것도 없었던 거고요. (total 규칙은 1250.75가 그냥 숫자라서 한 번도 터지지 않았어요.)

    해결책은 문자열을 이어 붙이지 말고 연산자를 명령으로 쓰는 거예요:

    proc rule_match {row rule} {
        lassign $rule field op value
        return [::tcl::mathop::$op [dict get $row $field] $value]
    }

    ne와 >= 둘 다 거기 있어요. 문서: https://www.tcl-lang.org/man/tcl9.0/TclCmd/mathop.htm

    그리고 expr 페이지에 표현식을 중괄호로 묶으라고 나와 있는 데는 이유가 있어요. 그 페이지를 몇 년 전에 읽었는데, 제대로 새겨듣지 못했나 봐요. Neovim에서 stray byte를 찾느라 한 시간 쓴 게 매뉴얼 30초 읽는 것보다 앞서다니, 웃기네요.

    영어에서 번역원문 보기

같은 질문의 다른 갈래

  1. 같은 Int인데 타깃이 둘이면 답도 둘이에요

    같은 소스, 같은 파일인데 타깃만 둘이에요. JS에서는 참가자 한 명이 사라져요. 에러도 없고 경고도 없어요. 이거 정말 마음에 안 들어요. gleam run을 하면 이렇게 나와요: gleam run --target java…

    답글 0최근 활동 gleam
  2. pure 호스트 함수가 캐시되고, 인자에 쓴 값이 그냥 사라져요

    이번 주 내내 다들한테 제 토이 evaluator 레지스트리의 memo 캐시 자랑하고 다녔어요 (D, 주말 프로젝트). 규칙은 이래요. 호스트 함수가 pure면 부작용이 없을 테니까 이름이랑 출력한 인자를 키로 해서 결과를…

    답글 2최근 활동 d
  3. 같은 parseServices인데 잘못된 줄은 빌드할 때만 깨지나요?

    내장 테이블은 절대 안 바뀌니까 auto를 enum으로 바꿨는데, 맞죠? 이제 smtp 25(proto 없음)가 dub build를 깨뜨려요. 에러는 stderr writeln not usable at compile time…

    답글 2최근 활동 d