lib/flags: progress

This commit is contained in:
magical 2023-05-24 22:23:41 -07:00
parent ba2d991765
commit 44d953a322
3 changed files with 181 additions and 71 deletions

View File

@ -15,6 +15,7 @@ all: true false cat env hexdump echo
prtest: prtest.o lib/printregs.o lib/hex.o prtest: prtest.o lib/printregs.o lib/hex.o
sorttest: lib/sort.o sorttest: lib/sort.o
flagtest: lib/flags.o
.PHONY: test .PHONY: test
test: test:

35
flagtest.s Normal file
View File

@ -0,0 +1,35 @@
.global _start
.extern init_flags
.extern parse_flags
.data
flags:
.dword 0 # value / default
.byte 1 # type: <end>, bool, string, int
.byte 5 # flag length
#.short 16 # help length
.ascii "-help"
#.ascii "prints help text"
# last flag
.dword 0
.dword 0
.text
_start:
.option push
.option norelax
la gp, __global_pointer$
.option pop
mv a0, sp
call init_flags
la a0, flags
call parse_flags
li a7, 93 # sys_exit
li a0, 0
ecall

View File

@ -1,4 +1,12 @@
.option norelax
.global init_flags
.global parse_flags
.macro subi dst, src, imm
addi \dst, \src, -\imm
.endm
.data .data
flags: flags:
@ -19,6 +27,15 @@ entry_sp: .dword 0
saved_argc: .dword 0 saved_argc: .dword 0
saved_argv: .dword 0 saved_argv: .dword 0
.section .rodata
flag_failure_msg: .ascii "flag parsing failed\n\0"
.equ flag_failure_msg_len, (.) - flag_failure_msg
unknown_flag_msg: .ascii "unknown flag\n\0"
.equ unknown_flag_msg_len, (.) - unknown_flag_msg
.text
# when a program starts, it should stash the initial value of the sp register somewhere # when a program starts, it should stash the initial value of the sp register somewhere
# e.g. # e.g.
# #
@ -56,11 +73,13 @@ parse_flags:
ld a2, (a2) ld a2, (a2)
tail parse_flags_args tail parse_flags_args
# n.b. tail clobbers t1
# a0 - flag definition struct # a0 - flag definition struct
# a1 - argc # a1 - argc
# a2 - argv # a2 - argv
parse_flags_args: parse_flags_args:
mv a7, ra
# TODO: positional arguments # TODO: positional arguments
# syntax: # syntax:
@ -68,27 +87,53 @@ parse_flags_args:
# -flag value (only non-boolean flags) # -flag value (only non-boolean flags)
# -flag=value # -flag=value
# main loop # if argc <= 1, return
li t0, 1
bleu a1, t0, .Ldone
# main loop:
# for each argument, if it starts with a '-' then see if it matches a flag # for each argument, if it starts with a '-' then see if it matches a flag
mv s2, a2
# skip arg 0
addi s2, a2, 8
subi a1, a1, 1
.L_arg_loop: .L_arg_loop:
ld s1, (s2) # load string pointer ld s1, (s2) # load string pointer
beqz s1, .L_next_arg # skip null pointer beqz s1, .L_next_arg # skip null pointer
# check if first char is a '-' # check if first char is a '-'
ld t0, (s1) lbu t0, 0(s1)
beqz t0, .Lno # empty string li t1, '-'
addi t0, t0, -'-' beq t0, t1, find_flag
bnez t0, .Lno # not a dash
.L_next_arg:
addi s2, s2, 8
subi a1, a1, 1
beqz a1, .Ldone
j .L_arg_loop
.Ldone:
mv ra, a7
ret
.Lunknown_flag:
mv ra, a7
li a7, 64 # sys_write
li a0, 2
la a1, unknown_flag_msg
li a2, unknown_flag_msg_len
ecall
li a0, -1
ret
# TODO: allow --flag # TODO: allow --flag
# TODO: -- should end flag parsing # TODO: -- should end flag parsing
find_flag:
mv s0, a0 mv s0, a0
.L_flag_loop: .L_flag_loop:
lbu t3, 8(s0) # type lbu t3, 8(s0) # type
lbu t2, 9(s0) # flag length lbu s3, 9(s0) # flag length
beqz t2, .Lunknown_flag beqz s3, .Lunknown_flag # len=0 signifies the end of the flag definitions
call flagcmp call flagcmp
# return value: # return value:
# 0: no match # 0: no match
@ -97,42 +142,53 @@ parse_flags_args:
bnez t0, .Lfound bnez t0, .Lfound
.L_next_flag: .L_next_flag:
addi s0, s0, 8 addi s0, s0, 10
add s0, s0, t2 add s0, s0, s3
j .L_flag_loop j .L_flag_loop
.Lfound: .Lfound:
add t0, s1, t2 # arg+len subi t3, t3, 1
lbu t1, (t0) add s1, s1, s3 # arg+len
beqz t1, 1f # is it a \0 or an =? lbu t0, (s1)
0:# move s1 past the = beqz t0, 1f # is it a \0 or an =?
addi t0, t0, 1 # =
mv s1, t0 # move s1 past the =
j 2f addi s1, s1, 1
1:# is this a boolean flag? j handle_flag
li t0, 1
beq t0, t3, 2f 1:# \0
# is this a boolean flag? then there's no value
beqz t3, handle_flag
# if not, set s1 to the next arg and advance s2 # if not, set s1 to the next arg and advance s2
ld s1, 8(s2) ld s1, 8(s2)
addi s2, s2, 8 addi s2, s2, 8
subi a1, a1, 1
# TODO: if it's null, that's an error # TODO: if it's null, that's an error
2: # fallthrough
# s1 now points to the value of the flag
handle_flag:
# ok now parse the flag # ok now parse the flag
.Lparse_flag: #lbu t3, 8(s0) # load type
lbu t3, 8(s0) # load type beqz t3, boolflag
li t0, 1 li t0, 2-1
beq t0, t3, boolflag
li t0, 2
beq t0, t3, strflag beq t0, t3, strflag
li t0, 3 li t0, 3-1
beq t0, t3, intflag beq t0, t3, intflag
j .Lbad_flag_def j .Lbad_flag_def
strflag:
# easy, just store the string pointer
# TODO: strlen
sd s1, 0(s0)
j .L_next_arg
boolflag: boolflag:
lbu t0, 0(s1) lbu t0, 0(s1)
# if arg is empty, set value to true # if arg is empty, set value to true
# TODO: should -bool and -bool= be treated differently?
beqz t0, 0f beqz t0, 0f
# check for "true" and "false" # check for "true" and "false"
li t1, 'f' li t1, 'f'
@ -140,63 +196,56 @@ boolflag:
li t1, 't' li t1, 't'
beq t0, t1, .Lboolcmptrue beq t0, t1, .Lboolcmptrue
# anything else is an error # anything else is an error
j .Lboolbad j .Lbadbool
.Lboolcmpfalse: .Lboolcmpfalse:
lbu t0, 1(s1) lbu t0, 1(s1)
beqz t0, .Lboolbad beqz t0, .Lbadbool
li t1, 'a' li t1, 'a'
bne t1, t0, .Lboolbad bne t1, t0, .Lbadbool
lbu t0, 2(s1) lbu t0, 2(s1)
beqz t0, .Lboolbad beqz t0, .Lbadbool
li t1, 'l' li t1, 'l'
bne t1, t0, .Lboolbad bne t1, t0, .Lbadbool
lbu t0, 3(s1) lbu t0, 3(s1)
beqz t0, .Lboolbad beqz t0, .Lbadbool
li t1, 's' li t1, 's'
bne t1, t0, .Lboolbad bne t1, t0, .Lbadbool
lbu t0, 4(s1) lbu t0, 4(s1)
beqz t0, .Lboolbad beqz t0, .Lbadbool
li t1, 's' li t1, 's'
bne t1, t0, .Lboolbad bne t1, t0, .Lbadbool
lbu t0, 5(s1) lbu t0, 5(s1)
bnez t0, .Lboolbad bnez t0, .Lbadbool
j 0b
sd x0, 0(s0) 0:sd x0, 0(s0)
j .L_next_arg j .L_next_arg
.Lboolcmptrue: .Lboolcmptrue:
lbu t0, 1(s1) lbu t0, 1(s1)
beqz t0, .Lboolbad beqz t0, .Lbadbool
li t1, 'r' li t1, 'r'
bne t1, t0, .Lboolbad bne t1, t0, .Lbadbool
lbu t0, 2(s1) lbu t0, 2(s1)
beqz t0, .Lboolbad beqz t0, .Lbadbool
li t1, 'u' li t1, 'u'
bne t1, t0, .Lboolbad bne t1, t0, .Lbadbool
lbu t0, 3(s1) lbu t0, 3(s1)
beqz t0, .Lboolbad beqz t0, .Lbadbool
li t1, 'e' li t1, 'e'
bne t1, t0, .Lboolbad bne t1, t0, .Lbadbool
lbu t0, 4(s1) lbu t0, 4(s1)
bnez t0, .Lboolbad bnez t0, .Lbadbool
1:li t1, 1 1:li t0, 1
sd t1, 0(s0) sd t0, 0(s0)
j .L_next_arg
strflag:
# easy, just store the string pointer
# TODO: strlen
sd s1, 0(s0)
j .L_next_arg j .L_next_arg
intflag: intflag:
@ -208,7 +257,7 @@ intflag:
li t2, 10 li t2, 10
lbu t1, 0(s1) lbu t1, 0(s1)
beqz t1, .Lbadint beqz t1, .Lbadint
0:addi t1, t1, -0x30 # '0' 0:subi t1, t1, 0x30 # '0'
bltz t1, .Lbadint bltz t1, .Lbadint
bge t1, t2, .Lbadint bge t1, t2, .Lbadint
mul t0, t0, t2 mul t0, t0, t2
@ -219,25 +268,50 @@ intflag:
1:sd t0, 0(s0) 1:sd t0, 0(s0)
j .L_next_arg j .L_next_arg
.Lbad_flag_def:
.L_next_arg:
ret
.Lbadint: .Lbadint:
.Lboolbad: .Lbadbool:
.Lunknown_flag: mv ra, a7
li a7, 64
li a0, 2
la a1, flag_failure_msg
li a2, flag_failure_msg_len
ecall
li a0, -1 li a0, -1
ret ret
# t4 - flag # in: s1 - string to compare, length unknown (null terminated)
# t5 - # in: s0 - flag definition
# t6 length # in: s3 - flag length
# t0 - return # out: t0 - match=1 no match=0
# clobbers t0, t1, t4, t5, t6, ra
flagcmp: flagcmp:
mv t0, zero add t1, s0, 10 # flag
add t0, t1, s3 # end of flag
mv t4, s1 # str
3:lbu t5, (t1)
lbu t6, (t4)
bne t5, t6, 0f
# we don't _need_ to specifically check if t6==\0
# since that should be caught by the comparison above
# unless the flag contains an embedded \0 (it shouldn't)
# but do the check anyway just to be extra safe
beqz t6, 0f
addi t1, t1, 1
addi t4, t4, 1
# once we reach the end of the flag, we just need to check
# that the argument string ends in the same place
bge t1, t0, .Ltail
j 3b # loop
.Ltail:
lbu t1, (t4)
li t0, '='
beq t0, t1, 2f
beqz t1, 1f
0:mv t0, zero
ret
2:li t0, 2
ret
1:li t0, 1
ret ret
handle_flag: