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