; Parsing macros v.1.1
; by Massimiliano Tomassoli, 2009

include "parsing.inc"

; We can use readSuccess to see if there is a match.

;-----------------------------------------------------------------------------
; Example 1
;-----------------------------------------------------------------------------

@rule1 equ push~eax
translateRules rule1

curExpr equ push eax

matchExpr rule1, 
match =1 , readSuccess
{
	display "match1!",13,10
}

;-----------------------------------------------------------------------------
; Example 2
;-----------------------------------------------------------------------------

@rule1 equ (mov|push)(eax|ebx)
translateRules rule1
irp expr, push eax, push ebx, mov eax, mov ebx
{
	curExpr equ expr

	matchExpr rule1, 
	\match =1 , readSuccess
	\{
		display "match2!",13,10
	\}
}

;-----------------------------------------------------------------------------
; Example 3
;-----------------------------------------------------------------------------

@rule1 equ (mov eax /, _S_) | (push~ebx)
translateRules rule1

curExpr equ mov eax, 654h

matchExpr rule1, 
match =1 , readSuccess
{
	display "match3!",13,10
}

;-----------------------------------------------------------------------------
; Example 4
;-----------------------------------------------------------------------------

; Escaping.
@rule1 equ mov /~ // /~ eax
translateRules rule1

curExpr equ mov ~ /~eax, 654h

matchExpr rule1,
match =1 , readSuccess
{
	display "match4!",13,10
}

;-----------------------------------------------------------------------------
; Example 5
;-----------------------------------------------------------------------------

; Data retrieval.
@rule1 equ (add|sub):instr ~ _S_:destOp ~ /, ~ _S_:srcOp
translateRules rule1

curExpr equ add eax, 654h

matchExpr rule1, <instr, destOp, srcOp>
match =1 , readSuccess
{
	display "match5: "
	\match	a|b|c , var@instr|var@destOp|var@srcOp
	\{
		display \`a," ",\`b,",",\`c,13,10
	\}
}
restore var@instr, var@destOp, var@srcOp

;-----------------------------------------------------------------------------
; Example 6
;-----------------------------------------------------------------------------

; Extracts the symbols "tree", "oak", "pine", "/" and "," from the expression preserving
; the order in which they appear.
@rule1 equ ((_S_ _notin_ {tree,oak,pine,/,<,>})* _S_>wordList)*
translateRules rule1

curExpr equ armchair tree relax sun ,. | &/8 563 sunday pine dog,oak,,,fridge...

matchExpr rule1, wordList
match =1 wList, readSuccess var@wordList
{
	display "match6: "
	\irp w, wList
	\{
		display \`w," "
	\}
	display 13,10
}
restore var@wordList

;-----------------------------------------------------------------------------
; Example 7
;-----------------------------------------------------------------------------

; Extracts the symbols "tree", "oak", "pine", "/" and "," from the expression preserving
; the order in which they appear (second version
@rule1 equ ((tree|oak|pine|/ /|/,)>wordList* _S_)*
translateRules rule1

curExpr equ armchair tree relax sun ,. | &/8 563 sunday pine dog,oak,,,fridge...

matchExpr rule1, wordList
match =1 wList, readSuccess var@wordList
{
	display "match7: "
	\irp w, wList
	\{
		display \`w," "
	\}
	display 13,10
}
restore wordList

;-----------------------------------------------------------------------------
; Example 8
;-----------------------------------------------------------------------------

@someInstr equ mov|add|sub|xor|test|cmp
@someRegs16 equ ax|bx|cx|dx
@someRegs32 equ eax|ebx|ecx|edx
@someRegs equ {someRegs16}|{someRegs32}
@rule1 equ {someInstr}>__instr {someRegs}>__dest/, {someRegs}>__src
translateRules someInstr, someRegs16, someRegs32, someRegs, rule1

curExpr equ sub eax, ecx

matchExpr rule1, <__instr, __dest, __src>
match =76,var@__instr|var@__dest|var@__src
{
}
display "match8: "
match =1 , readSuccess
{
	display "OK!",13,10
	stripAngBrackets var@__instr, var@__dest, var@__src
;	var@__instr		var@__dest, var@__src
}
match =0 , readSuccess
{
	display "ERROR!",13,10
}
restore var@__instr, var@__dest, var@__src

;-----------------------------------------------------------------------------
; Example 9
;-----------------------------------------------------------------------------

; Let's do some simple recursion.
@rule1 equ NOTOK [{rule1}]			; remember that [x] means that x is optional
translateRules rule1

curExpr equ NOTOK NOTOK NOTOK NOTOK NOTOK OK!

matchExpr rule1, 
match =1 unmExpr, readSuccess curExpr
{
	display "match9: ", `unmExpr,13,10
}

; Something like
;	@algExpr equ /({algExpr}/) | {algExpr}/+{algExpr} | x|y|z
; won't work! (infinite cycle!)

;-----------------------------------------------------------------------------
; Example 10
;-----------------------------------------------------------------------------

; Let's do some simple recursion! (PHASE 1)
@atom		equ a|b|c
@binOpSum	equ {atom}((/+|-){atom})*
@mainRule equ {binOpSum}
translateRules atom, binOpSum, mainRule

curExpr equ a-b+c-a+b OK!

matchExpr mainRule, 
match =1 unmExpr, readSuccess curExpr
{
	display "match10: ", `unmExpr,13,10
}

;-----------------------------------------------------------------------------
; Example 11
;-----------------------------------------------------------------------------

; Let's do some simple recursion! (PHASE 2)
@atom		equ a|b|c|d|e|f
@binOpSum	equ {binOpProd}((/+|-){binOpProd})*
@binOpProd	equ {atom}((/*|//){atom})*			; */ (C++ editor workaround)
@mainRule	equ {binOpSum}
translateRules atom, binOpSum, binOpProd, mainRule

curExpr equ a*b*c + c*d + e*f OK!

matchExpr mainRule, 
match =1 unmExpr, readSuccess curExpr
{
	display "match11: ", `unmExpr,13,10
}

;-----------------------------------------------------------------------------
; Example 12
;-----------------------------------------------------------------------------

; Let's do some simple recursion! (PHASE 3)
@atom		equ a|b|c|d|e|f
@operand	equ /({binOpSum}/) | {atom}				; handles sub-expressions!
@binOpSum	equ {binOpProd}((/+|-){binOpProd})*
@binOpProd	equ {operand}((/*|//){operand})*		; */ (C++ editor workaround)
@mainRule	equ {binOpSum}
translateRules atom, operand, binOpSum, binOpProd, mainRule

;curExpr equ (a*b*c + d +) c*(d+e)*f OK!		; <-- wrong expression!
curExpr equ (a*b*c + d) + c*(d+e)*f OK!			; <-- correct: this works!

matchExpr mainRule, 
match =1 unmExpr, readSuccess curExpr
{
	display "match12: ", `unmExpr,13,10
}

;-----------------------------------------------------------------------------
; Example 13
;-----------------------------------------------------------------------------

; Let's do some simple recursion! (PHASE 4)
@atom		equ a|b|c|d|e|f
@operand	equ /({binOpSum}/) | {atom}				; handles sub-expressions!
@unaryOps	equ {operand} !*
@binOpSum	equ {binOpProd}((/+|-){binOpProd})*
@binOpProd	equ {unaryOps}((/*|//){unaryOps})*		; */ (C++ editor workaround)
@mainRule	equ {binOpSum}
translateRules atom, operand, unaryOps, binOpSum, binOpProd, mainRule

;curExpr equ (a*b*c + d +) c*(d+e)*f OK!			; <-- wrong expression!
curExpr equ (a*b*c + d ! ! !) + c*(d+e)*f OK!		; <-- correct: this works!

; IMPORTANT:
;	! is not a special symbol to FASM, so a! will be recognized as a single symbol.
;	However, (a !) will work just fine because ) is a special symbol and thus !) are
;	always read as separate symbols.

matchExpr mainRule, 
match =1 unmExpr, readSuccess curExpr
{
	display "match13: ", `unmExpr,13,10
}

;-----------------------------------------------------------------------------
; Example 14
;-----------------------------------------------------------------------------

; Let's do some simple recursion! (PHASE 5)
@atom		equ a|b|c|d|e|f
@operand	equ /({binOpSum}/) | {atom}				; handles sub-expressions!
@unaryOps	equ {operand} !*
@binOpSum	equ {binOpProd}((/+|-){binOpProd})*
@binOpProd	equ {defaultOp}((/*|//){defaultOp})*	; */ (C++ editor workaround)
@defaultOp	equ {unaryOps}({unaryOps})*
@mainRule	equ {binOpSum}
translateRules atom, operand, unaryOps, binOpSum, binOpProd, defaultOp, mainRule

;curExpr equ (a*b*c + d +) c*(d+e)*f OK!				; <-- wrong expression!
curExpr equ (a*b*c + d e + d ! ! !) + c(d+e)f OK!		; <-- correct: this works!

; IMPORTANT:
;	! is not a special symbol to FASM, so a! will be recognized as a single symbol.
;	However, (a !) will work just fine because ) is a special symbol and thus !) are
;	always read as separate symbols.

matchExpr mainRule, 
match =1 unmExpr, readSuccess curExpr
{
	display "match14: ", `unmExpr,13,10
}

;-----------------------------------------------------------------------------
; Example 15
;-----------------------------------------------------------------------------

macro cmd@initLocals
{
	var@curData equ
	var@curOp equ
	var@opd2 equ
}

macro cmd@saveLocals
{
	savedCurData equ var@curData
	savedCurOp equ var@curOp
	savedOpd2 equ var@opd2
;	display "saveLocals",13,10
}

macro cmd@updCurData
{
	tmp equ var@curOp var@curData var@opd2
	restore var@curData
	var@curData equ tmp
	restore tmp
;	display "updCurData",13,10
}

macro cmd@updCurDataProd
{
	tmp equ * var@curData var@opd2
	restore var@curData
	var@curData equ tmp
	restore tmp
;	display "updCurData",13,10
}

macro cmd@updCurDataFact
{
	tmp equ ! var@curData
	restore var@curData
	var@curData equ tmp
	restore tmp
;	display "updCurData",13,10
}

macro cmd@restoreLocals
{
	restore var@curData
	var@curData equ savedCurData
	restore var@curOp
	var@curOp equ savedCurOp
	restore var@opd2
	var@opd2 equ savedOpd2

	restore savedCurData
	restore savedCurOp
	restore savedOpd2
;	display "restoreLocals",13,10
}

; Let's do some simple recursion! (PHASE 6: PHASE 1 REVISITED)
@atom		equ a|b|c
@binOpSum	equ <saveLocals>, \
					({atom}:curData ( (/+|-):curOp {atom}:opd2 <updCurData> )* = &curData), \
				<restoreLocals>
@mainRule	equ <initLocals>, {binOpSum}
translateRules atom, binOpSum, mainRule

curExpr equ a+c-b-a OK!

matchExpr mainRule, 
match =1 unmExpr, readSuccess curExpr
{
	display "match15: outData = "
	printStrN outData
}

;-----------------------------------------------------------------------------
; Example 16
;-----------------------------------------------------------------------------

; Let's do some simple recursion! (PHASE 7)
@atom		equ a|b|c|d|e|f
@subExpr	equ <saveLocals>, \
					(/({binOpSum}:curData/) = &curData), \
				<restoreLocals>
@operand	equ {subExpr} | {atom}
@unaryOps	equ <saveLocals>, \
					({operand}:curData (! <updCurDataFact>)* = &curData), \
				<restoreLocals>
@binOpSum	equ <saveLocals>, \
					({binOpProd}:curData ( (/+|-):curOp {binOpProd}:opd2 <updCurData> )* = &curData), \
				<restoreLocals>
@binOpProd	equ <saveLocals>, \
					({defaultOp}:curData ( (/ *|/ /):curOp {defaultOp}:opd2 <updCurData> )* = &curData), \
				<restoreLocals>
@defaultOp	equ <saveLocals>, \
					({unaryOps}:curData ( {unaryOps}:opd2 <updCurDataProd> )* = &curData), \
				<restoreLocals>
@mainRule	equ {binOpSum}
translateRules atom, subExpr, operand, unaryOps, binOpSum, binOpProd, defaultOp, mainRule
;printStrN cmd@defaultOp
;curExpr equ (a*b*c + d +) c*(d+e)*f OK!				; <-- wrong expression!
curExpr equ (a*b*c + d e + d ! ! !) + c(d+e)f OK!		; <-- correct: this works!

; IMPORTANT:
;	! is not a FASM special symbol so a! will be recognized as a single symbol.
;	However, (a !) will work just fine because ) is a special symbol and thus !) are
;	always read as separate symbols.

matchExpr mainRule, 
match =1 unmExpr, readSuccess curExpr
{
	display "match16:",13,10
	display "   outData = "
	printStrN outData
	display "   curExpr = "
	printStrN curExpr
}

;-----------------------------------------------------------------------------
; Example 17
;-----------------------------------------------------------------------------

macro cmd@put string
{
	restore outData
	outData equ string
}

macro cmd@errMod
{
	========== Error: missing modifier after ':' ==========
}

; When efficiency is really important, translations can be skipped and plain commands hardcoded
; (just use printStrSN to print the translations and put them into your source file).
@regs16 	equ ax|bx|cx|dx|si|di
@regs32		equ eax|ebx|ecx|edx|esi|edi
@names		equ _S_ _notin_ {uses, locals, args}
@add2		equ (&regsOff <put +2>):regsOff = _E_		; no output
@add4		equ (&regsOff <put +4>):regsOff = _E_		; no output
@regs		equ {regs16}{add2} | {regs32}{add4}
@uses		equ uses ((<put push> {regs})>push_regs)*
@dim_mod	equ word | dword
@args		equ args (/: {dim_mod}:def_dim | <put dword>:def_dim) \
				( {names} ((/: ({dim_mod}|<errMod>)) | <put :>&def_dim) )>arg_names *
@locals		equ locals (/: {dim_mod}:def_dim | <put dword>:def_dim) \
				( {names} ((/: ({dim_mod}|<errMod>)) | <put :>&def_dim) )>local_names *
@directives	equ _END_ | (({uses} | {args} | {locals}) {directives})
@rule1		equ _S_:proc_name {directives}
translateRules regs16, regs32, names, add2, add4, regs, uses, dim_mod, args, locals, directives, rule1

; stack:
;	arg2
;	arg1
;	eip
;	eax		<-- start saved regs
;	ebx		<-- end saved regs
;	ebp		<-- ebp
;	var1
;	var2	<-- esp
;
;argn = ebp+4+regsSize+4*n
;varn = ebp-4*n
;
; Format:
;	proc procName uses <regs1> ... <regsn> \
;				  locals[:<dimMod>] <var1[:<dimMod>]> ... <varn[:<dimMod>]> \
;				  args[:<dimMod>] <arg1[:<dimMod>]> ... <argn[:<dimMod>]>
; uses, locals, args may appear in any order and as many times as you want.
; Elements withing [ ] are optional.
; <regs1> ... <regsn> is a space-separated list of registers.
; <dimMod> is a dimension modifier i.e. "word" or "dword". A dimMod acts within the context it appears
; (i.e. the current locals or args). By default, "dword" is understood. If locals or args have a dimMod,
; that dimMod becomes the default dim in that context. If some args and vars don't have an explicit dimMod,
; the default one is understood.
;
; Example:
;	proc print uses eax cx dx \		; not recommended, of course
;			   args:word x y text:dword \
;			   locals addr1 addr2 addr3
;			   locals:word save1 save2 save3
macro proc input
{
	; Protects local variables by pushing a dummy value.
;	curExpr equ
	readSuccess equ
	localsOff equ
	__proc_argsOff__ equ
	__proc_names__ equ

;	restore curExpr
	curExpr equ input

	var@push_regs equ
	var@def_dim equ
	var@arg_names equ
	var@local_names equ
	var@regsOff equ
	matchExpr rule1, <push_regs, def_dim, arg_names, local_names, regsOff>
	\match =0 , readSuccess
	\{
		display "Error in parsing",13,10
		printStrSN input
	========== Error in parsing expression! ==========
	\}

	var@proc_name:

	\match expRegs, var@push_regs
	\{
		\\irp push_r, expRegs
		\\{
			push_r
		\\}
	\}

	push	ebp
	mov		ebp, esp

	\match expArgs, var@arg_names
	\{
;		display "args:",13,10
		\\irp args, expArgs
		\\{
;			printStrN args
			\\\match name:=word , args
			\\\{
				tmp equ __proc_names__ name
				restore __proc_names__
				__proc_names__ equ tmp
				restore tmp

				name equ ebp __proc_argsOff__+var@regsOff+8		; +8 for EBP and EIP

				tmp equ __proc_argsOff__+2
				restore __proc_argsOff__
				__proc_argsOff__ equ tmp
				restore tmp
			\\\}
			\\\match name:=dword , args
			\\\{
				tmp equ __proc_names__ name
				restore __proc_names__
				__proc_names__ equ tmp
				restore tmp

				name equ ebp __proc_argsOff__+var@regsOff+8		; +8 for EBP and EIP

				tmp equ __proc_argsOff__+4
				restore __proc_argsOff__
				__proc_argsOff__ equ tmp
				restore tmp
			\\\}
		\\}
	\}

	\match expLocals , var@local_names
	\{
;		display "locals:",13,10
		\\irp locs, expLocals
		\\{
;			printStrN locs
			\\\match name:=word , locs
			\\\{
				tmp equ localsOff-2
				restore localsOff
				localsOff equ tmp
				restore tmp

				tmp equ __proc_names__ name
				restore __proc_names__
				__proc_names__ equ tmp
				restore tmp

				name equ ebp localsOff
			\\\}
			\\\match name:=dword , locs
			\\\{
				tmp equ localsOff-4
				restore localsOff
				localsOff equ tmp
				restore tmp

				tmp equ __proc_names__ name
				restore __proc_names__
				__proc_names__ equ tmp
				restore tmp

				name equ ebp localsOff
			\\\}
		\\}
	\}

	sub		esp, -(localsOff)

	restore var@push_regs
	restore var@def_dim
	restore var@arg_names
	restore var@local_names
	restore var@regsOff

	restore curExpr
	restore readSuccess
	restore localsOff
}

macro retp
{
	mov		esp, ebp
	pop		ebp
	ret		__proc_argsOff__
}

macro endp
{
	restore __proc_argsOff__

	\match expNames , __proc_names__
	\{
		\\irps name , expNames
		\\{
			restore name
		\\}
	\}

	restore __proc_names__
}

; Let's see if our variables are really restored.
curExpr equ OK!
readSuccess equ OK!
localsOff equ OK!
__proc_argsOff__ equ OK!
__proc_names__ equ OK!
var@push_regs equ OK!
var@local_names equ OK!
var@regsOff equ OK!

display "------------------",13,10
display "BEFORE proc...endp",13,10
display "------------------",13,10
printStrN curExpr
printStrN readSuccess
printStrN localsOff
printStrN __proc_argsOff__
printStrN __proc_names__
printStrN x
printStrN y
printStrN text
printStrN addr1
printStrN addr2
printStrN addr3
printStrN save1
printStrN save2
printStrN save3
printStrN var@push_regs
printStrN var@local_names
printStrN var@regsOff

; stack:
;	text			; ebp+14h
;	y|x				; ebp+10h
;	eip
;	eax
;	cx|dx
;	ebp				; ebp
;	addr1			; ebp-4
;	addr2			; ebp-8
;	addr3			; ebp-0ch
;	save1|save2		; ebp-10h
;	save3|pad		; esp-14h
proc print uses eax cx dx \		; not recommended, of course
		   args:word x y text:dword \
		   locals addr1 addr2 addr3 \
		   locals:word save1 save2 save3 pad
	mov		eax, 55555555h		; start
	mov		ax, [x]
	mov		ax, [y]
	mov		eax, [text]
	mov		eax, [addr1]
	mov		eax, [addr2]
	mov		eax, [addr3]
	mov		ax, [save1]
	mov		ax, [save2]
	mov		ax, [save3]
	mov		eax, 0EEEEEEEEh		; end
	retp
endp

display "------------------",13,10
display "AFTER proc...endp",13,10
display "------------------",13,10
printStrN curExpr
printStrN readSuccess
printStrN localsOff
printStrN __proc_argsOff__
printStrN __proc_names__
printStrN x
printStrN y
printStrN text
printStrN addr1
printStrN addr2
printStrN addr3
printStrN save1
printStrN save2
printStrN save3
printStrN var@push_regs
printStrN var@local_names
printStrN var@regsOff
