lib/flags: progress
This commit is contained in:
parent
ba2d991765
commit
44d953a322
1
Makefile
1
Makefile
@ -15,6 +15,7 @@ all: true false cat env hexdump echo
|
||||
|
||||
prtest: prtest.o lib/printregs.o lib/hex.o
|
||||
sorttest: lib/sort.o
|
||||
flagtest: lib/flags.o
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
|
||||
35
flagtest.s
Normal file
35
flagtest.s
Normal 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
|
||||
216
lib/flags.s
216
lib/flags.s
@ -1,4 +1,12 @@
|
||||
|
||||
.option norelax
|
||||
.global init_flags
|
||||
.global parse_flags
|
||||
|
||||
.macro subi dst, src, imm
|
||||
addi \dst, \src, -\imm
|
||||
.endm
|
||||
|
||||
.data
|
||||
|
||||
flags:
|
||||
@ -19,6 +27,15 @@ entry_sp: .dword 0
|
||||
saved_argc: .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
|
||||
# e.g.
|
||||
#
|
||||
@ -56,11 +73,13 @@ parse_flags:
|
||||
ld a2, (a2)
|
||||
|
||||
tail parse_flags_args
|
||||
# n.b. tail clobbers t1
|
||||
|
||||
# a0 - flag definition struct
|
||||
# a1 - argc
|
||||
# a2 - argv
|
||||
parse_flags_args:
|
||||
mv a7, ra
|
||||
# TODO: positional arguments
|
||||
|
||||
# syntax:
|
||||
@ -68,27 +87,53 @@ parse_flags_args:
|
||||
# -flag value (only non-boolean flags)
|
||||
# -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
|
||||
mv s2, a2
|
||||
|
||||
# skip arg 0
|
||||
addi s2, a2, 8
|
||||
subi a1, a1, 1
|
||||
.L_arg_loop:
|
||||
ld s1, (s2) # load string pointer
|
||||
beqz s1, .L_next_arg # skip null pointer
|
||||
# check if first char is a '-'
|
||||
ld t0, (s1)
|
||||
beqz t0, .Lno # empty string
|
||||
addi t0, t0, -'-'
|
||||
bnez t0, .Lno # not a dash
|
||||
lbu t0, 0(s1)
|
||||
li t1, '-'
|
||||
beq t0, t1, find_flag
|
||||
|
||||
.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: -- should end flag parsing
|
||||
|
||||
find_flag:
|
||||
mv s0, a0
|
||||
.L_flag_loop:
|
||||
|
||||
lbu t3, 8(s0) # type
|
||||
lbu t2, 9(s0) # flag length
|
||||
beqz t2, .Lunknown_flag
|
||||
lbu s3, 9(s0) # flag length
|
||||
beqz s3, .Lunknown_flag # len=0 signifies the end of the flag definitions
|
||||
call flagcmp
|
||||
# return value:
|
||||
# 0: no match
|
||||
@ -97,42 +142,53 @@ parse_flags_args:
|
||||
bnez t0, .Lfound
|
||||
|
||||
.L_next_flag:
|
||||
addi s0, s0, 8
|
||||
add s0, s0, t2
|
||||
addi s0, s0, 10
|
||||
add s0, s0, s3
|
||||
j .L_flag_loop
|
||||
|
||||
|
||||
.Lfound:
|
||||
add t0, s1, t2 # arg+len
|
||||
lbu t1, (t0)
|
||||
beqz t1, 1f # is it a \0 or an =?
|
||||
0:# move s1 past the =
|
||||
addi t0, t0, 1
|
||||
mv s1, t0
|
||||
j 2f
|
||||
1:# is this a boolean flag?
|
||||
li t0, 1
|
||||
beq t0, t3, 2f
|
||||
subi t3, t3, 1
|
||||
add s1, s1, s3 # arg+len
|
||||
lbu t0, (s1)
|
||||
beqz t0, 1f # is it a \0 or an =?
|
||||
# =
|
||||
# move s1 past the =
|
||||
addi s1, s1, 1
|
||||
j handle_flag
|
||||
|
||||
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
|
||||
ld s1, 8(s2)
|
||||
addi s2, s2, 8
|
||||
subi a1, a1, 1
|
||||
# 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
|
||||
.Lparse_flag:
|
||||
lbu t3, 8(s0) # load type
|
||||
li t0, 1
|
||||
beq t0, t3, boolflag
|
||||
li t0, 2
|
||||
#lbu t3, 8(s0) # load type
|
||||
beqz t3, boolflag
|
||||
li t0, 2-1
|
||||
beq t0, t3, strflag
|
||||
li t0, 3
|
||||
li t0, 3-1
|
||||
beq t0, t3, intflag
|
||||
j .Lbad_flag_def
|
||||
|
||||
strflag:
|
||||
# easy, just store the string pointer
|
||||
# TODO: strlen
|
||||
sd s1, 0(s0)
|
||||
j .L_next_arg
|
||||
|
||||
boolflag:
|
||||
lbu t0, 0(s1)
|
||||
# if arg is empty, set value to true
|
||||
# TODO: should -bool and -bool= be treated differently?
|
||||
beqz t0, 0f
|
||||
# check for "true" and "false"
|
||||
li t1, 'f'
|
||||
@ -140,63 +196,56 @@ boolflag:
|
||||
li t1, 't'
|
||||
beq t0, t1, .Lboolcmptrue
|
||||
# anything else is an error
|
||||
j .Lboolbad
|
||||
j .Lbadbool
|
||||
|
||||
.Lboolcmpfalse:
|
||||
lbu t0, 1(s1)
|
||||
beqz t0, .Lboolbad
|
||||
beqz t0, .Lbadbool
|
||||
li t1, 'a'
|
||||
bne t1, t0, .Lboolbad
|
||||
bne t1, t0, .Lbadbool
|
||||
|
||||
lbu t0, 2(s1)
|
||||
beqz t0, .Lboolbad
|
||||
beqz t0, .Lbadbool
|
||||
li t1, 'l'
|
||||
bne t1, t0, .Lboolbad
|
||||
bne t1, t0, .Lbadbool
|
||||
|
||||
lbu t0, 3(s1)
|
||||
beqz t0, .Lboolbad
|
||||
beqz t0, .Lbadbool
|
||||
li t1, 's'
|
||||
bne t1, t0, .Lboolbad
|
||||
bne t1, t0, .Lbadbool
|
||||
|
||||
lbu t0, 4(s1)
|
||||
beqz t0, .Lboolbad
|
||||
beqz t0, .Lbadbool
|
||||
li t1, 's'
|
||||
bne t1, t0, .Lboolbad
|
||||
bne t1, t0, .Lbadbool
|
||||
|
||||
lbu t0, 5(s1)
|
||||
bnez t0, .Lboolbad
|
||||
j 0b
|
||||
bnez t0, .Lbadbool
|
||||
|
||||
sd x0, 0(s0)
|
||||
0:sd x0, 0(s0)
|
||||
j .L_next_arg
|
||||
|
||||
.Lboolcmptrue:
|
||||
lbu t0, 1(s1)
|
||||
beqz t0, .Lboolbad
|
||||
beqz t0, .Lbadbool
|
||||
li t1, 'r'
|
||||
bne t1, t0, .Lboolbad
|
||||
bne t1, t0, .Lbadbool
|
||||
|
||||
lbu t0, 2(s1)
|
||||
beqz t0, .Lboolbad
|
||||
beqz t0, .Lbadbool
|
||||
li t1, 'u'
|
||||
bne t1, t0, .Lboolbad
|
||||
bne t1, t0, .Lbadbool
|
||||
|
||||
lbu t0, 3(s1)
|
||||
beqz t0, .Lboolbad
|
||||
beqz t0, .Lbadbool
|
||||
li t1, 'e'
|
||||
bne t1, t0, .Lboolbad
|
||||
bne t1, t0, .Lbadbool
|
||||
|
||||
lbu t0, 4(s1)
|
||||
bnez t0, .Lboolbad
|
||||
bnez t0, .Lbadbool
|
||||
|
||||
1:li t1, 1
|
||||
sd t1, 0(s0)
|
||||
j .L_next_arg
|
||||
|
||||
strflag:
|
||||
# easy, just store the string pointer
|
||||
# TODO: strlen
|
||||
sd s1, 0(s0)
|
||||
1:li t0, 1
|
||||
sd t0, 0(s0)
|
||||
j .L_next_arg
|
||||
|
||||
intflag:
|
||||
@ -208,7 +257,7 @@ intflag:
|
||||
li t2, 10
|
||||
lbu t1, 0(s1)
|
||||
beqz t1, .Lbadint
|
||||
0:addi t1, t1, -0x30 # '0'
|
||||
0:subi t1, t1, 0x30 # '0'
|
||||
bltz t1, .Lbadint
|
||||
bge t1, t2, .Lbadint
|
||||
mul t0, t0, t2
|
||||
@ -219,25 +268,50 @@ intflag:
|
||||
1:sd t0, 0(s0)
|
||||
j .L_next_arg
|
||||
|
||||
|
||||
.L_next_arg:
|
||||
|
||||
|
||||
ret
|
||||
|
||||
.Lbad_flag_def:
|
||||
.Lbadint:
|
||||
.Lboolbad:
|
||||
.Lunknown_flag:
|
||||
.Lbadbool:
|
||||
mv ra, a7
|
||||
li a7, 64
|
||||
li a0, 2
|
||||
la a1, flag_failure_msg
|
||||
li a2, flag_failure_msg_len
|
||||
ecall
|
||||
li a0, -1
|
||||
ret
|
||||
|
||||
# t4 - flag
|
||||
# t5 -
|
||||
# t6 length
|
||||
# t0 - return
|
||||
# in: s1 - string to compare, length unknown (null terminated)
|
||||
# in: s0 - flag definition
|
||||
# in: s3 - flag length
|
||||
# out: t0 - match=1 no match=0
|
||||
# clobbers t0, t1, t4, t5, t6, ra
|
||||
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
|
||||
|
||||
handle_flag:
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user