**********************************************
* 正規表現によるパターン照合
* パターンのプリコンパイル
*  Oh!X 1993/4 p119-122
*  Improved 1997 by AIG-Soft
*   エラーメッセージの外部化、[]内の\??対応、マクロ機能
**********************************************
*
	.include	re.mac
	.include	myctype.mac
*
	.xref	__bm_comp
	.xref	ctypetable

**********************************************
* void SetGrepMCSrch(((unchar *)*func)(unsint))
* マクロサーチ関数を設定する
	.offset	4
adrs:		.ds.l	1	* 関数アドレス
*
	.text
	.even
*
	.xdef	_SetGrepMCSrch
_SetGrepMCSrch:
	move.l	adrs(sp),mcmp	* アドレスの正当性は検証しない
	rts

**********************************************
*
__CTYPETABLE__	reg	a4
*
* コンパイル結果を登録する
PUT1	macro	data1
	subq.w	#2,d6
	bcs	toolong
	move.w	data1,(a1)+
	.endm

PUT2	macro	data1,data2
	subq.w	#4,d6
	bcs	toolong
	move.w	data1,(a1)+
	move.w	data2,(a1)+
	.endm
*
* a1直前のd0.wバイトをずらしてnbytesの隙間を作る
INSERT	macro	nbytes
local	loop,done
	subq.w	#nbytes,d6
	bcs	toolong
	movea.l	a1,a5
	addq.l	#nbytes,a1
	movea.l	a1,a6
	move.w	d0,d1
	lsr.w	#1,d1
	subq.w	#1,d1
loop:	move.w	-(a5),-(a6)
	dbra	d1,loop
done:
	.endm
*
* 先読みした正規表現１単位を押し戻す
PBACK	macro
	move.w	d1,d3
	swap.w	d3
	move.w	d0,d3
	.endm
*
* 全角文字読みだし
GZEN	macro	dreg
	lsl.w	#8,dreg
	move.b	(a0)+,dreg
	.endm
*
*
* int _re_comp(unchar *pat,unshort *work,unshort *workend)
* ret(d0.l)=ワークエリア中の利用バイト数
* d0.l>0 : ok  , a0=ワークエリア先頭=work
* d0.l<0 : err , a0=不定
*
* _re_compの引数構造
	.offset	4
pat:		.ds.l	1	* パターン
work:		.ds.l	1	* 変換結果格納領域先頭
workend:	.ds.l	1	* 　　　〃　　　　末尾
*
	.text
	.even
*
	.xdef	__re_comp
__re_comp:
SAVREGS	reg	d1-d7/a1-a6
SAVSIZ	=	(7+6)*4
	movem.l	SAVREGS,-(sp)
	move.l	sp,d7			* sp保存
	movem.l	pat+SAVSIZ(sp),a0-a2
	tst.b	(a0)			* pat=""?
	beq	nopat			* Yes -> Err
	move.l	a2,d6
	sub.l	a1,d6			* d6=work size（残り）
	bcs	nomem
	moveq.l	#0,d3
	moveq.l	#0,d4
	moveq.l	#0,d5
	PUT1	d5
	cmpi.l	#$0000_8000,d6
	bcs	noshrink
	move.w	#$7fff,d6
noshrink:
	lea.l	ctypetable,a4
	bsr	orcomp
	tst.w	d0
	bne	unmatchedparen
	PUT1	d0
	movea.l	work+SAVSIZ(sp),a0
	moveq.l	#0,d4
	lea.l	reNFA(a0),a2
headloop:
	move.w	(a2)+,d1
	beq	setscanmode
	cmpi.w	#reLITERAL,d1
	beq	foundhead
	cmpi.w	#reCARET,d1
	beq	setbolmode
	bcs	setscanmode
	cmpi.w	#reLPAREN,d1
	bcs	headloop
	cmpi.w	#reREFER,d1
	bcc	setscanmode
	addq.w	#2,a2
	bra	headloop
setbolmode:
	ori.w	#reBOLSCAN,d4
	bra	headloop
*
foundhead:
	lea.l	-256(sp),sp	* スタック255バイト分（以上）を確保
	movea.l	sp,a3
	move.w	#255-1,d3	* _bm_compは255バイトまで対応だから
makestrloop:
	move.w	(a2)+,d2
	bpl	makestrhan
	subq.w	#1,d3
	bls	makestrbreak
	move.b	-2(a2),(a3)+
makestrhan:
	move.b	d2,(a3)+
	cmp.w	(a2)+,d1
	dbne	d3,makestrloop
makestrbreak:
	sf.b	(a3)
	pea.l	(sp)
	jsr	__bm_comp
	lea.l	256+4(sp),sp	* 確保したスタックを解放(+4 for pea)
	ori.w	#reBMSCAN,d4
setscanmode:
	move.w	d4,(a0)
	move.l	a1,d0		* a0=ワーク先頭（コンパイル結果）
	sub.l	a0,d0		* d0=ワーク使用量
compretn:
	movem.l	(sp)+,SAVREGS
	rts
*
* r1|r2|r3のコンパイル
*
orcomp:
	movea.l	a1,a2
	bsr	conccomp
	cmpi.w	#reVBAR,d0
	bne	orretn
	PUT2	#reBIND,d0
	move.l	a1,d0
	sub.l	a2,d0
	INSERT	4
	move.w	#reBRANCH,(a2)+
	move.w	d0,(a2)+
orloop:	movea.l	a1,a2
	bsr	conccomp
	cmpi.w	#reVBAR,d0
	bne	orbreak
	move.l	a1,d0
	sub.l	a2,d0
	addq.w	#4,d0
	move.w	d0,-2(a2)
	PUT2	#reBIND,d0
	INSERT	4
	move.w	#reBRANCH,(a2)+
	move.w	d0,(a2)+
	bra	orloop
orbreak:
	move.l	a1,d2
	sub.l	a2,d2
	move.w	d2,-(a2)
orretn:	rts
*
* r1･r2･r3のコンパイル
*
conccomp:
@@:	bsr	nextchar
	move.w	jumptable(pc,d0.w),d2
	jsr	jumptable(pc,d2.w)
	bra	@b
*
concbreak:
	addq.l	#4,sp
	rts
*
jumptable:
x	=	*
	.dc.w	concbreak-x
	.dc.w	comp_literal-x
	.dc.w	comp_dot-x
	.dc.w	comp_class-x
	.dc.w	comp_class-x
	.dc.w	comp_caret-x
	.dc.w	comp_doller-x
	.dc.w	comp_lbracket-x
	.dc.w	comp_rbracket-x
	.dc.w	comp_group-x
	.dc.w	concbreak-x
	.dc.w	comp_refer-x
	.dc.w	literal_asterisk-x
	.dc.w	literal_plus-x
	.dc.w	literal_qmark-x
	.dc.w	concbreak-x
*
* ^,$,<,>
*
comp_caret:
comp_doller:
comp_lbracket:
comp_rbracket:
	PUT1	d0
	rts
*
* (〜)
*
comp_group:
	movea.l	a1,a3
.ifdef	__GROUPING__
	addq.w	#1,d5
	compi.w	#10+1,d5
	bcs	compgroup0
	moveq.l	#10,d5
compgroup0:
	PUT2	d0,d5
.endif
	movem.l	a2/a3,-(sp)
	bsr	orcomp
	movem.l	(sp)+,a2/a3
	cmpi.w	#reRPAREN,d0
	bne	missingparen
.ifdef	__GROUPING__
	move.w	2(a3),d1
	PUT2	d0,d1
	bset.l	d1,d4
.endif
	bra	comp_repeat
*
* .
*
comp_dot:
	movea.l	a1,a3
	PUT1	d0
	bra	comp_repeat
*
* \1〜\9
*
comp_refer:
.ifdef	__GROUPING__
	bset.l	d1,d4
	bne	comp_literal
	addi.b	#'0',d1
	moveq.l	#reLITERAL,d0
	bra	comp_literal
.endif
*
* 通常文字
*
literal_qmark:
	moveq.l	#'?',d1
	bra	demote
literal_plus:
	moveq.l	#'+',d1
	bra	demote
literal_asterisk:
	moveq.l	#'*',d1
*	bra	demote
demote:
	moveq.l	#reLITERAL,d0
*	bra	compliteral
comp_literal:
	movea.l	a1,a3
	PUT2	d0,d1
comp_repeat:
	bsr	nextchar
	cmpi.w	#reASTERISK,d0
	beq	comp_asterisk
	cmpi.w	#rePLUS,d0
	beq	comp_plus
	cmpi.w	#reQMARK,d0
	beq	comp_qmark
	PBACK
	rts
*
* [〜] , [^〜]
* \??対応(AIG-Soft)
*
NCCMP	macro	dat
	move.w	#dat,-(sp)
	bsr	ncCMPx
	addq.l	#2,sp
	.endm
**
comp_class:
	movea.l	a1,a3
	NCCMP	'^'
	bne	pushclass	* no
	* [^のときはモード反転（範囲以外）
	moveq.l	#reNCLASS,d0
	addq.l	#1,a0		* ^なら１文字だからこれで良い
pushclass:
	PUT2	d0,d0
	
	subi.w	#256/8,d6	* 256bitのテーブルを用意する
	bcs	toolong
	movea.l	a1,a5		* a5=ビットテーブル
	move.l	d3,a6		* d3退避
	
	moveq.l	#0,d0		* ビットテーブルを初期化
	moveq.l	#256/8/4-1,d1	* 256bit/long -1 dbra
@@:	move.l	d0,(a1)+
	dbra	d1,@b

	bsr	nextcharx	* d0.w＝１文字
	beq	missingbracket	* EOSなら"]がない"のエラー
	bra	@f		* 最初は-]が来ても普通文字とみなす
	*
classloop:
	bsr	nextcharx	* d0.w＝１文字
	beq	missingbracket	* EOSなら"]がない"のエラー
	tst.b	fesc		* \??文字？
	bne	@f		* Yes
	cmpi.w	#']',d0		* 終端？（通常文字のみ有効）
	beq	classbreak
@@:	move.w	d0,d1		* 仮に下限＝上限
	NCCMP	'-'		* 範囲指定？
	bne	class2		* No（文字列記）
	* 範囲指定
	addq.l	#1,a0		* skip '-'
	move.l	d0,-(sp)
	bsr	nextcharx
	move.w	d0,d1		* 次の文字(d1.w)＝上限
	move.l	(sp)+,d0
	beq	missingbracket	* EOSなら"]がない"のエラー
	*
	cmp.w	d0,d1		* 下限≦上限を保証する
	bcc	class2		* ok
	exg.l	d0,d1		* 逆なら入れ換え
class2:
	tst.w	d1		* 上限≦$00ffなら
	bpl	han2han		* 半角〜半角
	tst.w	d0		* 下限＞$00ffなら
	bmi	zen2zen		* 全角〜全角
han2zen:
	PUT2	#$8000,d1	* 半角〜全角
	swap.w	d0
	move.w	#$0080,d0
	lsr.w	#8,d1
	bsr	setbitfield
	swap.w	d0
	move.w	#$00ff,d1
han2han:			* 半角〜半角
	bsr	setbitfield
	bra	classloop
*
zen2zen:			* 全角〜全角
	PUT2	d0,d1
	lsr.w	#8,d0
	lsr.w	#8,d1
	bsr	setbitfield
	bra	classloop
*
classbreak:
	move.l	a6,d3
	PUT1	#reTERM		* ]相当
	move.l	a1,d0
	sub.l	a3,d0
	subq.w	#4,d0
	move.w	d0,2(a3)
	bra	comp_repeat
*
* [〜],[^〜]用のビットテーブルを作成する
*
setbitfield:
	move.l	#7,d2
	and.b	d0,d2
	lsr.w	#3,d0
	moveq.l	#7,d3
	and.b	d1,d3
	lsr.w	#3,d1
	cmp.w	d1,d0
	bne	setbitfield0
	move.b	lboundtable(pc,d2.w),d2
	and.b	uboundtable(pc,d3.w),d2
	or.b	d2,0(a5,d0.w)
	bra	setbfretn
*
setbitfield0:
	tst.b	d2
	beq	setbitfield1
	move.b	lboundtable(pc,d2.w),d2
	or.b	d2,0(a5,d0.w)
	addq.w	#1,d0
setbitfield1:
	sub.w	d0,d1
	beq	setbitfield3
	subq.w	#1,d1
setbitfield2:
	st.b	0(a5,d0.w)
	addq.w	#1,d0
	dbra	d1,setbitfield2
setbitfield3:
	move.b	uboundtable(pc,d3.w),d3
	or.b	d3,0(a5,d0.w)
setbfretn:
	rts
*
lboundtable:
	*	 76543210
	.dc.b	%11111111	* 下限のマスク
	.dc.b	%11111110
	.dc.b	%11111100
	.dc.b	%11111000
	.dc.b	%11110000
	.dc.b	%11100000
	.dc.b	%11000000
	.dc.b	%10000000

uboundtable:
	*	 76543210
	.dc.b	%00000001	* 上限のマスク
	.dc.b	%00000011
	.dc.b	%00000111
	.dc.b	%00001111
	.dc.b	%00011111
	.dc.b	%00111111
	.dc.b	%01111111
	.dc.b	%11111111
*
* *,+,?
*
comp_asterisk:
	bsr	nextchar
	cmpi.w	#reASTERISK,d0
	beq	comp_asterisk
	cmpi.w	#rePLUS,d0
	beq	comp_asterisk
	cmpi.w	#reQMARK,d0
	beq	comp_asterisk
	PBACK
	move.l	a1,d0
	sub.l	a3,d0
	beq	asteriskretn
	INSERT	4
	addq.w	#4,d0
	move.w	#reBRANCH,(a3)+
	move.w	d0,(a3)+
	neg.w	d0
	PUT2	#reMAIN,d0
asteriskretn:
	rts
*
comp_plus:
	bsr	nextchar
	cmpi.w	#reASTERISK,d0
	beq	comp_asterisk
	cmpi.w	#rePLUS,d0
	beq	comp_plus
	cmpi.w	#reQMARK,d0
	beq	comp_asterisk
	PBACK
	move.l	a3,d0
	sub.l	a1,d0
	beq	plusretn
	subq.w	#4,d0
	PUT2	#reMAIN,d0
plusretn:
	rts
*
comp_qmark:
	bsr	nextchar
	cmpi.w	#reASTERISK,d0
	beq	comp_asterisk
	cmpi.w	#rePLUS,d0
	beq	comp_asterisk
	cmpi.w	#reQMARK,d0
	beq	comp_qmark
	PBACK
	move.l	a1,d0
	sub.l	a3,d0
	beq	qmarkretn
	INSERT	4
	move.w	#reBRANCH,(a3)+
	move.w	d0,(a3)+
qmarkretn:
	rts
*
*********************************************
* 次の文字を得る
* -> d1.w : 文字
* -> d0.l : 文字種類
* -> fesc : \??かどうか(<>0=Yes)
*********************************************
*
ncCMPx:				* ポインター移動無しで先頭文字の比較
SAVSIZ	=	(1+1)*4
	movem.l	d0/a0,-(sp)
	bsr	nextcharx	* -> d0.w
	tst.b	fesc		* \??による文字？
	bne	@f		* Yes -> 常に不一致(Z=0)
	* \??の文字ではなく通常文字
	cmp.w	4+SAVSIZ(sp),d0	* 比較
@@:	movem.l	(sp)+,d0/a0	* Z=1:一致 ,Z=0:不一致
	rts
*
nextcharx:			* d1.lを破壊せずにメタ文字を普通の文字とする１文字読み取り
	movem.l	d1/a5,-(sp)	*	-> d0.w
	st.b	fmeta		* メタ文字を普通の文字とする
	bsr	nextchar1	* １文字読みだし
	exg.l	d0,d1		* d0とd1入れ換え(d0.w=文字,d1.l=状態)
	cmp.l	#reTERM,d1	* EOS?
	bne	@f		* No(Z=0)
	moveq.l	#0,d0		* EOSのときはd0.w=0(Z=1)にする
@@:	movem.l	(sp)+,d1/a5	* フラグを壊さないため
	rts

nextchar:
	move.w	d3,d0		* 押し戻された文字があれば...
	beq	nextchar0	* ない
	swap.w	d3		* それを返す
	move.w	d3,d1
	moveq.l	#0,d3
	rts
*
nextchar0:
	sf.b	fmeta		* メタ文字判定する
nextchar1:
	sf.b	fesc		* \??文字でない
	moveq.l	#0,d1
	move.b	(a0)+,d1
	beq	eos		* EOS
	BHAN	d1,nextcharhan	* 半角文字?
zenliteral:			* 全角文字
	GZEN	d1		* 全角文字読み取り
	beq	eos		* ２バイト目がEOS
literal:
	* ﾏｸﾛﾁｪｯｸ
	tst.b	fmacro
	bne	@f		* すでにﾏｸﾛ中
	tst.b	fesc
	bne	@f		* \??の文字はﾏｸﾛ対象にしない
	tst.b	fmeta
	bne	@f		* [〜]内の文字はﾏｸﾛ対象にしない
	move.l	mcmp,d0
	beq	@f		* ﾏｸﾛ関数が設定されていない
	movem.l	d2/a0-a2,-(sp)	* Ｃからも呼び出せるように(d0は破壊してもいい,d1は下で保存)
	move.l	d1,-(sp)	* 文字を退避
	move.l	d0,a5		* a5=mcmp
	jsr	(a5)		* d0.l>0=文字列アドレス , <0=この文字はこのまま
	move.l	(sp)+,d1	* 前の文字を復帰
	movem.l	(sp)+,d2/a0-a2
	tst.l	d0
	bmi	@f		* ﾏｸﾛにしない
	move.l	a0,moto		* pat読みだし位置保存
	move.l	d0,a0		* マクロ文字列を設定
	st.b	fmacro		* macro on
	bra	nextchar1	* マクロモードで文字読み出しを開始する
	*
@@:	moveq.l	#reLITERAL,d0
	rts
*
eos:	tst.b	fmacro		* ﾏｸﾛ中？
	beq	@f		* NO
	* ﾏｸﾛ中
	move.l	moto,a0		* 元のpat読み出しに戻る
	sf.b	fmacro		* macro off
	bra	nextchar1
	*
@@:	subq.l	#1,a0		* (a0)=$00
	moveq.l	#reTERM,d0
	rts
*
nextcharhan:			* 半角文字
	cmpi.b	#'\',d1
	beq	getesctype	* -> \??の文字である
	* \の付かない文字
	tst.b	fmeta		* メタ文字を普通文字とする？
	bne	literal		* Yes
	* メタ文字？
	lea.l	syntaxtable1(pc),a5
@@:	move.w	(a5)+,d0
	beq	literal		* 通常文字
	cmp.b	d0,d1
	bne	@b
	* 一致
	lsr.w	#8,d0		* メタ文字 : char|type
	rts
*
syntaxtable1:	* \なしメタ文字テーブル
	.dc.b	reDOT,'.'
	.dc.b	reASTERISK,'*'
	.dc.b	rePLUS,'+'
	.dc.b	reQMARK,'?'
	.dc.b	reCARET,'^'
	.dc.b	reDOLLER,'$'
	.dc.b	reCLASS,'['
	*
	.dc.b	reLPAREN,'('
	.dc.b	reRPAREN,')'
	.dc.b	reVBAR,'|'
	.dc.b	reLBRACKET,'<'
	.dc.b	reRBRACKET,'>'
	.dc.b	0,0
*
getesctype:			* \??の文字
	move.b	(a0)+,d1	* ?の後ろの文字
	beq	illegalesc	* ?の後ろがない
	st.b	fesc		* \??文字である
	BZEN	d1,zenliteral	* \全角？ -> 全角文字として返す
.ifdef	__GROUPING__
	ISDIGIT	d1
	beq	notdigit
	moveq.l	#'0',d0
	cmp.b	d0,d1
	beq	literal		* \0 -> 0（常時）
	tst.b	fmeta		* メタ文字を普通文字とする？
	bne	literal		* Yes , \1〜\9 -> 1〜9
	sub.b	d0,d1		* '1' -> 1
	moveq.l	#reREFER,d0
	rts
notdigit:
.endif
	* \を使うメタ文字？
	lea	syntaxtable2(pc),a5
@@:	move.w	(a5)+,d0
	beq	getescchar	* メタ文字にはならない
	cmp.b	d0,d1
	bne	@b
	* 一致
	tst.b	fmeta		* メタ文字を普通文字とする？
	bne	literal		* Yes
	lsr.w	#8,d0		* メタ文字 : char|type
	rts
*
syntaxtable2:	* \ありメタ文字テーブル
*	.dc.b	reLPAREN,'('
*	.dc.b	reRPALEN,')'
*	.dc.b	reVBAR,'|'
*	.dc.b	reLBRACKET,'<'
*	.dc.b	reRBRACKET,'>'
	.dc.b	0,0
*
getescchar:			* ?nn文字
	cmpi.b	#'x',d1		* \x?? = 16進数	?
	beq	hexesc		* Yes
	* \? = コントロール文字かも
	lea.l	esctable(pc),a5
@@:	move.w	(a5)+,d0
	beq	literal		* 普通文字(\? -> ?)
	cmp.b	d0,d1
	bne	@b
	move.b	-2(a5),d1	* 変換後の文字そのもの
	bra	literal
*
esctable:
	.dc.b	'G'-'@','a'	* \a = ^G	Ｃと同じもの
	.dc.b	'H'-'@','b'	* \b = ^H	〃
	.dc.b	'['-'@','e'	* \e = ^[	〃
	.dc.b	'L'-'@','f'	* \f = ^L	〃
	.dc.b	'J'-'@','n'	* \n = ^J	〃
	.dc.b	'M'-'@','r'	* \r = ^M	〃
	.dc.b	'I'-'@','t'	* \t = ^I	〃
	.dc.b	'K'-'@','v'	* \v = ^K	〃
	.dc.b	' ','s'		* \s = SPC	拡張
	.dc.b	0,0
*
hexesc:	* \xnn=16進文字
	moveq.l	#0,d1
	move.b	(a0)+,d0
	beq	illegalhexesc
	ISHEX	d0
	beq	illegalhexesc
hexescloop:
	ori.b	#$20,d0		* 英小文字化
	subi.b	#'0',d0
	cmpi.b	#10,d0
	bcs	addhex
	subi.b	#'a'-'0'-10,d0
addhex:
	lsl.w	#4,d1
	add.b	d0,d1
	move.b	(a0)+,d0
	beq	hexescretn
	ISHEX	d0
	bne	hexescloop	* 一応ANSI C風に無限長の16進数を許す
hexescretn:
	subq.l	#1,a0
	moveq.l	#reLITERAL,d0
	rts
*
* エラー
*
missingbracket:
	moveq.l	#-1,d0
	bra	errretn
*
unmatchedparen:
	moveq.l	#-2,d0
	bra	errretn
*
missingparen:
	moveq.l	#-3,d0
	bra	errretn
*
toolong:
	moveq.l	#-4,d0
	bra	errretn
*
illegalesc:
	moveq.l	#-5,d0
	bra	errretn
*
illegalhexesc:
	moveq.l	#-6,d0
	bra	errretn
*
nomem:
	moveq.l	#-7,d0
	bra	errretn
*
nopat:
	moveq.l	#-8,d0
errretn:
	tst.b	fmacro		* ﾏｸﾛ中？
	beq	@f		* No
	sub.l	#100,d0		* -1〜-8 -> -101〜-108 : ﾏｸﾛ中のエラーの印
@@:	movea.l	d7,sp		* spを戻す
	bra	compretn

**********

	.data
	.even
mcmp:	.dc.l	0		* ﾏｸﾛ判定関数(0=マクロ無効,<>0=マクロ関数アドレス)
fmacro:	.dc.b	0		* ﾏｸﾛモード中(0=No,<>0=Yes)
*
	.bss
	.even
fesc:	.ds.b	1		* \xnn,\?による文字かどうか(0=No,<>0=Yes)
				* []内で\??で^-]が作られた時に一致させないためのフラグ
fmeta:	.ds.b	1		* メタ文字を普通文字にするかどうか(0=No,<>0=Yes)
moto:	.ds.l	1		* ﾏｸﾛに入る前のpat読みだしアドレス
	.end
