/**
 * @file mmlCompiler.c
 * @author AIG-Soft/Shinichiro Nakamura
 * @copyright (original)
 * ===============================================================
 * A tiny MML parser (Version 0.5.0)
 * ===============================================================
 * Copyright (c) 2014-2015 Shinichiro Nakamura
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */
// 2015/09/25 for X-BASIC' by AIG-Soft

#include "mml.h"
//#include "ntlibc.h"

/*
 X-BASIC'ではバックグラウンドで動くことになるが、どうもバックグラウンドでreallocしてはいけないようである
 This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes.  This will cause an exception in a future release.
 が発生している。
 iOS9だけで、iOS8では発生しない。
 なので、呼び出し側でメインスレッドで実行するように対策すること
 */

#ifndef NTLIBC_H
#include <ctype.h>
#include <string.h>
#include <stdlib.h>				// atoi()
#define LIBC_TOUPPER			toupper
#define LIBC_STRCHR				strchr
#define LIBC_ATOI				atoi
#define	LIBC_ISDIGIT			isdigit
#else
#define LIBC_TOUPPER			ntlibc_toupper
#define LIBC_STRCHR				ntlibc_strchr
#define LIBC_ATOI				ntlibc_atoi
#define	LIBC_ISDIGIT			ntlibc_isdigit
#endif
#include <stdio.h>
#include "atoi3b.h"
#include "malloc2.h"

#define MML_WORK_BUFFER_SIZE	(16)	// トークン読み出しバッファーbuf1/2のサイズ(コメント以外)


#if (MML_CONFIG_USE_AVR_PROGMEM)
#include <avr/pgmspace.h>
#define TEXT_READ(HANDLE)				(LIBC_TOUPPER(pgm_read_byte((HANDLE)->tp)))
#define TEXT_NEXT(HANDLE)				do { ((HANDLE)->tp)++; } while (0)
#define TEXT_SET_POINTER(HANDLE, P)		do { ((HANDLE)->tp) = (P); } while (0)
#define TEXT_GET_POINTER(HANDLE, PP)	do { (*PP) = ((HANDLE)->tp); } while (0)
#else
#define TEXT_READ(HANDLE)				(LIBC_TOUPPER(*(HANDLE)->tp))	// 大文字化付き
#define TEXT_READ2(HANDLE)				((*(HANDLE)->tp))	// そのまま
#define TEXT_NEXT(HANDLE)				do { ((HANDLE)->tp)++; } while (0)
#define TEXT_SET_POINTER(HANDLE, P)		do { ((HANDLE)->tp) = (P); } while (0)
#define TEXT_GET_POINTER(HANDLE, PP)	do { (*PP) = ((HANDLE)->tp); } while (0)
#endif

typedef enum {
	MML_CHAR_TYPE_NOTE,
	MML_CHAR_TYPE_SHARP,
	MML_CHAR_TYPE_FLAT,
	MML_CHAR_TYPE_TIE,
	MML_CHAR_TYPE_REST,
	MML_CHAR_TYPE_OCTAVE,
	MML_CHAR_TYPE_LENGTH,
	MML_CHAR_TYPE_VOLUME,
	MML_CHAR_TYPE_TEMPO,
	MML_CHAR_TYPE_OCTAVE_UP,
	MML_CHAR_TYPE_OCTAVE_DOWN,
#if ENABLE_TONE_BY_COMMAND
	MML_CHAR_TYPE_SOUND,
#endif
	MML_CHAR_TYPE_PANPOT,
	MML_CHAR_TYPE_NUMBER,
	MML_CHAR_TYPE_EXTEND_MARKER,		// @
	MML_CHAR_TYPE_REPEAT_START,
	MML_CHAR_TYPE_REPEAT_END,
	MML_CHAR_TYPE_RENPU_START,
	MML_CHAR_TYPE_RENPU_END,
	MML_CHAR_TYPE_JUMP_START,
	MML_CHAR_TYPE_RATIO,
	MML_CHAR_TYPE_WAON,
	MML_CHAR_TYPE_UNKNOWN,
} MML_CHAR_TYPE;

//--------------------------------------------------------------------------------------
// 格納処理
/* コマンドの格納方法は次の通り。
 	COM		コマンド		1B
	VAL		1byte値		1B
 +
 	WVAL	2byte値		1W
	WK1/2	各1byte値	1B*2	または	WVAL2	2byte値	1W
 
 	[COM][VAL]
	[COM][VAL][WVAL]
	[COM][VAL][WVAL][WK1][WK2]
	[COM][VAL][WVAL][WVAL2]

	コマンド類はすべてWordサイズ単位で記録され、先頭はmalloc()だから境界整合は気にしなくていいはず。
 	でも実装の都合上、ほとんどの処理はバイト単位で書かれている。
 */
static
MML_RESULT storeMML2Bytes(MML *handle,char b1,char b2)
{
	printFunc();
	if (handle->obj+2>handle->objSize) {
		nslog(@"storeMML2Bytes:入れるだけの余裕が無い時は先に広げる");
		if (handle->objSize+MML_OBJECT_SIZE_STEP>MML_MAX_OBJECT) {
			nslog(@"大きすぎる");
			return MML_RESULT_TOO_LARGE;
		}
		handle->objSize+=MML_OBJECT_SIZE_STEP;
		char *newP;
		if ((newP=realloc2(handle->objTop, handle->objSize))==NULL) {
			nslog(@"メモリが確保できない");
			return MML_RESULT_NOMEMORY;
		}
		handle->objTop=newP;
	}
	if (handle->jumpCnt!=1) {
		handle->objTop[handle->obj++]=b1;
		handle->objTop[handle->obj++]=b2;
	} // ==1の間；最初の[!]〜[!]の間はストアしない
	return MML_RESULT_OK;
}

#define storeMMLCommand1(handle,com,value)	storeMML2Bytes(handle,com,value)

static
MML_RESULT storeMMLWord(MML *handle, int w)
{
	printFunc();
	return storeMML2Bytes(handle,(char)(w),(char)(w>>8));
}

static
// longは32ビット以上必要で、格納するのは下位32ビットのみ
MML_RESULT storeMMLLong(MML *handle,long l)
{
	printFunc();
	// L->Hの順(ARMに合わせて)
	MML_RESULT mr=storeMMLWord(handle,(int)(l&0xffff));
	if (mr==MML_RESULT_OK) {
		mr=storeMMLWord(handle,(int)(l>>16));
	}
	return mr;
}

static
MML_RESULT storeMMLCommand2(MML *handle,int com,int type,int value)
{
	printFunc();
	MML_RESULT mr=storeMMLCommand1(handle, com, type);
	if (mr==MML_RESULT_OK) {
		// L,Hの順(ARMに合わせて)
		mr=storeMMLWord(handle, value);
	}
	return mr;
}

static
MML_RESULT storeMMLCommand2Adrs(MML *handle,int com,int type,int ofst)
{
	printFunc();
	MML_RESULT mr=storeMMLCommand1(handle, com, type);
	if (mr==MML_RESULT_OK) {
		// L,Hの順(ARMに合わせて)
		mr=storeMMLWord(handle, ofst);
	}
	return mr;
}

static
MML_RESULT storeMMLCommand3Adrs(MML *handle,int com,int type,int ofst,int wk1,int wk2)
{
	printFunc();
	MML_RESULT mr=storeMMLCommand1(handle, com, type);
	if (mr==MML_RESULT_OK) {
		// L,Hの順(ARMに合わせて)
		mr=storeMMLWord(handle, ofst);
		if (mr==MML_RESULT_OK) {
			mr=storeMMLCommand1(handle, wk1, wk2);
		}
	}
	return mr;
}

static
MML_RESULT storeMMLCommand4Adrs(MML *handle,int com,int type,int ofst,int ofst2)
{
	printFunc();
	MML_RESULT mr=storeMMLCommand1(handle, com, type);
	if (mr==MML_RESULT_OK) {
		// L,Hの順(ARMに合わせて)
		mr=storeMMLWord(handle, ofst);
		if (mr==MML_RESULT_OK) {
			mr=storeMMLWord(handle, ofst2);
		}
	}
	return mr;
}

// コマンドの書き換え用;既存の部分の書き換えなので、エラー処理はない
static
void storeMML1ByteChange(MML *handle,int adrs,char b1)
{
	printFunc();
	handle->objTop[adrs]=b1;
}

static
void storeMML2BytesChange(MML *handle,int adrs,char b1,char b2)
{
	printFunc();
	handle->objTop[adrs++]=b1;
	handle->objTop[adrs  ]=b2;
}

static
void storeMMLWordChange(MML *handle, int adrs,int w)
{
	printFunc();
	storeMML2BytesChange(handle,adrs,(char)(w),(char)(w>>8));
}

static
MML_RESULT storeMMLComment(MML *handle,char *comment,int *ofst)
{
	printFunc();
	if (handle->jumpCnt!=1) {
		int len=(int)strlen(comment)+1; // +1 for EOS
		nslog(@"comment(%d)=%s",len,comment);
		nslog(@"handle->com=%d,handle->comTop=%p,handle->comSize=%d",handle->comp,handle->comTop,handle->comSize);
		if (handle->comp+len>=handle->comSize) {
			nslog(@"storeMMLComment:入れるだけの余裕が無い時は先に広げる");
			if (handle->comSize+MML_COMMENT_SIZE_STEP>MML_MAX_OBJECT) {
				nslog(@"大きすぎる");
				return MML_RESULT_TOO_LARGE;
			}
			handle->comSize+=MML_COMMENT_SIZE_STEP;
			char *newP;
			// コメントワークはmalloc()系
			if ((newP=realloc(handle->comTop, handle->comSize))==NULL) {
				nslog(@"メモリが確保できない");
				return MML_RESULT_NOMEMORY;
			}
			handle->comTop=newP;
		}
		*ofst=handle->comp; // 書き込んだオフセットを返す
		strcpy(&handle->comTop[handle->comp], comment);
		handle->comp+=len;
		handle->realComSize+=len;
	} // ==1の間；最初の[!]〜[!]の間はストアしない
	return MML_RESULT_OK;
}

//--------------------------------------------------------------------------------------
// 解釈処理
//--------------------------------------------------------------------------------------

static MML_RESULT skip_whitespace(MML *handle)
{
	while (
			((TEXT_READ(handle)) == ' ')
		 || ((TEXT_READ(handle)) == '\r')
		 || ((TEXT_READ(handle)) == '\n')
		 || ((TEXT_READ(handle)) == '\t')
	) {
		TEXT_NEXT(handle);
	}
	return MML_RESULT_OK;
}

static char *skipWhitespace(char *p)
{
	char c;
	while (c=*p,
		      (c == ' ')
		   || (c == '\r')
		   || (c == '\n')
		   || (c == '\t')
		   ) {
		p++;
	}
	return p;
}

//--------------------------------

static MML_RESULT get_note_number(MML *handle, char *text, int *number)
{
	printFunc();
	char *note_distance = (char *)"C D EF G A B";
	char *p;

	p = LIBC_STRCHR(note_distance, text[0]);
	if (p == 0) {
		printf("MML_RESULT_ILLEGAL_NOTE_TEXT\n");
		return MML_RESULT_ILLEGAL_NOTE_TEXT;
	}
	*number = (int)p - (int)note_distance;

	p = &text[1];
	while (*p) {
		if (*p == '+') {
			*number = *number + 1;
		}
		if (*p == '-') {
			*number = *number - 1;
		}
		p++;
	}
	*number += (handle->option.octave * MUSIC_SCALES);

	return MML_RESULT_OK;
}

//--------------------------------

static MML_RESULT convert_note_length_to_ticks(MML *handle, int note_length, int *ticks)
{
	printFunc();
	int bticks = handle->option.bticks;
	nslog(@"bticks=%d",bticks);
	if (note_length<0) {
		// @Ln
		*ticks=((-note_length)*bticks/48);
		nslog(@"詳細音長:%d->ticks:%d",-note_length,*ticks);
	} else {
		switch (note_length) {
			case 0: // 隠し機能:音長0
				*ticks = 0;
				break;
			case 1:
				*ticks = (bticks * 4);
				break;
			case 2:
				*ticks = (bticks * 2);
				break;
			case 4:
				*ticks = (bticks * 1);
				break;
			case 8:
				*ticks = (bticks / 2);
				break;
			case 16:
				*ticks = (bticks / 4);
				break;
			case 32:
				*ticks = (bticks / 8);
				break;
			case 64:
				*ticks = (bticks / 16);
				break;
			default:
				*ticks = 0;
				printf("音長指定異常:%d\n",note_length);
				return MML_RESULT_ILLEGAL_LENGTH_VALUE;
		}
	}
	return MML_RESULT_OK;
}

//--------------------------------

// 付点 n.. の読み出し
static int get_futen(char *p,int baseTicks)
{
	int ticks = baseTicks;
	while (p=skipWhitespace(p),*p == '.') { // 付点は複数付くことがある
		baseTicks/=2; // 1個付くごとに音長/2^nを加える
		ticks += baseTicks;
		nslog(@"付点:+%d",baseTicks);
		p++;
	}
	return ticks;
}

// textはget_token()で切りだされたもの
static MML_RESULT get_note_ticks(MML *handle, char *text, int *ticks)
{
	printFunc();
	
	UByte *p;
	int note_length = atoi3b((UByte *)text,10,&p);
	nslog(@"note_length=%s(%d)",text,note_length);
	
	int val;
	MML_RESULT mr;
	mr = convert_note_length_to_ticks(handle, note_length, &val);
	if (mr != MML_RESULT_OK) {
		return mr;
	}
	nslog(@"ticks=%d",val);

	// 付点
	*ticks=get_futen((char *)p, val);
	
	return MML_RESULT_OK;
}

static MML_RESULT get_note_ticks_default(MML *handle, int *ticks)
{
	return convert_note_length_to_ticks(handle, handle->option.length, ticks);
}

//--------------------------------

// get_token()で切り出しに使う文字はすべて登録しなければならない
static MML_CHAR_TYPE get_char_type(char c)
{
	switch (c) {
		case 'C':
		case 'D':
		case 'E':
		case 'F':
		case 'G':
		case 'A':
		case 'B':
			printf("MML_CHAR_TYPE_NOTE\n");
			return MML_CHAR_TYPE_NOTE;
			
		case '#':
		case '+':
			printf("MML_CHAR_TYPE_SHARP\n");
			return MML_CHAR_TYPE_SHARP;
		case '-':
			
			printf("MML_CHAR_TYPE_FLAT\n");
			return MML_CHAR_TYPE_FLAT;
			
		case 'R':
			printf("MML_CHAR_TYPE_REST\n");
			return MML_CHAR_TYPE_REST;
		case 'L':
			printf("MML_CHAR_TYPE_LENGTH\n");
			return MML_CHAR_TYPE_LENGTH;
		case 'V':
			printf("MML_CHAR_TYPE_VOLUME\n");
			return MML_CHAR_TYPE_VOLUME;
		case 'T':
			printf("MML_CHAR_TYPE_TEMPO\n");
			return MML_CHAR_TYPE_TEMPO;
			
		case 'O':
			printf("MML_CHAR_TYPE_OCTAVE\n");
			return MML_CHAR_TYPE_OCTAVE;
#if (MML_CONFIG_OCTAVE_REVERSE)
		case '<':
			printf("MML_CHAR_TYPE_OCTAVE_UP\n");
			return MML_CHAR_TYPE_OCTAVE_UP;
		case '>':
			printf("MML_CHAR_TYPE_OCTAVE_DOWN\n");
			return MML_CHAR_TYPE_OCTAVE_DOWN;
#else
		case '>':
			printf("MML_CHAR_TYPE_OCTAVE_UP\n");
			return MML_CHAR_TYPE_OCTAVE_UP;
		case '<':
			printf("MML_CHAR_TYPE_OCTAVE_DOWN\n");
			return MML_CHAR_TYPE_OCTAVE_DOWN;
#endif
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
		case '.':
			printf("MML_CHAR_TYPE_NUMBER\n");
			return MML_CHAR_TYPE_NUMBER;

		case '&':
			printf("MML_CHAR_TYPE_TIE\n");
			return MML_CHAR_TYPE_TIE;
			
#if ENABLE_TONE_BY_COMMAND
		case 'S':
			printf("MML_CHAR_TYPE_SOUND\n");
			return MML_CHAR_TYPE_SOUND;
#endif
		case 'P':
			printf("MML_CHAR_TYPE_PANPOT\n");
			return MML_CHAR_TYPE_PANPOT;

		case 'Q':
			printf("MML_CHAR_TYPE_RATIO\n");
			return MML_CHAR_TYPE_RATIO;
		
		case '@':
			printf("MML_CHAR_TYPE_EXTEND_MARKER\n");
			return MML_CHAR_TYPE_EXTEND_MARKER;

			
		case '\'':
			printf("MML_CHAR_TYPE_WAON\n");
			return MML_CHAR_TYPE_WAON;
	
		//
		case '|':	// ループ開始またはループ中のカウント別処理
			printf("MML_CHAR_TYPE_REPEAT_START\n");
			return MML_CHAR_TYPE_REPEAT_START;
		case ':':
			printf("MML_CHAR_TYPE_REPEAT_END\n");
			return MML_CHAR_TYPE_REPEAT_END;
		//
		case '{':
			printf("MML_CHAR_TYPE_RENPU_START\n");
			return MML_CHAR_TYPE_RENPU_START;
		case '}':
			printf("MML_CHAR_TYPE_RENPU_END\n");
			return MML_CHAR_TYPE_RENPU_END;
			
		//
		case '[':
			printf("MML_CHAR_TYPE_JUMP_START\n");
			return MML_CHAR_TYPE_JUMP_START;
		// ]は[の処理内で読み取られるのでここでは不要
//		case ']':
//			printf("MML_CHAR_TYPE_JUMP_END\n");
//			return MML_CHAR_TYPE_JUMP_END;
	}
	// tokenの終端として改行コードが来るときはここ
//	if (c<' ') 	printf("MML_CHAR_TYPE_UNKNOWN:$%02x\n",c);
//	else		printf("MML_CHAR_TYPE_UNKNOWN:%c\n",c);
	return MML_CHAR_TYPE_UNKNOWN;
}

//--------------------------------

static MML_RESULT get_token(MML *handle, char *buf, int siz)
{
	MML_CHAR_TYPE ct_head, ct_next;
	int cnt = 0;

//	for (int i = 0; i < siz; i++) {
//		buf[i] = '\0';
//	}

	skip_whitespace(handle);
	if (TEXT_READ(handle) == '\0') {
		// MMLが終わった
		nslog(@"MML終了");
		return MML_RESULT_EOT;
	}
	ct_head = get_char_type(TEXT_READ(handle));
	while ((ct_next = get_char_type(TEXT_READ(handle))) == ct_head) {
		*buf++ = TEXT_READ(handle);
		TEXT_NEXT(handle);
		if ((MML_CHAR_TYPE_NOTE 		== ct_head) && (MML_CHAR_TYPE_NOTE 			== ct_next)) {
			break;
		}
		if ((MML_CHAR_TYPE_REPEAT_START	== ct_head) && (MML_CHAR_TYPE_REPEAT_START	== ct_next)) {
			break;
		}
		if ((MML_CHAR_TYPE_REPEAT_END   == ct_head) && (MML_CHAR_TYPE_REPEAT_END	== ct_next)) {
			break;
		}
		cnt++;
		if (siz <= (cnt + 1)) {
			*buf++ = 0;
			printf("MML_RESULT_ILLEGAL_TOKEN_LENGTH\n");
			return MML_RESULT_ILLEGAL_TOKEN_LENGTH;
		}
	}

	while (1) {
		MML_CHAR_TYPE ct_work;
		ct_work = get_char_type(TEXT_READ(handle));
		if ((ct_work != MML_CHAR_TYPE_SHARP) && (ct_work != MML_CHAR_TYPE_FLAT)) {
			break;
		}
		*buf++ = TEXT_READ(handle);
		TEXT_NEXT(handle);
	}

	*buf++ = 0;

	return MML_RESULT_OK;
}

//----------------------------------------------------------
// ユーザーイベントの文字列を読み出す
// @?～?の～ cstからestまででくくられた範囲
// handleはcstの直後にあること
// ユーザーイベント内は大文字化しない(コメントのため);iOSで変更
static MML_RESULT get_user_event_token(MML *handle, char *buf, int siz,char cst,char est)
{
	int cnt = 0;

//	for (int i = 0; i < siz; i++) {
//		buf[i] = '\0';
//	}

	char c;
	while ((c=TEXT_READ2(handle)) != est) {
		*buf++ = c;
		TEXT_NEXT(handle);
		if (c == '\0') {
			nslog(@"MML_RESULT_ILLEGAL_SEQUENCE_FOUND");
			return MML_RESULT_ILLEGAL_SEQUENCE_FOUND;
		}
		cnt++;
		if (siz <= (cnt + 1)) {
			*buf++ = 0;
			printf("MML_RESULT_ILLEGAL_USER_EVENT_LENGTH\n");
			return MML_RESULT_ILLEGAL_USER_EVENT_LENGTH;
		}
	}
	*buf++ = 0;
	TEXT_NEXT(handle); // estの次へ
	return MML_RESULT_OK;
}

//----------------------------------------------------------
// MMLコンパイル
//----------------------------------------------------------

MML_RESULT mml_compile1(MML *handle)
{
	printFunc();
	
	char buf1[MML_WORK_BUFFER_SIZE];
	char buf2[MML_WORK_BUFFER_SIZE];
	char *p;
	
	MML_RESULT mr;
	mr = get_token(handle, buf1, sizeof(buf1));
	if (mr != MML_RESULT_OK) {
		// EOTがあった
		nslog(@"MML終了");
		if (handle->fwaon==YES) {
			nslog(@"和音が閉じていない");
			return MML_RESULT_ILLEGAL_WAON;
		}
		// |: :|が閉じているかチェック
		if (mml_int_stack_count(&handle->startAdrs)>0) {
			// スタック上に残っている＝閉じていない
			nslog(@"リピートが閉じていない");
			return MML_RESULT_ILLEGAL_REPEAT_PAIR;
		}
		// 連符が閉じているかチェック
		if (handle->frenpu!=RENPU_NONE) {
			nslog(@"連符が閉じてない");
			return MML_RESULT_ILLEGAL_RENPU_PAIR;
		}
		// TOCODAが解決しているかチェック
		if (handle->storeToCoda!=0) {
			nslog(@"TOCODAのCODAが見つからない");
			return MML_RESULT_NO_CODA;
		}
		// [SEGNO]があって[D.S.]がなくてもエラーにはしない
		// [DO]があって[LOOP]がなくてもエラーにはしない
		// MML_RESULT_EOTなら終了
		return mr;
	}
	buf2[0]=0; // これとしておかないとtoken1だけで終わっている時にbuf2が前の値のままになる
	nslog(@"token1=%s",buf1);

	// 和音処理中に記述できるのはnoteのみ(音長付き+タイ)
	switch (get_char_type(buf1[0])) {
		default:
			if (handle->fwaon==YES) {
				nslog(@"和音の中に音符以外がある:%s",buf1);
				return MML_RESULT_ILLEGAL_WAON;
			}
		// 以下は和音内でも有効
		case MML_CHAR_TYPE_WAON:
		case MML_CHAR_TYPE_NOTE:
		case MML_CHAR_TYPE_OCTAVE_UP:
		case MML_CHAR_TYPE_OCTAVE_DOWN:
		case MML_CHAR_TYPE_OCTAVE:
			break;
	}
	
	int ticks,noteNumber,value;
	switch (get_char_type(buf1[0])) {
		case MML_CHAR_TYPE_NOTE:
			if (handle->fwaon==YES) {
				if (++handle->waonCnt>MML_MAX_WAON) {
					nslog(@"和音の数が多すぎる:%d",handle->waonCnt);
					return MML_RESULT_WAON_TOO_MUCH;
				}
			}
			// 先に音長の取得
			TEXT_GET_POINTER(handle, &p); // 音長指定がないときに元に戻すため
			mr = get_token(handle, buf2, sizeof(buf2));
			if (mr != MML_RESULT_OK) return mr;
			nslog(@"note token2=%s",buf2);
			// 音階の解釈
			mr = get_note_number(handle, buf1, &noteNumber);
			if (mr != MML_RESULT_OK) return mr;
			
			if (handle->frenpu==RENPU_REWRITE) {
				nslog(@"連符音長を持って発声");
				handle->prevNote=handle->obj; // 直前のNOTEのアドレス(オブジェクト)
				mr=storeMMLCommand2(handle,MML_COM_NOTE,noteNumber,handle->renpuTicks);
				if (mr != MML_RESULT_OK) return mr;
				TEXT_SET_POINTER(handle, p);
				break;
			}
			if (get_char_type(buf2[0]) == MML_CHAR_TYPE_NUMBER) {
				nslog(@"音長指定がある(付点を含む):%s",buf2);
				switch (handle->frenpu) {
					case RENPU_START:
						nslog(@"連符なのに個別の音に音長指定がある");
						return MML_RESULT_ILLEGAL_RENPU;
					default:
						break;
				}
				if (!LIBC_ISDIGIT(buf2[0])) {
					nslog(@"実は音長指定はなく付点だけだった");
					// 現在の音長の読み出し
					mr = get_note_ticks_default(handle, &ticks);
					if (mr != MML_RESULT_OK) return mr;
					nslog(@"現在音長=%d",ticks);
					ticks=get_futen(buf2, ticks);
				} else {
					// 音長の読み出し
					mr = get_note_ticks(handle , buf2, &ticks);
					if (mr != MML_RESULT_OK) return mr;
				}
				handle->prevNote=handle->obj; // 直前のNOTEのアドレス(オブジェクト)
				mr=storeMMLCommand2(handle,MML_COM_NOTE,noteNumber,ticks);
				if (mr != MML_RESULT_OK) return mr;
			} else { // 音長/付点がない
				nslog(@"音長指定がない"); // 連符中は必ずこちら
				if (handle->frenpu!=RENPU_START) {
					// 現在の音長の読み出し
					mr = get_note_ticks_default(handle, &ticks);
					if (mr != MML_RESULT_OK) return mr;
					nslog(@"現在音長=%d",ticks);
					handle->prevNote=handle->obj; // 直前のNOTEのアドレス(オブジェクト)
					mr=storeMMLCommand2(handle,MML_COM_NOTE,noteNumber,ticks);
					if (mr != MML_RESULT_OK) return mr;
				} else {
					// 連符中は数を数えるだけ
					handle->renpuCount++;
				}
				// 読み取り位置を戻す
				TEXT_SET_POINTER(handle, p);
			}
			break;
			
		case MML_CHAR_TYPE_REST:
			handle->prevNote=0; // 直前はNOTEではない
			// 先に音長の取得
			TEXT_GET_POINTER(handle, &p); // 音長指定がないときに元に戻すため
			mr = get_token(handle, buf2, sizeof(buf2));
			nslog(@"rest token2=%s",buf2);
			// 音楽的にはありえないかもしれないけど、連符の中に休符も入れられるようにしておく
			if (handle->frenpu==RENPU_REWRITE) {
				nslog(@"音長書き込み中");
				mr=storeMMLCommand2(handle,MML_COM_REST,0,handle->renpuTicks);
				if (mr != MML_RESULT_OK) return mr;
				TEXT_SET_POINTER(handle, p);
				break;
			}
			if (get_char_type(buf2[0]) == MML_CHAR_TYPE_NUMBER) {
				nslog(@"音長指定がある:%s",buf2);
				switch (handle->frenpu) {
					case RENPU_START:
						nslog(@"連符なのに個別の音に音長指定がある");
						return MML_RESULT_ILLEGAL_RENPU;
						break;
						
					case RENPU_NONE:
					default:
						break;
				}
				mr = get_note_ticks(handle, buf2, &ticks);
				if (mr != MML_RESULT_OK) return mr;
				mr=storeMMLCommand2(handle,MML_COM_REST,0,ticks);
				if (mr != MML_RESULT_OK) return mr;
			} else {
				nslog(@"音長指定がない"); // 連符中は必ずこちら
				if (handle->frenpu!=RENPU_START) {
					// 現在の音長の読み出し
					mr = get_note_ticks_default(handle, &ticks);
					if (mr != MML_RESULT_OK) return mr;
					mr=storeMMLCommand2(handle,MML_COM_REST,0,ticks);
					if (mr != MML_RESULT_OK) return mr;
				} else {
					// 連符中は数を数えるだけ
					handle->renpuCount++;
				}
				// 読み取り位置を戻す
				TEXT_SET_POINTER(handle, p);
			}
			break;
			
		case MML_CHAR_TYPE_RENPU_START:
			handle->prevNote=0; // 直前はNOTEではない
		{
			nslog(@"連符開始{");
			if (handle->frenpu!=RENPU_NONE) {
				nslog(@"連符がネストしている");
				return MML_RESULT_ILLEGAL_RENPU_PAIR;
			}
			// handleは{の次の文字を指している
			TEXT_GET_POINTER(handle, &handle->renpuTop); // 位置保存
			handle->frenpu=RENPU_START;
			handle->renpuCount=0;
		}
			break;
			
		case MML_CHAR_TYPE_RENPU_END:
			nslog(@"}連符終了");
			if (handle->frenpu==RENPU_NONE) {
				nslog(@"連符中でないのに}があった");
				return MML_RESULT_ILLEGAL_RENPU_PAIR;
			}
			if (handle->frenpu==RENPU_REWRITE) {
				nslog(@"連符の音処理終了");
				handle->frenpu=RENPU_NONE;
				TEXT_SET_POINTER(handle, handle->renpuEnd); // 連符後ろから読み取り再開
				break;
			}
			// handle->frenpu==RENPU_START
			nslog(@"この連符に割り当てる音長を取得する");
			char *p;
			TEXT_GET_POINTER(handle, &p); // 音長指定がないときに元に戻すため
			mr = get_token(handle, buf2, sizeof(buf2));
			if (mr != MML_RESULT_OK) return mr;
			nslog(@"renpu token2=%s",buf2);
			if (get_char_type(buf2[0]) == MML_CHAR_TYPE_NUMBER) {
				nslog(@"音長指定がある:%s",buf2);
				mr = get_note_ticks(handle , buf2, &ticks);
				if (mr != MML_RESULT_OK) {
					return mr;
				}
				// 音長後ろ位置を保存
				TEXT_GET_POINTER(handle, &handle->renpuEnd);
			} else {
				nslog(@"音長指定がない");
				mr = get_note_ticks_default(handle, &ticks);
				if (mr != MML_RESULT_OK) {
					return mr;
				}
				handle->renpuEnd=p;
			}
			// 1音あたりの音長さ
			nslog(@"音符数=%d/全体ticks=%d",handle->renpuCount,ticks);
			handle->renpuTicks=ticks/handle->renpuCount;
			nslog(@"1音あたりのticks=%d",handle->renpuTicks);
			if (handle->renpuTicks<1) {
				nslog(@"連符により1音が短くなりすぎる");
				return MML_RESULT_ILLEGAL_RENPU;
			}
			
			nslog(@"遡って連符音長を登録する");
			handle->frenpu=RENPU_REWRITE;
			TEXT_SET_POINTER(handle, handle->renpuTop); // 読み取り位置を戻す
			break;
			
		case MML_CHAR_TYPE_WAON:
			// '〜'間を一気に発音させる
			if (handle->fwaon==NO) {
				// NO->YES
				nslog(@"和音開始");
				handle->fwaon=YES;
				handle->waonCnt=0;
				mr=storeMMLCommand1(handle, MML_COM_WAON, 0);
				handle->prevNote=0; // 直前はNOTEではない
			} else {
				// YES->NO
				// 最適化はしてないので、''と書いた場合は[WAON][WAON]と出力され、実質何も行われない
				mr=storeMMLCommand1(handle, MML_COM_WAON, handle->waonCnt);
				handle->fwaon=NO;
				// ここでは直前がNOTEを消さない
				nslog(@"和音終了");
			}
			break;
			
		case MML_CHAR_TYPE_LENGTH:
			handle->prevNote=0; // 直前はNOTEではない
			mr = get_token(handle, buf2, sizeof(buf2));
			nslog(@"number(音長)=%s",buf2);
			if (get_char_type(buf2[0]) != MML_CHAR_TYPE_NUMBER) {
				nslog(@"Lの後の数値省略時はエラー"); // それがnoteとの違い
				return MML_RESULT_ILLEGAL_LENGTH_VALUE;
			}
			value=LIBC_ATOI(buf2);
			nslog(@"number(length)=%d",value);
			handle->option.length = value;
			break;
	
		case MML_CHAR_TYPE_OCTAVE:
			handle->prevNote=0; // 直前はNOTEではない
			mr = get_token(handle, buf2, sizeof(buf2));
			if (mr != MML_RESULT_OK) return mr;
			nslog(@"number(octave)=%s",buf2);
			if (get_char_type(buf2[0]) != MML_CHAR_TYPE_NUMBER) {
				printf("MML_RESULT_ILLEGAL_OCTAVE_VALUE\n");
				return MML_RESULT_ILLEGAL_OCTAVE_VALUE;
			}
			value = LIBC_ATOI(buf2);
			if (value<MML_OCTAVE_MIN) value=MML_OCTAVE_MIN;
			if (value>MML_OCTAVE_MAX) value=MML_OCTAVE_MAX;
			handle->option.octave  = value;
			mr=storeMMLCommand1(handle, MML_COM_OCTAVE, value);
			if (mr != MML_RESULT_OK) return mr;
			break;
			
		case MML_CHAR_TYPE_OCTAVE_UP:
			handle->prevNote=0; // 直前はNOTEではない
			p = buf1;
			while (*p++) {
				if (handle->option.octave < MML_OCTAVE_MAX) {
					handle->option.octave++;
				}
			}
			mr=storeMMLCommand1(handle, MML_COM_OCTAVE, handle->option.octave);
			if (mr != MML_RESULT_OK) return mr;
			break;
			
		case MML_CHAR_TYPE_OCTAVE_DOWN:
			handle->prevNote=0; // 直前はNOTEではない
			p = buf1;
			while (*p++) {
				if (MML_OCTAVE_MIN < handle->option.octave) {
					handle->option.octave--;
				}
			}
			mr=storeMMLCommand1(handle, MML_COM_OCTAVE, handle->option.octave);
			if (mr != MML_RESULT_OK) return mr;
			break;
			
		case MML_CHAR_TYPE_TEMPO:
			handle->prevNote=0; // 直前はNOTEではない
			mr = get_token(handle, buf2, sizeof(buf2));
			if (mr != MML_RESULT_OK) return mr;
			nslog(@"number(tempo)=%s",buf2);
			if (get_char_type(buf2[0]) != MML_CHAR_TYPE_NUMBER) {
				printf("MML_RESULT_ILLEGAL_TEMPO_VALUE\n");
				return MML_RESULT_ILLEGAL_TEMPO_VALUE;
			}
			value = LIBC_ATOI(buf2);
			mml_tempo(handle, value); // 内部設定
			mr=storeMMLCommand1(handle, MML_COM_TEMPO, value);
			if (mr != MML_RESULT_OK) return mr;
			break;
	
		case MML_CHAR_TYPE_VOLUME:
			handle->prevNote=0; // 直前はNOTEではない
			mr = get_token(handle, buf2, sizeof(buf2));
			if (mr != MML_RESULT_OK) return mr;
			nslog(@"number(volume)=%s",buf2);
			if (get_char_type(buf2[0]) != MML_CHAR_TYPE_NUMBER) {
				printf("MML_RESULT_ILLEGAL_VOLUME_VALUE\n");
				return MML_RESULT_ILLEGAL_VOLUME_VALUE;
			}
			value = LIBC_ATOI(buf2);
			if (value<MML_MIN_VOLUME) value=MML_MIN_VOLUME;
			if (value>MML_MAX_VOLUME) value=MML_MAX_VOLUME;
			// V0～15を@{V}0～127に変換する
			value=value*(MML_MAX_EX_VOLUME/MML_MAX_VOLUME);
			mr=storeMMLCommand1(handle, MML_COM_VOLUME, value);
			if (mr != MML_RESULT_OK) return mr;
			break;
	
		case MML_CHAR_TYPE_PANPOT:
			handle->prevNote=0; // 直前はNOTEではない
			mr = get_token(handle, buf2, sizeof(buf2));
			if (mr != MML_RESULT_OK) return mr;
			nslog(@"number(panpot)=%s",buf2);
			if (get_char_type(buf2[0]) != MML_CHAR_TYPE_NUMBER) {
				printf("MML_RESULT_ILLEGAL_PANPOT_VALUE\n");
				return MML_RESULT_ILLEGAL_PANPOT_VALUE;
			}
			value= LIBC_ATOI(buf2);
			// Pでは1～3だけど、内部的には@Pの0～127
			switch (value) {
				case MML_PANPOT_LEFT:	// 左
					value=MML_MAX_EX_PANPOT;
					break;
				case MML_PANPOT_RIGHT:	// 右
					value=MML_MIN_EX_PANPOT;
					break;
				default:
				case MML_PANPOT_CENTER:	// 中央
					value=MML_CENTER_EX_PANPOT;
					break;
			}
			nslog(@"->%d",value);
			mr=storeMMLCommand1(handle, MML_COM_PANPOT, value);
			if (mr != MML_RESULT_OK) return mr;
			break;

		case MML_CHAR_TYPE_RATIO:
			handle->prevNote=0; // 直前はNOTEではない
			mr = get_token(handle, buf2, sizeof(buf2));
			if (mr != MML_RESULT_OK) return mr;
			nslog(@"number(ratio)=%s",buf2);
			if (get_char_type(buf2[0]) != MML_CHAR_TYPE_NUMBER) {
				printf("MML_RESULT_ILLEGAL_RATIO_VALUE\n");
				return MML_RESULT_ILLEGAL_RATIO_VALUE;
			}
			value = LIBC_ATOI(buf2);
			mr=storeMMLCommand1(handle, MML_COM_RATIO, value);
			if (mr != MML_RESULT_OK) return mr;
			break;

		case MML_CHAR_TYPE_EXTEND_MARKER:
		{
			handle->prevNote=0; // 直前はNOTEではない
			mr = get_token(handle, buf2, sizeof(buf2));
			if (mr != MML_RESULT_OK) return mr;
			nslog(@"sub token=%@",@(buf2));
			/* 内部での拡張分:buf2
				コメント		@'〜'
				音量			@V0～127
				パンポット	@P0～127
			 	音色			@1〜100
			 	音長			@L1〜192
				*/
			int value;
			if (get_char_type(buf2[0]) == MML_CHAR_TYPE_NUMBER) {
				nslog(@"音色");
				value= LIBC_ATOI(buf2);
				if (value<MML_MIN_TONE) value=MML_MIN_TONE;
				if (value>MML_MAX_TONE) value=MML_MAX_TONE;
				mr=storeMMLCommand1(handle, MML_COM_SOUND, value);
				if (mr != MML_RESULT_OK) return mr;
				break;
			}
			switch (buf2[0]) {
				case 'L':
					nslog(@"拡張音長");
					mr = get_token(handle, buf2, sizeof(buf2));
					if (mr != MML_RESULT_OK) return mr;
					nslog(@"number(拡張音長)=%s",buf2);
					if (get_char_type(buf2[0]) != MML_CHAR_TYPE_NUMBER) {
						nslog(@"@Lの後の数値省略時はエラー"); // それがnoteとの違い
						return MML_RESULT_ILLEGAL_LENGTH_VALUE;
					}
					value = LIBC_ATOI(buf2);
					if (value<MML_MIN_EX_LENGTH) value=MML_MIN_EX_LENGTH;
					if (value>MML_MAX_EX_LENGTH) value=MML_MAX_EX_LENGTH;
					// @Lnの指定では、n*10がticksになる
					handle->option.length = -value; // 拡張時はマイナス値で入れておく
					break;
				case 'V':
					nslog(@"拡張volume");
					mr = get_token(handle, buf2, sizeof(buf2));
					if (mr != MML_RESULT_OK) return mr;
					nslog(@"number(volume)=%s",buf2);
					if (get_char_type(buf2[0]) != MML_CHAR_TYPE_NUMBER) {
						printf("MML_RESULT_ILLEGAL_VOLUME_VALUE\n");
						return MML_RESULT_ILLEGAL_VOLUME_VALUE;
					}
					value = LIBC_ATOI(buf2);
					if (value<MML_MIN_EX_VOLUME) value=MML_MIN_EX_VOLUME;
					if (value>MML_MAX_EX_VOLUME) value=MML_MAX_EX_VOLUME;
					mr=storeMMLCommand1(handle, MML_COM_VOLUME, value);
					if (mr != MML_RESULT_OK) return mr;
					break;
				case 'P':
					nslog(@"拡張panpot");
					mr = get_token(handle, buf2, sizeof(buf2));
					if (mr != MML_RESULT_OK) return mr;
					nslog(@"number(panpot)=%s",buf2);
					if (get_char_type(buf2[0]) != MML_CHAR_TYPE_NUMBER) {
						printf("MML_RESULT_ILLEGAL_PANPOT_VALUE\n");
						return MML_RESULT_ILLEGAL_PANPOT_VALUE;
					}
					value = LIBC_ATOI(buf2);
					if (value<MML_MIN_EX_PANPOT) value=MML_MIN_EX_PANPOT;
					if (value>MML_MAX_EX_PANPOT) value=MML_MAX_EX_PANPOT;
					mr=storeMMLCommand1(handle, MML_COM_PANPOT, value);
					if (mr != MML_RESULT_OK) return mr;
					break;
				case '\'':
				{
					nslog(@"コメント");
					char comment[MML_COMMENT_NAME_SIZE]={0};
					mr=get_user_event_token(handle, comment, sizeof(comment),buf2[0],buf2[0]);
					if (mr!=MML_RESULT_OK) {
						return mr;
					}
					nslog(@"comment:%@",@(comment));
					int ofst;
					mr=storeMMLComment(handle,comment,&ofst);
					if (mr != MML_RESULT_OK) return mr;
					mr=storeMMLCommand2(handle, MML_COM_COMMENT, 0, ofst);
					if (mr != MML_RESULT_OK) return mr;
				}
					break;
					
				default:
					nslog(@"MML_RESULT_ILLEGAL_SEQUENCE_FOUND(%d):%@",__LINE__,@(buf1));
					return MML_RESULT_ILLEGAL_SEQUENCE_FOUND;
					
			} // switch (c)
		} // MML_CHAR_TYPE_EXTEND_MARKER
			break;
			
		case MML_CHAR_TYPE_REPEAT_START:
		{
			handle->prevNote=0; // 直前はNOTEではない
			nslog(@"|:？");
			// handleは|の次の文字を指している
			TEXT_GET_POINTER(handle, &p);
			if (*p!=':') { // |:ではない
				mr = get_token(handle, buf2, sizeof(buf2));
				if (mr != MML_RESULT_OK) return mr;
				nslog(@"number(ループn回目)=%s",buf2);
				if (get_char_type(buf2[0]) != MML_CHAR_TYPE_NUMBER) {
					nslog(@"|nのnの指定がない");
					return MML_RESULT_ILLEGAL_REPEATN_VALUE;
				}
				// |nである
				if (mml_int_stack_count(&handle->startAdrs)==0) {
					nslog(@"|:がないのに|nがあった");
					return MML_RESULT_ILLEGAL_REPEAT_PAIR;
				}

				if (handle->prevRepeat!=0) {
					nslog(@"前の|nがあるので、そこへジャンプアドレスを書き込む:$%04x",handle->obj);
					storeMMLWordChange(handle,handle->prevRepeat+4,handle->obj); // ジャンプアドレスのみ書き換え
					handle->prevRepeat=0; // 開放
				}
				
				value = LIBC_ATOI(buf2);
				nslog(@"|n=%d",value);
				// 位置として記録するのはオブジェクトの位置
				if (mml_int_stack_push(&(handle->repeatNAdrs), handle->obj) != MML_STACK_RESULT_OK) {
					printf("MML_RESULT_STACK_FULL_REPEAT_START\n");
					return MML_RESULT_STACK_FULL_REPEAT_START;
				}
				// 仮出力
				// カウンターアドレスとジャンプアドレスは未定
				handle->prevRepeat=handle->obj; // 書き込み位置保存
				// ループ回数は内部では0〜
				mr=storeMMLCommand4Adrs(handle, MML_COM_NEJUMP, value-1, 0,0);
				break;
			}
			nslog(@"|:だった");
			p++; // 読み取りポインタは先に進める(:の次)
			TEXT_SET_POINTER(handle, p);

			// デバッグ用
			mr=storeMMLCommand1(handle, MML_COM_NOP, MML_NOP_LOOP_START);
			if (mr != MML_RESULT_OK) return mr;

			int loopCount=2; // 省略時は2回
			mr = get_token(handle, buf2, sizeof(buf2));
			if (get_char_type(buf2[0]) == MML_CHAR_TYPE_NUMBER) {
				nslog(@"|:n");
				loopCount=LIBC_ATOI(buf2);
				if (loopCount<MML_MIN_LOOP) loopCount=MML_MIN_LOOP;
				if (loopCount>MML_MAX_LOOP) loopCount=MML_MAX_LOOP;
				nslog(@"number(ループ回数)=%d",loopCount);
			} else {
				// 省略時は読み取り位置を戻す
				TEXT_SET_POINTER(handle, p);
			}
			// 回数を保存
			if (mml_int_stack_push(&(handle->cnt), loopCount) != MML_STACK_RESULT_OK) {
				printf("MML_RESULT_STACK_FULL_REPEAT_START\n");
				return MML_RESULT_STACK_FULL_REPEAT_START;
			}
			// ループ開始位置として記録するのはオブジェクトの位置
			if (mml_int_stack_push(&(handle->startAdrs), handle->obj) != MML_STACK_RESULT_OK) {
				printf("MML_RESULT_STACK_FULL_REPEAT_START\n");
				return MML_RESULT_STACK_FULL_REPEAT_START;
			}
		}
			break;
			
		case MML_CHAR_TYPE_REPEAT_END:
		{
			handle->prevNote=0; // 直前はNOTEではない
			nslog(@":|？");
			// handleは:の次の文字を指している
			TEXT_GET_POINTER(handle, &p);
			if (*p!='|') { // :|でない時
				nslog(@":|でない→:のみとみなし、無視する"); // X68のMMLにそういう記述がある(用途は不明)
				break;
			}
			nslog(@":|だった");
			p++; // 読み取りポインタは先に進める(:|の次)
			TEXT_SET_POINTER(handle, p);

			if (handle->prevRepeat!=0) {
				nslog(@"前の|nがあるので、そこへジャンプアドレスを書き込む");
				int ofst=handle->obj; // 現在の書き込み位置を保存
				handle->obj=handle->prevRepeat+4; // ジャンプアドレスのみ書き換え
				mr=storeMMLWord(handle,ofst);
				if (mr!=MML_RESULT_OK) return mr;
				// 各|nにループカウンタアドレスを書き込む
				// ループカウンタは次に書き込みコマンドの[com.b][cnt.b][adrs.w][現在回数.b][0.b]現在回数位置
				// +4の位置
				while (mml_int_stack_pop(&handle->repeatNAdrs, &handle->obj)!=MML_STACK_RESULT_ERROR_EMPTY) {
					handle->obj+=2;
					mr=storeMMLWord(handle,ofst+4);
					if (mr!=MML_RESULT_OK) return mr;
				}
				handle->obj=ofst; // 元に戻す
				handle->prevRepeat=0; // 開放
			}

			nslog(@"戻り位置とループ回数を読み出し");
			int loopCount;
			if (mml_int_stack_pop(&(handle->cnt), &loopCount)   != MML_STACK_RESULT_OK) {
				printf("MML_RESULT_STACK_EMPTY_REPEAT_START\n");
				return MML_RESULT_STACK_EMPTY_REPEAT_START;
			}
			int jump;
			if (mml_int_stack_pop(&(handle->startAdrs), &jump)   != MML_STACK_RESULT_OK) {
				printf("MML_RESULT_STACK_EMPTY_REPEAT_START\n");
				return MML_RESULT_STACK_EMPTY_REPEAT_START;
			}
			mr=storeMMLCommand3Adrs(handle, MML_COM_LOOP, loopCount, jump,0,0);
			if (mr != MML_RESULT_OK) return mr;
		}
			break;
			
		case MML_CHAR_TYPE_JUMP_START:
		{
			handle->prevNote=0; // 直前はNOTEではない
			nslog(@"ジャンプ系");
			// ジャンプ系は内部処理だけなのでcallbackはなし
			
			// handleは'の次の文字を指している
			// []の中を読み取る
			mr=get_user_event_token(handle, buf2, sizeof(buf2),'[',']');
			if (mr!=MML_RESULT_OK) {
				return mr;
			}
			nslog(@"jump:%s",buf2);
			// ジャンプ系は、中間コードレベルでは識別する必要がない
			if (strcasecmp(buf2,"D.C.")==0) {
				nslog(@"先頭に戻す->$%04x", handle->objTrueTop);
				mr=storeMMLCommand2Adrs(handle, MML_COM_JUMP, 0, handle->objTrueTop);
				if (mr != MML_RESULT_OK) return mr;
				break;
			}
			//------------
			if (strcasecmp(buf2,"D.S.")==0) {
				nslog(@"[SEGNO]に戻す");
				if (handle->segno==0) {
					nslog(@"[SENGNO]の指定がない");
					return MML_RESULT_NO_SEGNO;
				}
				mr=storeMMLCommand2Adrs(handle, MML_COM_JUMP, 0, handle->segno);
				if (mr != MML_RESULT_OK) return mr;
				handle->segno=0; // 使ったので開放
				break;
			}
			if (strcmp(buf2,"$")==0||strcasecmp(buf2,"SEGNO")==0) {
				if (handle->segno!=0) {
					nslog(@"[SENGNO]の指定が多重にある");
					return MML_RESULT_ILLEGAL_SEGNO;
				}
				// オブジェクト位置記録のみ
				handle->segno=handle->obj;
				// デバッグ用
				mr=storeMMLCommand1(handle, MML_COM_NOP, MML_NOP_SEGNO);
				if (mr != MML_RESULT_OK) return mr;
				break;
			}
			//------------
			if (strcasecmp(buf2,"LOOP")==0) {
				nslog(@"[DO]に戻す");
				if (handle->doloop==0) {
					nslog(@"[DO]の指定がない");
					return MML_RESULT_NO_DO;
				}
				mr=storeMMLCommand2Adrs(handle, MML_COM_JUMP, -1, handle->doloop);
				if (mr != MML_RESULT_OK) return mr;
				handle->doloop=0; // 使ったので開放
				break;
			}
			if (strcasecmp(buf2,"DO")==0) {
				if (handle->doloop!=0) {
					nslog(@"[DO]の指定が多重にある");
					return MML_RESULT_ILLEGAL_DO;
				}
				// オブジェクト位置記録のみ
				handle->doloop=handle->obj;
				// デバッグ用
				mr=storeMMLCommand1(handle, MML_COM_NOP, MML_NOP_DO);
				if (mr != MML_RESULT_OK) return mr;
				break;
			}
			//------------
			if (strcmp(buf2,"*")==0||strcasecmp(buf2,"TOCODA")==0) {
				nslog(@"CODAに飛ばす");
				int ofst=handle->coda;
				if (ofst==0) {
					nslog(@"まだCODAはないので、見つかり次第書き込む準備");
					handle->storeToCoda=handle->obj; // コマンドを書き込むアドレスを記憶
					// 今回のコマンド書き込みはジャンプ先未定のまま
				} else {
					nslog(@"すでに設定があるのでそこへ飛ぶ");
					handle->coda=0; // 使ったので開放
				}
				mr=storeMMLCommand2Adrs(handle, MML_COM_JUMP, 0,ofst);
				if (mr != MML_RESULT_OK) return mr;
				break;
			}
			if (strcasecmp(buf2,"CODA")==0) {
				if (handle->coda!=0) {
					nslog(@"[CODA]の指定が多重にある");
					return MML_RESULT_ILLEGAL_CODA;
				}
				// デバッグ用
				mr=storeMMLCommand1(handle, MML_COM_NOP, MML_NOP_CODA);
				if (mr != MML_RESULT_OK) return mr;
				
				if (handle->storeToCoda!=0) {
					nslog(@"先にTOCODAがあったのでそこを確定する");
					int ofst=handle->obj; // 現在の位置を一旦保存
					handle->obj=handle->storeToCoda; // ここへ書き込む
					mr=storeMMLCommand2Adrs(handle, MML_COM_JUMP, 0,ofst);
					if (mr != MML_RESULT_OK) return mr; // 書き換えなのでエラーは出ないはずだけど
					handle->obj=ofst; // 戻す
					handle->storeToCoda=0; // 開放
				} else {
					nslog(@"次のTOCODAで書き込みアドレスを保存する");
					handle->coda=handle->obj;
				}
				break;
			}
			//------------
			if (strcasecmp(buf2,"FINE")==0||strcmp(buf2,"^")==0) {
				mr=storeMMLCommand1(handle, MML_COM_FINE, 0);
				if (mr != MML_RESULT_OK) return mr;
				break;
			}
			if (strcasecmp(buf2,"END")==0) {
				mr=storeMMLCommand1(handle, MML_COM_END, 0);
				if (mr != MML_RESULT_OK) return mr;
				break;
			}
			//------------
			if (strcasecmp(buf2,"!")==0) {
				nslog(@"[!];コンパイルスキップ");
				handle->jumpCnt++; // ==1の間のみコンパイル結果のストアをスキップする
				break;
			}
			nslog(@"MML_RESULT_ILLEGAL_SEQUENCE_FOUND(%d):%@",__LINE__,@(buf2));
			return MML_RESULT_ILLEGAL_SEQUENCE_FOUND;
		}

		case MML_CHAR_TYPE_TIE:
			nslog(@"タイがあった");
			// 直前の発声コマンドをタイ型に変更する
			// 和音の場合、最後の音符が対象になる
			if (handle->prevNote==0) {
				nslog(@"直前はNOTEではない");
				return MML_RESULT_ILLEGAL_TIE;
			}
			storeMML1ByteChange(handle, handle->prevNote,MML_COM_NOTET); // 直前のNOTEをタイ型に変更する
			handle->prevNote=0; // 開放
			break;
			
			
		// 単独で出くるとエラーな文字
		case MML_CHAR_TYPE_FLAT:
		case MML_CHAR_TYPE_NUMBER:
		case MML_CHAR_TYPE_SHARP:
		case MML_CHAR_TYPE_UNKNOWN:
			/*
			 * This is a illegal case of the parsing because these characters are needed a command.
			 */
			nslog(@"MML_RESULT_ILLEGAL_SEQUENCE_FOUND(%d):%@",__LINE__,@(buf1));
			return MML_RESULT_ILLEGAL_SEQUENCE_FOUND;
	}
	return mr;
}

//----------------------------------------------------------
// 基本テンポをbticksで設定するので、
// (1)mml_tempoまたはmml_compile_init
// (2)mml_compile
// の順で呼び出さないと正しい速度で演奏されない。

MML_RESULT mml_compile_init(MML *handle)
{
	printFunc();
	handle->option.octave = MML_OPTION_DEFAULT_OCTAVE;
	handle->option.length = MML_OPTION_DEFAULT_LENGTH;
	handle->option.bticks = calcTempo2Tick(MML_OPTION_DEFAULT_TEMPO);
	handle->objTop=NULL;
	handle->comTop=NULL;

	return MML_RESULT_OK;
}

// テンポ設定
// tempo=MML_MIN_TEMPO〜MML_MAX_TEMPO
MML_RESULT mml_tempo(MML *handle,int tempo)
{
	printFunc();
	if (tempo<MML_MIN_TEMPO) tempo=MML_MIN_TEMPO;
	if (tempo>MML_MAX_TEMPO) tempo=MML_MAX_TEMPO;
	// bticksはL4の1分間の音符数
	handle->option.bticks=calcTempo2Tick(tempo);
	nslog(@"テンポ:%d->bticks:%d",tempo,handle->option.bticks);
	return MML_RESULT_OK;
}

MML_RESULT mml_compile(MML *handle,char *mmlSource)
{
	printFunc();
	if (*mmlSource==0) {
		nslog(@"MMLの内容がない");
		return MML_RESULT_NO_MML;
	}
	nslog(@"mml=%@",@(mmlSource));
	
	// 初期コンパイラオブジェクト格納領域確保
	// MMLオブジェクトは確保したまま外部に渡すのでmalloc2()
	handle->objSize=(int)strlen(mmlSource)/2; // ソースの半分にしておく
	if ((handle->objTop=malloc2(handle->objSize))==NULL) {
		return MML_RESULT_NOMEMORY;
	} else {
		// 初期コメント格納領域確保
		// コメント領域はこのコンパイル処理の中で開放されるのでmalloc()でよい
		handle->comSize=MML_COMMENT_SIZE_STEP;
		if ((handle->comTop=malloc(handle->comSize))==NULL) {
			free2(handle->objTop);
			return MML_RESULT_NOMEMORY;
		}
		// 最終的にコメント領域はコンパイラオブジェクトの中に入れられる
	}
	handle->obj =0;
	handle->comp=0;
	handle->realComSize=0;
	
	TEXT_SET_POINTER(handle, mmlSource);
	handle->top=mmlSource;	// 先頭位置保存
	// ジャンプ系の位置(オフセット)
	handle->prevRepeat=		// 1つ前の|nの位置
	handle->doloop=			// DO位置
	handle->storeToCoda=	// TOCODA書き込み位置
	handle->coda=			// CODA位置
	handle->segno=0;		// SEGNO位置
	
	handle->jumpCnt=0;			// [!]の出現回数
	handle->frenpu=RENPU_NONE;	// 連符
	handle->fwaon=NO;			// 和音モード
	
	mml_int_stack_init(&(handle->startAdrs  ));
	mml_int_stack_init(&(handle->cnt        ));
	mml_int_stack_init(&(handle->repeatNAdrs));
	
	// デフォルト値ストア
	handle->option.octave 	= MML_OPTION_DEFAULT_OCTAVE;
	handle->option.length	= MML_OPTION_DEFAULT_LENGTH;
	
	memcpy(handle->objTop,MML_HEADER,SZ_MML_HEADER); // データ識別用
	handle->obj+=SZ_MML_HEADER;
	storeMMLLong(handle,0); // 次に0を入れる；後でコメント領域へのオフセットを入れるから
	storeMMLCommand1(handle,MML_COM_OCTAVE,handle->option.octave);
	storeMMLCommand1(handle,MML_COM_SOUND ,MML_OPTION_DEFAULT_SOUND);
	storeMMLCommand1(handle,MML_COM_RATIO ,MML_OPTION_DEFAULT_RATIO);
	// パンポット、音量は外部で設定可能なのでここでは初期値をストアしない
	// 先頭への書き込みなのでここではエラーは発生しない

	// 実質先頭位置を記録
	handle->objTrueTop=handle->obj;
	nslog(@"実質先頭位置=%04x(%d)",handle->objTrueTop,handle->objTrueTop);
	
	
	// コンパイル開始
	MML_RESULT mr=MML_RESULT_OK;
	do {
		mr=mml_compile1(handle);
	} while (mr==MML_RESULT_OK);
	if (mr<0) {
		nslog(@"エラー発生:%d",mr);
	}
	
	// 終端を記録
	// エラー発生時もダンプできるように
	// メモリー系エラーが発生しているときはここでも再度エラーが発生するので問題ない
	storeMMLCommand1(handle,MML_COM_END, 0); // ここでエラーが出ても無視(エラーでも書き込む必要があるから)

#ifdef DEBUG //!!test!!debug!!
	{
		FILE *fp=fopen(CTESTDIR"comment.dat","wb");
		if (fp!=NULL) {
			fwrite(handle->comTop,handle->comSize,1,fp);
			fclose(fp);
		}
	}
#endif
	
	nslog(@"realComSize=%d/mr=%d",handle->realComSize,mr);
	if (mr==MML_RESULT_EOT) {
		// コメント領域を結合する
		if (handle->realComSize>0) {
			// 実際にコメントが有るときのみ
			if (handle->obj+handle->realComSize>handle->objSize) {
				// 入れるだけの余裕が無い時は先に広げる
				// コメント結合ではMML_MAX_OBJECTを超えても構わない
				int dsize=(handle->obj+handle->realComSize)-handle->objSize;
				nslog(@"残り必要サイズ=%d",dsize);
				handle->objSize+=dsize;
				char *newP;
				if ((newP=realloc(handle->objTop, handle->objSize))==NULL) {
					// 領域が広げられない
					free(handle->comTop);
					handle->comTop=NULL; // 開放した印
					return MML_RESULT_NOMEMORY;
				}
				handle->objTop=newP;
			} else {
				nslog(@"現在の領域に入り込む");
				// オブジェクトのサイズを調整
				handle->objSize=handle->obj+handle->realComSize;
			}
			// コメント内容のコピー
			memcpy(&handle->objTop[handle->obj],handle->comTop,handle->realComSize);
			// コメント領域へのオフセットを記録
			int ofst=handle->obj;
			handle->obj=SZ_MML_HEADER; // もう使わないからそのまま変更
			storeMMLLong(handle,ofst); // ここも先頭領域なのでエラーは出ない
		}
		// 外部に返すリターン値としてはこれで返す
		mr=MML_RESULT_OK;
	}
	// コメント領域を開放
	free(handle->comTop);
	handle->comTop=NULL; // 開放した印
	
#ifdef DEBUG //!!test!!debug!!
	{
		FILE *fp=fopen(CTESTDIR"mml.dat","wb");
		if (fp!=NULL) {
			fwrite(handle->objTop,handle->objSize,1,fp);
			fclose(fp);
		}
	}
#endif
	
	// オブジェクト領域はエラーであっても自動開放されないので、別途mml_compiler_free()またはfree2()で開放すること
	nslog(@"コンパイル終了");
	return mr;
}

// オブジェクトのアドレスを得る
char *mml_compile_object(MML *handle,int *size)
{
	if (size!=NULL) {
		*size=handle->objSize;
	}
	return handle->objTop;
}

// オブジェクトの開放
void mml_compile_free(MML *handle)
{
	printFunc();
	if (handle->objTop!=NULL) { // 念のため
		char *p=handle->objTop;
		for (int i=0;i<SZ_MML_HEADER;i++) *p++=0; // 識別子を消しておく
		free2(handle->objTop);
	}
}
// objTopからの領域は単なるmalloc2()で確保された領域なので、その開放はfree2()で構わない

//----------------------------------------------------------

// 音階文字列
const char *scale_string[MUSIC_SCALES]={
	"C",	// 基準
	"C#",
	"D",
	"D#",
	"E",
	"F",
	"F#",
	"G",
	"G#",
	"A",	// 基準
	"A#",
	"B"
};

//----------------------------------------------------------

// iOS版の中ではoctaveとscaleで処理されるので変換のため
int numberToOctaveAndScale(int number,int *scale)
{
	*scale=number%MUSIC_SCALES;
	return number/MUSIC_SCALES; // オクターブを返す
}

//----------------------------------------------------------

// オブジェクトのダンプ（デバッグ用）
MML_RESULT mml_compile_dump(MML *handle)
{
	char *p=handle->objTop;
	if (p==NULL) {
		nslogA(@"データがない");
		return MML_RESULT_EOT;
	}

	int adrs=0;
	
	// 識別子チェック
	if (memcmp(p,MML_HEADER,SZ_MML_HEADER)!=0) {
		nslogA(@"識別子がない=データがおかしい");
		return MML_RESULT_ILLEGAL_DATA;
	}
	p   +=SZ_MML_HEADER;
	adrs+=SZ_MML_HEADER;
	
	unsigned char com,val,wk1,wk2;
	// コメント領域へのオフセットを得ておく
	int ofst;
	UWord *wp=(UWord *)p;
	ofst=*wp++;
	ofst|=((*wp++)<<16);
	char *commentTop=handle->objTop+ofst;
	adrs+=SZ_WORD*2;
	
	MML_RESULT mr=MML_RESULT_OK;
	
	p=(char *)wp;
	do {
		if (adrs==handle->objTrueTop) {
			// 実質先頭
			fprintf(stdout,"-------------------\n");
		}
		fprintf(stdout,"C%04x ",adrs);
		com=*p++;
		val=*p++;
		wp=(UWord *)p;
		adrs+=2;

		int w;
		switch (com) {
			case MML_COM_NOP:
			{
				char  *jums[]={"","|:","SEGNO","CODA","DO"};
				fprintf(stdout,"[%c:%02x]No Operation:%s",com,val,jums[val]);
			}
				break;
				
			case MML_COM_RATIO:
				fprintf(stdout,"[%c:%02x]発声割合:%d",com,val,val);
				break;
			case MML_COM_SOUND:
				fprintf(stdout,"[%c:%02x]音色:%d",com,val,val);
				break;
			case MML_COM_PANPOT:
				fprintf(stdout,"[%c:%02x]パンポット:%d",com,val,val);
				break;
			case MML_COM_VOLUME:
				fprintf(stdout,"[%c:%02x]音量:%d",com,val,val);
				break;
			case MML_COM_OCTAVE:
				fprintf(stdout,"[%c:%02x]オクターブ:%d",com,val,val);
				break;
			case MML_COM_TEMPO:
				fprintf(stdout,"[%c:%02x]テンポ:%d",com,val,val);
				break;
			case MML_COM_WAON:
				fprintf(stdout,"[%c:%02x]和音:%d",com,val,val);
				break;
			case MML_COM_FINE:
				fprintf(stdout,"[%c:%02x]FINE",com,val);
				break;
			case MML_COM_END:
				fprintf(stdout,"[%c:%02x]終了",com,val);
				break;
			//
			case MML_COM_NOTET :
			case MML_COM_NOTE:
				w=*wp++;
				p+=2;
				adrs+=2;
				int scale;
				int octave=numberToOctaveAndScale(val,&scale);
				fprintf(stdout,"[%c:%02x:%5d]音%s:オクターブ%d,%s,ticks=%d",com,val,w
						,(com==MML_COM_NOTET ? "(タイ付き)":"")
						,octave,scale_string[scale],w);
				break;
			case MML_COM_REST:
				w=*wp++;
				p+=2;
				adrs+=2;
				fprintf(stdout,"[%c:%02x:%5d]休符:%d,ticks=%d",com,val,w, val,w);
				break;
				
			case MML_COM_COMMENT:
				w=*wp++;
				p+=2;
				adrs+=2;
				fprintf(stdout,"[%c:%02x:%04x]コメント:%s",com,val,w ,&commentTop[w]);
				break;
				
			case MML_COM_LOOP:
				w=*wp++;
				p+=2;
				wk1=*p++;
				wk2=*p++;
				adrs+=4;
				fprintf(stdout,"[%c:%02x:%04x:%02x:%02x]ループ:回数=%d,adrs=$%04x,現在回数=%d,wk2=%d",com,val,wk1,wk2,w ,val,w,wk1,wk2);
				break;
				
			case MML_COM_JUMP:
				w=*wp++;
				p+=2;
				adrs+=2;
				fprintf(stdout,"[%c:%02x:%04x]ジャンプ:adrs=$%04x",com,val,w ,w);
				if (val<0) fprintf(stdout,"(無限ループ)");
				break;

			case MML_COM_NEJUMP:
			{
				w=*wp++; // カウンターadrs
				int w2=*wp++; // ジャンプadrs
				p+=4;
				adrs+=4;
				// pはwの位置
				fprintf(stdout,"[%c:%02x:%04x:%04x]!=ジャンプ:カウンターadrs=$%04x,ジャンプadrs=$%04x",com,val,w,w2 ,w,w2);
			}
				break;

			default:// 通常ありえないけど念のため
				nslogA(@"不正なコマンド:%c(%02x)",com,com);
				mr=MML_RESULT_ILLEGAL_COMMAND;
				break;
		}
		fprintf(stdout,"\n");
	} while (com!=MML_COM_END);
	return mr;
}

//----------------------------------------------------------
