<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Claude、ＮＣデータ、Python、バイブコーディング | じじぃの引出し</title>
	<atom:link href="https://www.kazuban.com/blog/tag/claude%E3%80%81%EF%BD%8E%EF%BD%83%E3%83%87%E3%83%BC%E3%82%BF%E3%80%81python%E3%80%81%E3%83%90%E3%82%A4%E3%83%96%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.kazuban.com/blog</link>
	<description>なにかしら、皆さんの参考になれば幸いです！</description>
	<lastBuildDate>Tue, 20 Oct 2026 05:21:28 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</generator>

<image>
	<url>https://www.kazuban.com/blog/wp-content/uploads/2020/05/cropped-ICON-1-32x32.png</url>
	<title>Claude、ＮＣデータ、Python、バイブコーディング | じじぃの引出し</title>
	<link>https://www.kazuban.com/blog</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Gコード分析ライブラリ for Python・Ncコメント編【Vol.2】</title>
		<link>https://www.kazuban.com/blog/ncdata-library-nc-comment/</link>
					<comments>https://www.kazuban.com/blog/ncdata-library-nc-comment/#respond</comments>
		
		<dc:creator><![CDATA[kazuban]]></dc:creator>
		<pubDate>Tue, 20 Oct 2026 05:01:55 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<category><![CDATA[NC]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[Claude、ＮＣデータ、Python、バイブコーディング]]></category>
		<guid isPermaLink="false">https://www.kazuban.com/blog/?p=13107</guid>

					<description><![CDATA[Ncコメント編【Vol.2】 前回は、「NCワード」を「アドレスと数値・数字」に分解するコードを作成しました次は、一行の「NCブロック」を「NCワード」に分解させる予定ですが、その前に処理しておきたいのがコメントです。コ [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h1 class="wp-block-heading has-text-align-center">Ncコメント編【Vol.2】</h1>



<p class="wp-block-paragraph">前回は、「NCワード」を「アドレスと数値・数字」に分解するコードを作成しました<br>次は、一行の「NCブロック」を「NCワード」に分解させる予定ですが、その前に処理しておきたいのがコメントです。<br>コメントはファナックでは、（　～　）に挟まれた文字は、制御器は無視するのでメッセージなどに利用されますが、<br>「Gコード」や「X、Y、Z」などNCワードと同形式の文字列がコメント部に含まれていると<br>単純に文字列検索だけで処理すると、思わぬ分析ミスになりかねません<br>この記事では、純粋なコード部分とコメント部分を分離するライブラリを作成します</p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-2" checked><label class="toc-title" for="toc-checkbox-2">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"></li><li><a href="#toc1" tabindex="0">コントローラによるコメント記号の種類</a></li><li><a href="#toc2" tabindex="0">クラスライブラリ</a></li><li><a href="#toc3" tabindex="0">NCコメントプロセッサクラス</a></li><li><a href="#toc4" tabindex="0">NC分析ライブラリ、記事一覧</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">コントローラによるコメント記号の種類</span></h2>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Fanuc制御器では ( で始まり、 ) で閉じるまでをコメントと認識<br>このコメントは、<span class="marker-under">複数行を跨いでも</span> ) で閉じるまで、コメントとして扱います</p>
</blockquote>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">OSP制御器も基本的にFanucと同じですが、<span class="marker-under">( から、行末まで</span>をコメントにします<br>したがって、 ) で閉じる必要はないですが、Fanucのように、行を跨がせないのが一般的です<br>変換ソフトなどで、ファナックと共有したい場合には、確実に閉じたほうが安全です</p>
</blockquote>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Heidenhain の場合は、<span class="marker-under">; 文字から、行末まで</span>、<br><span class="marker-under">行頭に　* の場合一行すべてをコメント</span>として扱います</p>
</blockquote>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Roedersの場合には、C言語に似ていて、<br><span class="marker-under">/* ～ */の間</span>、や　<span class="marker-under">// 文字列から行末まで</span>がコメントになります</p>
</blockquote>



<p class="wp-block-paragraph">今回は、将来性も考えて、上記４点に対応した、コメント処理ライブラリを作成します</p>



<h2 class="wp-block-heading"><span id="toc2">クラスライブラリ</span></h2>



<div class="wp-block-cocoon-blocks-label-box-1 label-box block-box not-nested-style cocoon-block-label-box"><div class="label-box-label block-box-label box-label"><span class="label-box-label-text block-box-label-text box-label-text"><strong><span class="marker-blue">　<span class="fz-14px"><span class="fz-18px"><span class="fz-20px">class BlockCommentRule:</span></span></span>　</span></strong></span></div><div class="label-box-content block-box-content box-content">
<p class="wp-block-paragraph">「開始記号〜終了記号」で囲むタイプのコメント1種類分の仕様を定義<br>行コメント（&#8221;;&#8221; や &#8220;//&#8221;）はこのクラスの対象外（line_markers側で扱う）</p>



<p class="wp-block-paragraph">open_: 開始記号。例 &#8220;(&#8221; &#8220;/*&#8221;<br>close_: 終了記号。例 &#8220;)&#8221; &#8220;*/&#8221;<br>nested: 入れ子 &#8220;(&#8230;(&#8230;)&#8230;)&#8221; を許容するか。<br>multiline: 行末までに閉じなかった場合、未閉鎖の状態を次の行に持ち越すか<br>　　True &nbsp;= 複数行にまたがるコメントを許容する（例: FANUC）<br>　　False = 必ずその行の中で強制的に閉じる（例: OSPは自動で閉じるがデータでの処理が安全）</p>



<pre class="wp-block-preformatted has-black-color has-watery-red-background-color has-text-color has-background"># test code<br>bcr = BlockCommentRule("/*", "*/")<br>open = bcr.open<br>close = bcr.close<br>print(f"コメント始まり文字={open} コメント終了文字={close}")</pre>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"> /* */</p>
</blockquote>
</div></div>



<div class="wp-block-cocoon-blocks-label-box-1 label-box block-box not-nested-style cocoon-block-label-box"><div class="label-box-label block-box-label box-label"><span class="label-box-label-text block-box-label-text box-label-text"><strong><span class="marker-blue">　<span class="fz-14px"><span class="fz-18px"><span class="fz-20px">class DialectSpec(TypedDict):</span></span></span>　</span></strong></span></div><div class="label-box-content block-box-content box-content">
<p class="wp-block-paragraph"><code>TypedDict</code> は 実態はただの辞書（dict）であるが、dictに型情報を付与するための仕組み。<br>dictは値の型が曖昧だが、このクラスで辞書の型を決定し型情報が違う場合、<br>静的型チェッカーが教えてくれる。</p>



<pre class="wp-block-preformatted has-black-color has-watery-red-background-color has-text-color has-background">#このクラスでコメントのタイプの型形式を定義
class DialectSpec(TypedDict):
&nbsp; &nbsp; blocks: List[BlockCommentRule] # 囲むタイプ [("(",")") , ("/*","*/")]など
&nbsp; &nbsp; line_markers: List[str]        # 行末までのタイプ [ ";" , "//" ]など
&nbsp; &nbsp; line_start_markers: List[str]  # 必ず先頭にあるタイプ["*"]など</pre>
</div></div>



<div class="wp-block-cocoon-blocks-label-box-1 label-box block-box not-nested-style cocoon-block-label-box"><div class="label-box-label block-box-label box-label"><span class="label-box-label-text block-box-label-text box-label-text"><strong><span class="marker-blue">　<span class="fz-14px"><span class="fz-18px"><span class="fz-20px">DIALECTS: Dict[str, DialectSpec]</span></span></span>　</span></strong></span></div><div class="label-box-content block-box-content box-content">
<p class="wp-block-paragraph">制御器名とコメント記法（囲みコメントか単独か）の違いをまとめて定義<br>DIALECTS[&#8220;fanuc&#8221;][&#8220;blocks&#8221;][0].open で、<br>&#8220;fanuc&#8221;の&#8221;blicks&#8221;コメントの始まり文字(open)を参照できる</p>



<pre class="wp-block-preformatted has-black-color has-watery-red-background-color has-text-color has-background"># test code
a = DIALECTS["osp"]["blocks"][0].open
b = DIALECTS["osp"]["blocks"][0].close
c = DIALECTS["osp"]["blocks"][0].nested
d = DIALECTS["osp"]["blocks"][0].multiline
print(f"open={a} close={b} nested={c} multiline={d}") </pre>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">「実行結果」<br>open=( close=) nested=True multiline=False</p>
</blockquote>
</div></div>



<div class="wp-block-cocoon-blocks-label-box-1 label-box block-box not-nested-style cocoon-block-label-box"><div class="label-box-label block-box-label box-label"><span class="label-box-label-text block-box-label-text box-label-text"><strong><span class="marker-blue">　<span class="fz-14px"><span class="fz-18px"><span class="fz-20px">class NcCommentProcessor:</span></span></span>　</span></strong></span></div><div class="label-box-content block-box-content box-content">
<p class="wp-block-paragraph">NCブロックを純粋コードとコメントに分解する</p>



<div class="wp-block-cocoon-blocks-label-box-1 label-box block-box not-nested-style cocoon-block-label-box"><div class="label-box-label block-box-label box-label"><span class="label-box-label-text block-box-label-text box-label-text"><strong><span class="marker-blue"><span class="marker">　<span class="fz-14px"><span class="fz-18px"><span class="fz-20px"><span class="fz-16px">def  _init__</span></span></span></span></span></span></strong><span class="marker">(self, dialect: Union[str, Sequence[str]] = &#8220;fanuc&#8221;):</span><strong><span class="marker-blue"><span class="marker">　</span></span></strong></span></div><div class="label-box-content block-box-content box-content">
<p class="wp-block-paragraph">引数：dialectに制御器情報を渡すが、複数定義にも対応している<br> dialect=&#8221;fanuc&#8221;（文字列1個）と dialect=[&#8220;fanuc&#8221;,&#8221;heidenhain&#8221;]（リスト）の両方に対応<br>また、省略定義にも対応<br>&#8220;F&#8221;→&#8221;fanuc&#8221;  &#8220;O&#8221;→&#8221;osp&#8221;  &#8220;H&#8221;→&#8221;heidenhain&#8221;  &#8220;R&#8221;→&#8221;roeders&#8221; </p>
</div></div>



<div class="wp-block-cocoon-blocks-label-box-1 label-box block-box not-nested-style cocoon-block-label-box"><div class="label-box-label block-box-label box-label"><span class="label-box-label-text block-box-label-text box-label-text"><strong><span class="marker-blue"><span class="marker">　</span></span></strong><span class="marker"> @property</span><br>    <span class="marker">def  open_comment(self) -> str:</span><strong><span class="marker-blue"><span class="marker">　</span></span></strong></span></div><div class="label-box-content block-box-content box-content">
<p class="wp-block-paragraph">コメントの始まり記号を返す（@propertyなので、変数のように使える)</p>
</div></div>



<div class="wp-block-cocoon-blocks-label-box-1 label-box block-box not-nested-style cocoon-block-label-box"><div class="label-box-label block-box-label box-label"><span class="label-box-label-text block-box-label-text box-label-text"><strong><span class="marker-blue"><span class="marker">　</span></span></strong><span class="marker">def reset(self) -> None:</span><strong><span class="marker-blue"><span class="marker">　</span></span></strong></span></div><div class="label-box-content block-box-content box-content">
<p class="wp-block-paragraph">コメントの引き継ぎ状態をリセットする</p>
</div></div>



<div class="wp-block-cocoon-blocks-label-box-1 label-box block-box not-nested-style cocoon-block-label-box"><div class="label-box-label block-box-label box-label"><span class="label-box-label-text block-box-label-text box-label-text"><strong><span class="marker-blue"><span class="marker">　</span></span></strong><span class="marker">def  _scan(self, line: str)-> List[Tuple[str, str]]:</span><strong><span class="marker-blue"><span class="marker">　</span></span></strong></span></div><div class="label-box-content block-box-content box-content">
<p class="wp-block-paragraph">一行のNC文字列を、NCコードとコメントに分解する</p>
</div></div>



<div class="wp-block-cocoon-blocks-label-box-1 label-box block-box not-nested-style cocoon-block-label-box"><div class="label-box-label block-box-label box-label"><span class="label-box-label-text block-box-label-text box-label-text"><strong><span class="marker-blue"><span class="marker">　</span></span></strong><span class="marker">reset_open_block(self):</span><strong><span class="marker-blue"><span class="marker">　</span></span></strong></span></div><div class="label-box-content block-box-content box-content">
<p class="wp-block-paragraph">別のNCデータを連続で実施する場合、コメントを確実に閉じる</p>
</div></div>



<div class="wp-block-cocoon-blocks-label-box-1 label-box block-box not-nested-style cocoon-block-label-box"><div class="label-box-label block-box-label box-label"><span class="label-box-label-text block-box-label-text box-label-text"><strong><span class="marker-blue"><span class="marker">　</span></span></strong><span class="marker">def  parse_code_comment(self, line: str)-> Tuple[str, str]:</span><strong><span class="marker-blue"><span class="marker">　</span></span></strong></span></div><div class="label-box-content block-box-content box-content">
<p class="wp-block-paragraph">一行NC文字列をNCコードとコメントに分解し、それぞれtupleで返す</p>
</div></div>



<pre class="wp-block-preformatted has-black-color has-watery-red-background-color has-text-color has-background"># test code<br>ncblock = [<br>    "G01 X10 Y20 (this is a comment) F100(next",<br>    "next)G02 X30(unterminated comment ;starts here",<br>]<br>processor = NcCommentProcessor(dialect="fanuc")<br><br>for line in ncblock:<br>    print(f"Original Nc={line!r}")<br>    code, comment = processor.parse_code_comment(line)<br>    print(f"parse_code_comment: code={code!r}, comment={comment!r}")<br>    print(f"-------------------------------")</pre>



<pre class="wp-block-preformatted">「実行結果」<br>Original Nc='G01 X10 Y20 (this is a comment) F100(next'<br>parse_code_comment: code='G01 X10 Y20  F100', comment='(this is a comment)(next)'<br>-------------------------------<br>Original Nc='next)G02 X30(unterminated comment ;starts here'<br>parse_code_comment: code='G02 X30', comment='(next)(unterminated comment ;starts here)'<br>-------------------------------</pre>



<p class="wp-block-paragraph"></p>
</div></div>



<h2 class="wp-block-heading"><span id="toc3">NCコメントプロセッサクラス</span></h2>



<p class="wp-block-paragraph">実際の、コードです<br>ただし、コメント行が跨る場合で、意図しない分割になる可能性もあります<br>NCのコメントは、確実にその行で閉じる処理をすることが安全です</p>



<pre class="wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-1"><code>from typing import Dict, List, Optional, Sequence, Tuple, TypedDict, Union


class BlockCommentRule:
    """
    「開始記号〜終了記号」で囲むタイプのコメント1種類分の仕様。
    例: FANUCの "(" ")" や Roedersの "/*" "*/" など、開始と終了のペアで
    構成されるコメント記法を表す。開始記号だけ・終了記号だけの
    行コメント（";" や "//"）はこのクラスの対象外（line_markers側で扱う）。
    """

    __slots__ = ("open", "close", "nested", "multiline")
    """ __slots__ を書くと、このクラスのインスタンスは
    「ここに列挙した名前の属性しか持てない」という制限がかかる"""

    def __init__(
        self, open_: str, close_: str, nested: bool = False, multiline: bool = False
    ):
        """
        Args:
            open_: 開始記号。例 "(" "/*"
            close_: 終了記号。例 ")" "*/"
            nested: 入れ子 "(...(...)...)" を許容するか。
                True なら開始記号が現れるたびにネスト深さを1つ増やし、
                対応する終了記号がすべて出そろうまで閉じたとみなさない。
            multiline: 行末までに閉じなかった場合、未閉鎖の状態を
                次の行に持ち越すか。
                True  = 複数行にまたがるコメントを許容する（例: FANUC）
                False = 必ずその行の中で強制的に閉じる（例: OSP）
        """
        self.open = open_
        self.close = close_
        self.nested = nested  # 入れ子 "(...(...)...)" を許容するか
        self.multiline = multiline  # 閉じないまま行末を迎えたとき、次行に持ち越すか


class DialectSpec(TypedDict):
    """
    1つの制御装置（機種）が持つコメント記法一式を表す型。
    DIALECTS 辞書の値の型として使う。全dialectで3つのキーを
    必ず揃えること（使わないものは空リスト &#91;] を入れる）。
    """

    blocks: List&#91;BlockCommentRule]
    """開始〜終了で囲むタイプのコメント規則の一覧。例: &#91;("(",")"), ("/*","*/")]"""

    line_markers: List&#91;str]
    """行の"どこにあっても"、そこから行末まで丸ごとコメントになる記号。例: ";" "//" """

    line_start_markers: List&#91;str]
    """行の"先頭にある場合のみ"、そこから行末まで丸ごとコメントになる記号。
    例: HEIDENHAINの "*"（行の途中では掛け算として使われるため区別が必要）"""


# 機種(dialect)ごとのコメント記法定義
# ※ キー構成(blocks / line_markers / line_start_markers)は全エントリで必ず揃える
#   （使わないものは空リストを明示する）。バラバラだと型チェッカーが辞書の形を
#   一つに決められず、DIALECTS&#91;name]&#91;"..."] のようなアクセスで警告が出る。
DIALECTS: Dict&#91;str, DialectSpec] = {
    # FANUC: ( ) 入れ子OK、複数行にまたがる未閉鎖を許容
    "fanuc": {
        "blocks": &#91;BlockCommentRule("(", ")", nested=True, multiline=True)],
        "line_markers": &#91;],
        "line_start_markers": &#91;],
    },
    # OSP: ( ) 入れ子OK、ただし必ず1行内で強制的に閉じる
    #　通常は複数行にはまたがらないが、fanucとの互換性のため、multiline=Trueにする
    "osp": {
        "blocks": &#91;BlockCommentRule("(", ")", nested=True, multiline=True)],
        "line_markers": &#91;],
        "line_start_markers": &#91;],
    },
    # HEIDENHAIN: ; はどこにあっても行末までコメント。
    # * は「掛け算」としても使われるため、行頭にある場合のみ
    # アウトライン的な見出しコメントとして扱う（行の途中の * はコード）。
    "heidenhain": {
        "blocks": &#91;],
        "line_markers": &#91;";"],
        "line_start_markers": &#91;"*"],
    },
    # Roeders(独自制御言語): /* */ の囲み(複数行可) または // で行末までコメント
    # ※ 囲みコメント内での単文字コメント(; など)の扱いは未確認のため、
    #    ひとまず「囲み内は閉じタグ(*/)まで不透明」という一般的な仕様を仮定しています。
    #    要調査・要確認ポイント。
    "roeders": {
        "blocks": &#91;BlockCommentRule("/*", "*/", nested=False, multiline=True)],
        "line_markers": &#91;"//"],
        "line_start_markers": &#91;],
    },
}


class NcCommentProcessor:
    """
    NCブロックを純粋コードとコメントに分解する（機種ごとのコメント記法に対応）

    processor = NcCommentProcessor(dialect="fanuc")
    for line in ncs:
        code, comment = processor.parse_code_comment(line)
        print(f"code={code},comment={comment}")

    dialect:
        "F" or "fanuc"      : ( ) 入れ子OK、複数行にまたがる未閉鎖を許容
        "O" or "osp"        : ( ) 入れ子OK、必ず1行内で強制的に閉じる（次行に持ち越さない）
        "H" or "heidenhain" : ; はどこでも行末までコメント。* は行頭にある場合のみ行末までコメント
        "R" or "roeders"    : /* */ の囲み(複数行可) または // で行末までコメント

    複数dialectをまとめて使いたい場合はリストで渡す（ルールを合成する）:
        NcCommentProcessor(dialect=&#91;"fanuc", "heidenhain", "roeders"])
    リスト内で同じ開始記号（例: "(" ）を複数dialectが定義していた場合は、
    リストで先に書いたdialectのルールが優先される。
    """

    def __init__(self, dialect: Union&#91;str, Sequence&#91;str]] = "fanuc"):
        # isinstance:指定したデータ型（クラス）であるかどうか
        names = &#91;dialect] if isinstance(dialect, str) else list(dialect)
        mapping = {"F": "fanuc", "O": "osp", "H": "heidenhain", "R": "roeders"}
        for n, i in enumerate(names, 0):
            names&#91;n] = mapping.get(i, i)

        unknown = &#91;d for d in names if d not in DIALECTS]
        if unknown:
            raise ValueError(
                f"未知のdialectです: {unknown!r} (選べるのは {list(DIALECTS)})"
            )
        self.dialect = names&#91;0] if len(names) == 1 else names

        # 複数dialect分のルールを合成する。開始記号(open)が重複するものは
        # 先に書いたdialectのルールを優先し、後発の重複ルールは捨てる。
        blocks: List&#91;BlockCommentRule] = &#91;]
        seen_open = set()
        for name in names:
            for rule in DIALECTS&#91;name]&#91;"blocks"]:
                if rule.open in seen_open:
                    continue
                seen_open.add(rule.open)
                blocks.append(rule)

        line_markers: List&#91;str] = &#91;]
        for name in names:
            for marker in DIALECTS&#91;name]&#91;"line_markers"]:
                if marker not in line_markers:
                    line_markers.append(marker)
        # 長いマーカーを先に判定できるよう、長さ降順に並べる
        # （例: "/*" と "*" が両方あるとき、"*" だけで先に食われないように）
        line_markers.sort(key=len, reverse=True)

        line_start_markers: List&#91;str] = &#91;]
        for name in names:
            for marker in DIALECTS&#91;name].get("line_start_markers", &#91;]):
            #for marker in DIALECTS&#91;name]&#91;"line_start_markers"]:
                if marker not in line_start_markers:
                    line_start_markers.append(marker)
        line_start_markers.sort(key=len, reverse=True)

        self._blocks: List&#91;BlockCommentRule] = blocks
        self._line_markers: List&#91;str] = line_markers
        self._line_start_markers: List&#91;str] = line_start_markers
        # 行をまたぐ未閉鎖ブロックの状態: (rule, 現在の未閉鎖の深さ)
        self._open_block: Optional&#91;Tuple&#91;BlockCommentRule, int]] = None

    @property
    def open_comment(self) -> str:
        return self._open_block&#91;0].open if self._open_block else ""

    def reset(self) -> None:
        self._open_block = None

    def _scan(self, line: str) -> List&#91;Tuple&#91;str, str]]:
        """1行を (種別, 文字列) のトークン列に分解する。種別は 'code' か 'comment'。"""
        text = line.rstrip()
        n = len(text)
        tokens: List&#91;Tuple&#91;str, str]] = &#91;]
        i = 0

        # 前の行から続く未閉鎖ブロックコメントの続きを最優先で処理する
        # （ここを最初に処理することで、囲みの中の ; や // を誤って行コメント開始と
        #   判定してしまう不具合を避けている）
        if self._open_block is not None:
            rule, depth = self._open_block
            j = 0
            while j &lt; n and depth > 0:
                if rule.nested and text.startswith(rule.open, j):
                    depth += 1
                    j += len(rule.open)
                    continue
                if text.startswith(rule.close, j):
                    depth -= 1
                    j += len(rule.close)
                    continue
                j += 1
            if depth > 0:
                # まだ閉じない: 開始記号を補って「継続中のコメント」と分かるようにし、
                # 残っている深さ(depth)を次行にそのまま引き継ぐ
                tokens.append(("comment", rule.open + text + rule.close * depth))
                self._open_block = (rule, depth)
                return tokens
            tokens.append(("comment", rule.open + text&#91;:j]))
            self._open_block = None
            i = j

        while i &lt; n:
            matched = False

            # 行頭専用のマーカー（例: HEIDENHAINの見出し的な "*"）は
            # 行の一番最初の位置でのみコメット開始とみなす
            if i == 0:
                for marker in self._line_start_markers:
                    if text.startswith(marker, i):
                        tokens.append(("comment", text&#91;i:n]))
                        i = n
                        matched = True
                        break
                if matched:
                    continue

            for rule in self._blocks:
                if text.startswith(rule.open, i):
                    depth = 1
                    j = i + len(rule.open)
                    while j &lt; n and depth > 0:
                        if rule.nested and text.startswith(rule.open, j):
                            depth += 1
                            j += len(rule.open)
                            continue
                        if text.startswith(rule.close, j):
                            depth -= 1
                            j += len(rule.close)
                            continue
                        j += 1
                    if depth > 0:
                        tokens.append(("comment", text&#91;i:n] + rule.close * depth))
                        if rule.multiline:
                            self._open_block = (rule, depth)
                        i = n
                    else:
                        tokens.append(("comment", text&#91;i:j]))
                        i = j
                    matched = True
                    break

            if matched:
                continue

            for marker in self._line_markers:
                if text.startswith(marker, i):
                    tokens.append(("comment", text&#91;i:n]))
                    i = n
                    matched = True
                    break

            if matched:
                continue

            if tokens and tokens&#91;-1]&#91;0] == "code":
                tokens&#91;-1] = ("code", tokens&#91;-1]&#91;1] + text&#91;i])
            else:
                tokens.append(("code", text&#91;i]))
            i += 1

        return tokens


    def reset_open_block(self):
        """別のNCデータを連続で実施する場合、コメントを確実に閉じる"""
        block = self._open_block
        if block is not None:
            self.reset()

    def parse_code_comment(self, line: str) -> Tuple&#91;str, str]:
        """純粋コードとコメントを分割する"""
        tokens = self._scan(line)
        code = "".join(t for k, t in tokens if k == "code").rstrip()
        comment = "".join(t for k, t in tokens if k == "comment")
        return code, comment


# test code
if __name__ == "__main__":
    ncblock = &#91;
        "G01 X10 Y20 (this is a comment) F100(next",
        "next)G02 X30(unterminated comment ;starts here",
    ]
    processor = NcCommentProcessor(dialect="fanuc")

    for line in ncblock:
        print(f"Original Nc={line!r}")
        # code = processor.normalize(line)
        # print(f"normalize: line={code!r}")
        code, comment = processor.parse_code_comment(line)
        print(f"parse_code_comment: code={code!r}, comment={comment!r}")
        print(f"-------------------------------")
    processor.reset_open_block()
    for line in ncblock:
        print(f"Original Nc={line!r}")
        # code = processor.normalize(line)
        # print(f"normalize: line={code!r}")
        code, comment = processor.parse_code_comment(line)
        print(f"parse_code_comment: code={code!r}, comment={comment!r}")
        print(f"-------------------------------")
</code></pre>



<h2 class="wp-block-heading"><span id="toc4">NC分析ライブラリ、記事一覧</span></h2>



<div class="wp-block-columns has-watery-red-background-color has-background is-style-bottom-margin-1em has-bottom-margin is-layout-flex wp-container-core-columns-is-layout-cb1de518 wp-block-columns-is-layout-flex" style="border-style:none;border-width:0px;border-radius:0px">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:15%">
<figure class="wp-block-image size-full is-resized"><img fetchpriority="high" decoding="async" width="1376" height="768" src="https://www.kazuban.com/blog/wp-content/uploads/2026/09/ncdata-library-nc-word.jpg" alt="" class="wp-image-13070" style="object-fit:cover;width:100px;height:70px" srcset="https://www.kazuban.com/blog/wp-content/uploads/2026/09/ncdata-library-nc-word.jpg 1376w, https://www.kazuban.com/blog/wp-content/uploads/2026/09/ncdata-library-nc-word-300x167.jpg 300w, https://www.kazuban.com/blog/wp-content/uploads/2026/09/ncdata-library-nc-word-500x279.jpg 500w, https://www.kazuban.com/blog/wp-content/uploads/2026/09/ncdata-library-nc-word-767x428.jpg 767w, https://www.kazuban.com/blog/wp-content/uploads/2026/09/ncdata-library-nc-word-799x446.jpg 799w, https://www.kazuban.com/blog/wp-content/uploads/2026/09/ncdata-library-nc-word-240x135.jpg 240w, https://www.kazuban.com/blog/wp-content/uploads/2026/09/ncdata-library-nc-word-320x180.jpg 320w" sizes="(max-width: 1376px) 100vw, 1376px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p class="wp-block-paragraph"><a href="https://www.kazuban.com/blog/ncdata-library-nc-word/" target="_blank">NCワード</a></p>
</div>
</div>



<div class="wp-block-columns has-watery-green-background-color has-background is-style-bottom-margin-1em has-bottom-margin is-layout-flex wp-container-core-columns-is-layout-cb1de518 wp-block-columns-is-layout-flex" style="border-style:none;border-width:0px;border-radius:0px">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:15%">
<figure class="wp-block-image size-full is-resized"><img decoding="async" width="1376" height="768" src="https://www.kazuban.com/blog/wp-content/uploads/2026/09/Gemini_Generated_Image_ud5k15ud5k15ud5k.jpg" alt="" class="wp-image-13104" style="object-fit:cover;width:100px;height:70px" srcset="https://www.kazuban.com/blog/wp-content/uploads/2026/09/Gemini_Generated_Image_ud5k15ud5k15ud5k.jpg 1376w, https://www.kazuban.com/blog/wp-content/uploads/2026/09/Gemini_Generated_Image_ud5k15ud5k15ud5k-500x279.jpg 500w, https://www.kazuban.com/blog/wp-content/uploads/2026/09/Gemini_Generated_Image_ud5k15ud5k15ud5k-800x447.jpg 800w, https://www.kazuban.com/blog/wp-content/uploads/2026/09/Gemini_Generated_Image_ud5k15ud5k15ud5k-300x167.jpg 300w, https://www.kazuban.com/blog/wp-content/uploads/2026/09/Gemini_Generated_Image_ud5k15ud5k15ud5k-768x429.jpg 768w, https://www.kazuban.com/blog/wp-content/uploads/2026/09/Gemini_Generated_Image_ud5k15ud5k15ud5k-240x135.jpg 240w, https://www.kazuban.com/blog/wp-content/uploads/2026/09/Gemini_Generated_Image_ud5k15ud5k15ud5k-320x180.jpg 320w" sizes="(max-width: 1376px) 100vw, 1376px" /></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p class="wp-block-paragraph"><a href="https://www.kazuban.com/blog/ncdata-library-nc-comment/">NCコメント</a></p>
</div>
</div>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.kazuban.com/blog/ncdata-library-nc-comment/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
