Original 80x86 assembler


;************************************************************************
;	ParseExpression() - Build expression tree                       *
;************************************************************************
; NOTES:
; 1.    This routine builds an expression tree using standard operator
;       precedence analysis. It is lifted from Bornat, "Understanding and 
;       Writing Compilers" Chpt 17 with various modifications.
;
;       The structure of the tree is different from Bornat; to cater for 
;       multiple arguments, they are held in the form of a linked list of 
;       nodes; nodep->child points to the first argument (if any), with the 
;       arguments held in the linked list sibling->.
;
;                               node
;                                |
;                        +-------+
;                        |
;                        +-->node----->node----->node...
;                             |          | 
;                   +---------+     +----+
;                   |               |
;                   +-->node...     +-->node...
;
;
; 2. On entry:
;	EDI -> environment
;	EAX = priority
;
;    On exit:
;	ESI -> root node

ParseExpression proc near
	push	eax						; Save registers
	push	ebx
	push	ecx
	push	edx
	
	sub	esp,MAX_TOKENLEN
	push	eax						; Save priority

	mov	esi,0	 

	call	ParsePrimary					; Get ESI -> left node
	cmp	esi,0
	je	parse_error

parse_loop:
	; Get CL = token value, e.g. TOK_TIMES
	mov	cl,[edi+env_tokval]				
	
	; Get EBX priority if operator (-1 if other token, e.g TOK_IDENT)
	movzx	eax,cl
	mov	ebx,OP_ENTRY_SIZE
	mul	ebx
	movsx	ebx,byte ptr [eax+offset op_table + op_priority]
	
	cmp	ebx,-1
	jne	priority_ok
	
	; Not an operator. TOK_END and TOK_COMMA are ok, else error
	cmp	cl,TOK_END
	je	parse_done
	cmp	cl,TOK_COMMA
	je	parse_done
	
	; Oops, not an operator
	push	offset message_operator				; Error: "Operator expected"
	call	_errmsg
	add	esp,4
	jmp	parse_error
	
priority_ok:
	; Have:
	;	CL = token value
	;	EBX = operator priority 
	;	[ESP] = arg priority 
	;	ESI -> left node 
	;	EDI -> env
	
	cmp	cl,TOK_RPAREN
	jne	not_rparen					
	xor	ebx,ebx						; Adjust left priority of )
not_rparen:

	cmp	ebx,[esp]					; op_priority <= priority ?
	jle	parse_done
        
        ; Copy token string to stack
       	lea	edx,[esp+4]
       	push	esi
       	lea	esi,[edi+env_tokstr]
copy_loop:
	mov	al,[esi]
	mov	[edx],al
	and	al,al
	je	copy_done
	inc	esi
	inc	edx
	jmp	copy_loop
copy_done:
	pop	esi

	call	LexAnalyse
	
	cmp	cl,TOK_LPAREN
	jne	not_lparen
	xor	ebx,ebx 					; Adjust right priority of (
not_lparen:

	; Have:
	;	CL = token value 
	;	EBX = operator priority 
	;	[ESP] = arg priority
	;       [ESP+4] = token string 
	;	ESI -> left node 
	;	EDI -> env

	push	esi
	mov	eax,ebx						; EAX = op priority
	call	ParseExpression					; Get ESI -> right node
	pop	ebx						;     EBX -> left node
	
	cmp	esi,0
	je	parse_error

	; Call newnode with:
	;	EDI -> env
	;	CL = token value
	;	EDX -> token string
	;	EBX -> left node
	;	ESI -> right node
	;
	; Returns ESI -> new node
	lea	edx,[esp+4]
	call	newnode	
	cmp	esi,0
	jne	parse_loop

parse_error:
	mov	esi,0
		
parse_done:
	; Arrive here with ESI -> left hand node (result)
	pop	eax						; Clean up stack
	add	esp,MAX_TOKENLEN
	
	pop	edx						; Restore registers
	pop	ecx
	pop	ebx
	pop	eax
	ret
	
ParseExpression endp          
		  
		    
;************************************************************************
;	ParsePrimary() - Mutually recursive routine of ParseExpression()*
;************************************************************************
; NOTES:
; 1.    This routine performs top down analysis of expression primaries. 
;       See ParseExpression() above.
;
; 2. On entry:
;	EDI -> environment
;
;    On exit:
;	ESI -> root node

ParsePrimary proc
	push	eax
	push	ebx
	push	ecx
	push	edx
	sub	esp,MAX_TOKENLEN
	
	mov	esi,0	 
	
	; Get CL = token value
	movzx	ecx,byte ptr [edi+env_tokval]

	; A % operator occuring where an operand is expected is probably the result
	; of misinterpreting a binary operand like %10101. Try to correct ourselves now
	cmp	cl,TOK_MOD
	jne	not_mod
	
	mov	ebx,[edi+env_expptr]				; EBX -> pointer to expression
	mov	esi,[ebx]					; ESI -> expression
	mov	al,[esi]						; AL = next char. Is it 0 or 1 ?
	cmp	al,'0'
	jl	not_mod
	cmp	al,'1'
	jg	not_mod
	
	mov	ecx,TOK_INT					; Aha, change to TOK_INT
	mov	[edi+env_tokval],cl
	
	lea	edx,[edi+env_tokstr]				; Copy %10101 string 
	mov	byte ptr [edx],'%'
	inc	edx
	
mod_loop:
	mov	al,[esi]
	cmp	al,'0'
	jl	mod_done
	cmp	al,'1'
	jg	mod_done
	mov	[edx],al
	inc	esi
	inc	edx
	jmp	mod_loop
	
mod_done:
	mov	[ebx],esi					; Update expression pointer
	mov	byte ptr [edx],0
	
not_mod:
	; Identifier or integer ?
	cmp	cl,TOK_IDENT	
    	je	tok_ident_int
    	cmp	cl,TOK_INT
    	jne	tok_check_lparen
	
tok_ident_int:
	lea	edx,[edi+env_tokstr]				; EDI -> env, EDX -> token string, CL = token value
	call	endnode
	cmp	esi,0
	je	parsep_error
	jmp	parsep_done
	
tok_check_lparen:
	; Left parenthesis ?
	cmp	cl,TOK_LPAREN
	jne	tok_check_plus
	
	call	LexAnalyse					; Get next token

	mov	eax,0
	call	ParseExpression					; Parse expression in ()
	cmp	esi,0						; Parsed ok?
	je	parsep_error

	cmp	byte ptr [edi+env_tokval], TOK_RPAREN		; Got right ) ?
	je	parsep_done
	
	push	offset message_rparen				; Error "Right parenthesis missing"
	call	_errmsg
	add	esp,4
	jmp	parsep_error
	
tok_check_plus:
	; Plus or minus ? As we're expecting operand, must be unary + or -
	cmp	cl,TOK_PLUS
	jne	tok_check_minus
	mov	ecx,TOK_UNIPLUS
	mov	byte ptr [edi+env_tokval],cl
	jmp	tok_uniop
	
tok_check_minus:
	cmp	cl,TOK_MINUS
	jne	tok_check_not
	mov	ecx,TOK_NEGATE
	mov	byte ptr [edi+env_tokval],cl
	jmp	tok_uniop
	
tok_check_not:
	cmp	cl,TOK_LOGICAL_NOT
	je	tok_uniop
	cmp	cl,TOK_BIT_NOT
	je	tok_uniop
	
	; Oops, expected an operand
	push	offset message_operand				; Error : "Operand expected"
	call	_errmsg
	add	esp,4
	jmp	parsep_error
	
tok_uniop:
	; Get here if we have unary operator, + - ! or ~
	mov	edx,esp						; Copy token string to stack. LexAnalyse will kill it
	lea	esi,[edi+env_tokstr]	
tcopy_loop:
	mov	al,[esi]
	mov	[edx],al
	and	al,al
	je	tcopy_done
	inc	esi
	inc	edx
	jmp	tcopy_loop
tcopy_done:
	
	call	LexAnalyse					; Consume token

	call	ParsePrimary					; Get ESI -> node tree for argument of unary operator
	cmp	esi,0
	je	parsep_error

	; Call newnode with:
	;	EDI -> env
	;	CL = token value
	;	EDX -> token string
	;	EBX -> left node
	;	ESI -> right node
	;
	; Returns ESI -> new node
	mov	edx,esp
	mov	ebx,esi
	mov	esi,0
	call	newnode	
	jmp	parsep_return

parsep_done:
	call	LexAnalyse
	
	; Is this a function invocation ?
	cmp	esi,0
	je	parsep_error
	mov	cl,[esi+node_tokval]
	cmp	cl,TOK_IDENT
	jne	parsep_return
	
	mov	eax,[esi+node_tokstr]					; EAX -> function name
	call	check_function						; EAX = function index, or -1
	
	cmp	byte ptr [edi+env_tokval],TOK_LPAREN
	je	got_lparen
	
	cmp	eax,-1							; Function without args?
	je	parsep_return						 
	mov	byte ptr [esi+node_tokval],TOK_FUNC			; Yes, so just change type to TOK_FUNC
	mov	[esi+node_idx],ax					; and store index into function table
	jmp	parsep_return
	
got_lparen:
	; Have (, hopefully following function
	call	LexAnalyse
	cmp	eax,-1
	je	invalid_func
	
	mov	byte ptr [esi+node_tokval],TOK_FUNC			; Change type to TOK_FUNC
	mov	[esi+node_idx],ax					; Store index into function table
	call	ParseArguments						; Parse arguments in ()
	jmp	parsep_return

invalid_func:
	push	offset message_invalid_func				; Error : "Invalid function name"
	call	_errmsg
	add	esp,4
	jmp	parsep_error
	
parsep_return:
	add	esp,MAX_TOKENLEN
	pop	edx
	pop	ecx
	pop	ebx
	pop	eax
	ret

parsep_error:
	mov	esi,0
	jmp	parsep_return

ParsePrimary	endp