summaryrefslogtreecommitdiff
path: root/src/mml_composemusic_mcp/ast_nodes.py
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-07-12 17:58:19 +0900
committertanaka takashi <isazaka@gmail.com>2026-07-12 17:59:16 +0900
commitbf36a773918959eeb6ffe7dae2fb8b0874438eb4 (patch)
treeda7ff0037ea923fea255d2ed69ed2fb51395d9b9 /src/mml_composemusic_mcp/ast_nodes.py
parentf1cd4d7d16f6e75a50bb0c587c0f759f7cf710cd (diff)
feat(ppmck): mckc準拠の構文拡張とIR保持コマンド追加
- @をデューティ比、qをクオンタイズに修正(mckc原本準拠) - ^タイ、Dディチューン、sスイープ、v+/v-相対音量を追加 - @v/@@/@MP/@EP/@EN エンベロープ・LFO系を構文解析してIR保持 - 上記IR保持コマンドは合成未対応を SEMANTIC_UNSUPPORTED_FEATURE で警告 - Design.md / MML_BNF.md を更新 - 全200テスト + ruff チェック通過
Diffstat (limited to 'src/mml_composemusic_mcp/ast_nodes.py')
-rw-r--r--src/mml_composemusic_mcp/ast_nodes.py159
1 files changed, 150 insertions, 9 deletions
diff --git a/src/mml_composemusic_mcp/ast_nodes.py b/src/mml_composemusic_mcp/ast_nodes.py
index bc4fdc4..3341510 100644
--- a/src/mml_composemusic_mcp/ast_nodes.py
+++ b/src/mml_composemusic_mcp/ast_nodes.py
@@ -118,6 +118,13 @@ class DetuneStmt(ASTNode):
@dataclass
+class QuantizeStmt(ASTNode):
+ """ppmck quantize command (q1-q8)."""
+
+ value: int
+
+
+@dataclass
class TieStmt(ASTNode):
"""Tie/slur command (&)."""
@@ -125,6 +132,27 @@ class TieStmt(ASTNode):
@dataclass
+class TieCmdStmt(ASTNode):
+ """ppmck tie command (^). Extends duration, pitch from previous note."""
+
+ target: NoteStmt | int | None = None
+
+
+@dataclass
+class DetuneCmdStmt(ASTNode):
+ """ppmck detune command (D<num>)."""
+
+ value: int
+
+
+@dataclass
+class RelativeVolumeStmt(ASTNode):
+ """ppmck relative volume (v+num / v-num)."""
+
+ delta: int # positive=up, negative=down
+
+
+@dataclass
class RepeatStartStmt(ASTNode):
"""Start of repeat section ([)."""
@@ -154,21 +182,92 @@ class ExtCmdStmt(ASTNode):
params: list[int] = field(default_factory=list)
-# --- Stage 2 statements (kept in AST but ignored by semantic analyzer) ---
+# --- Envelope / LFO / vibrato statements (IR kept, synthesis warning) ---
@dataclass
-class CountLengthStmt(ASTNode):
- """ppmck count length note (c%48)."""
+class VolumeEnvelopeUseStmt(ASTNode):
+ """ppmck volume envelope use (@v<num>)."""
- note_name: str
- accidental: int
- count: int
+ slot: int
+
+
+@dataclass
+class VolumeEnvelopeDefStmt(ASTNode):
+ """ppmck volume envelope definition (@v<num> = {...|...})."""
+
+ slot: int
+ points: list[int] = field(default_factory=list)
+ loop_points: list[int] = field(default_factory=list)
+
+
+@dataclass
+class DutyEnvelopeDefStmt(ASTNode):
+ """ppmck duty envelope definition (@<num> = {...|...})."""
+
+ slot: int
+ points: list[int] = field(default_factory=list)
+ loop_points: list[int] = field(default_factory=list)
+
+
+@dataclass
+class DutyEnvelopeUseStmt(ASTNode):
+ """ppmck duty envelope use (@@<num>)."""
+
+ slot: int
+
+
+@dataclass
+class LfoDefStmt(ASTNode):
+ """ppmck LFO definition (@MP<num> = {p1,p2,p3,p4})."""
+
+ slot: int
+ delay: int
+ speed: int
+ depth: int
+ transition: int
+
+
+@dataclass
+class LfoUseStmt(ASTNode):
+ """ppmck LFO use (MP<num>)."""
+
+ slot: int
+
+
+@dataclass
+class LfoOffStmt(ASTNode):
+ """ppmck LFO off (MPOF)."""
+
+ pass
+
+
+@dataclass
+class PitchEnvDefStmt(ASTNode):
+ """ppmck pitch envelope definition (@EP<num> = {...|...})."""
+
+ slot: int
+ points: list[int] = field(default_factory=list)
+ loop_points: list[int] = field(default_factory=list)
+
+
+@dataclass
+class PitchEnvUseStmt(ASTNode):
+ """ppmck pitch envelope use (EP<num>)."""
+
+ slot: int
+
+
+@dataclass
+class PitchEnvOffStmt(ASTNode):
+ """ppmck pitch envelope off (EPOF)."""
+
+ pass
@dataclass
-class EnvelopeDefStmt(ASTNode):
- """ppmck envelope definition (@v0 = { ... })."""
+class NoteEnvDefStmt(ASTNode):
+ """ppmck note envelope definition (@EN<num> = {...|...})."""
slot: int
points: list[int] = field(default_factory=list)
@@ -176,6 +275,32 @@ class EnvelopeDefStmt(ASTNode):
@dataclass
+class NoteEnvUseStmt(ASTNode):
+ """ppmck note envelope use (EN<num>)."""
+
+ slot: int
+
+
+@dataclass
+class NoteEnvOffStmt(ASTNode):
+ """ppmck note envelope off (ENOF)."""
+
+ pass
+
+
+# --- Stage 2 statements (kept in AST but ignored by semantic analyzer) ---
+
+
+@dataclass
+class CountLengthStmt(ASTNode):
+ """ppmck count length note (c%48)."""
+
+ note_name: str
+ accidental: int
+ count: int
+
+
+@dataclass
class SweepStmt(ASTNode):
"""ppmck sweep command (s1,2)."""
@@ -201,13 +326,29 @@ Statement = (
| GateTimeStmt
| TransposeStmt
| DetuneStmt
+ | QuantizeStmt
| TieStmt
+ | TieCmdStmt
+ | DetuneCmdStmt
+ | RelativeVolumeStmt
| RepeatStartStmt
| RepeatEndStmt
| BarStmt
| ExtCmdStmt
+ | VolumeEnvelopeUseStmt
+ | VolumeEnvelopeDefStmt
+ | DutyEnvelopeDefStmt
+ | DutyEnvelopeUseStmt
+ | LfoDefStmt
+ | LfoUseStmt
+ | LfoOffStmt
+ | PitchEnvDefStmt
+ | PitchEnvUseStmt
+ | PitchEnvOffStmt
+ | NoteEnvDefStmt
+ | NoteEnvUseStmt
+ | NoteEnvOffStmt
| CountLengthStmt
- | EnvelopeDefStmt
| SweepStmt
| NoiseModeStmt
)