Okay, einen hätte ich noch. 8086-Assembler. Überstzen mit NASM; laufen lassen unter DOS:
Code: Alles auswählen
cpu 8086
org 100h
expr_length equ 80h
expr equ 81h
segment .text
start:
cld
mov al, [expr_length]
or al, al
jz print_usage
mov si, expr+1 ; +1 to skip the leading space DOS puts there.
call evaluate
;
; Print the result in AX as decimal number.
;
xor cx, cx ; CX to count the decimal digits pushed to the stack.
mov bx, 10 ; Base 10.
.loop_1:
xor dx, dx ; AX := AX/DX DIV 10; DX := AX/DX MOD 10
div bx
or dl, '0' ; Push remainder as ASCII digit to the stack.
push dx
inc cx ; Increase digit counter.
or ax, ax ; Are we done?
jnz .loop_1
mov ah, 2 ; Print CX digits from stack.
.loop_2:
pop dx
int 21h
loop .loop_2
mov ah, 9 ; Newline.
mov dx, crlf
int 21h
end:
ret
segment .rodata
usage_txt:
db "Please give an expression with '+' and '*' and the numbers"
db " 1,2,3,4,5,6,7,8,9", 13, 10
db "without any spaces as command line argument.", 13, 10, '$'
segment .text
;--------------------------------------
print_usage:
mov ah, 9
mov dx, usage_txt
int 21h
ret
;--------------------------------------
;
; n is on top of the stack.
;
; In:
; SI = pointer to 0dh terminated expression string.
; Out:
; AX = result
;--------------------------------------
evaluate:
call get_number ; n := get_number()
push bp
push ax
mov bp, sp
.loop:
mov al, [si] ; AL := operator.
cmp al, 0dh
je .no_operator
inc si
cmp al, '+'
jne .not_plus
call evaluate ; n := n + evaluate()
add [bp], ax
jmp .loop
.not_plus:
cmp al, '*'
jne unexpected_operator
call get_number ; n := n * get_number()
mul word [bp]
mov [bp], ax
jmp .loop
.no_operator: ; AX := n
pop ax
pop bp
ret
;--------------------------------------
; In:
; SI = pointer to next character in expression.
; Out:
; SI = pointer after the number in the expression.
; AX = number
;--------------------------------------
get_number:
xor ah, ah
lodsb
cmp al, 0dh
je unexpected_end
sub al, '0'
jz unexpected_digit
cmp ax, 9
jg unexpected_digit
ret
;--------------------------------------
segment .rodata
unexpected_end_txt:
db "unexpected end", 13, 10, '$'
unexpected_digit_txt:
db "expected digit", 13, 10, '$'
unexpected_operator_txt:
db "expected operator", 13, 10, '$'
caret:
db '^'
crlf:
db 13, 10, '$'
;--------------------------------------
segment .text
unexpected_end:
mov ax, unexpected_end_txt
jmp print_error
unexpected_digit:
mov ax, unexpected_digit_txt
jmp print_error
unexpected_operator:
mov ax, unexpected_operator_txt
; fallthrough
;--------------------------------------
; In:
; AX := Pointer to $-terminated error message.
;--------------------------------------
print_error:
push ax
mov bl, [expr_length] ; Put 0ah+'$' at the end of the expression
xor bh, bh ; and then print it.
mov word [expr+1+bx], 240ah
mov ah, 9
mov dx, expr
int 21h
mov cx, si ; CX := index of the error in the expression - 1.
sub cx, expr+1
mov dl, ' ' ; Print spaces.
mov ah, 2
.loop:
int 21h
loop .loop
mov dx, caret ; Print '^' at place of the error.
mov ah, 9
int 21h
pop dx ; Print error message.
mov ah, 9
int 21h
mov ax, 4c01h ; Halt the program with return code 1.
int 21h
Testlauf mit allen drei möglichen Fehlerfällen:
Code: Alles auswählen
C:\>dir eval
Volume in drive C is BROKEN
Volume Serial Number is DEAD-CAFE
Directory of C:\
EVAL COM 363 30-06-2022 11:19a
1 File(s) 363 Bytes
0 Dir(s) 84,881,920 Bytes free
C:\>eval
Please give an expression with '+' and '*' and the numbers 1,2,3,4,5,6,7,8,9
without any spaces as command line argument.
C:\>eval hallo
hallo
^
expected digit
C:\>eval 1
1
C:\>eval 1+
1+
^
unexpected end
C:\>eval 1+2
3
C:\>eval 1+2*3
7
C:\>eval 42
42
^
expected operator