Skip to content

Commit 9452423

Browse files
committed
fix parsing of integer literals with base prefix
1 parent f77a321 commit 9452423

File tree

5 files changed

+129
-13
lines changed

5 files changed

+129
-13
lines changed

esp32_ulp/assemble.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,13 +219,13 @@ def fill(self, section, amount, fill_byte):
219219
raise ValueError('fill in bss section not allowed')
220220
if section is TEXT: # TODO: text section should be filled with NOPs
221221
raise ValueError('fill/skip/align in text section not supported')
222-
fill = int(fill_byte or 0).to_bytes(1, 'little') * amount
222+
fill = int(self.opcodes.eval_arg(str(fill_byte or 0))).to_bytes(1, 'little') * amount
223223
self.offsets[section] += len(fill)
224224
if section is not BSS:
225225
self.sections[section].append(fill)
226226

227227
def d_skip(self, amount, fill=None):
228-
amount = int(amount)
228+
amount = int(self.opcodes.eval_arg(amount))
229229
self.fill(self.section, amount, fill)
230230

231231
d_space = d_skip
@@ -246,7 +246,7 @@ def d_global(self, symbol):
246246
self.symbols.set_global(symbol)
247247

248248
def append_data(self, wordlen, args):
249-
data = [int(arg).to_bytes(wordlen, 'little') for arg in args]
249+
data = [int(self.opcodes.eval_arg(arg)).to_bytes(wordlen, 'little') for arg in args]
250250
self.append_section(b''.join(data))
251251

252252
def d_byte(self, *args):

esp32_ulp/opcodes.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,13 +285,38 @@ def eval_arg(arg):
285285
_, _, sym_value = symbols.get_sym(token)
286286
parts.append(str(sym_value))
287287
else:
288-
parts.append(token)
288+
try:
289+
# attempt to parse, to convert numbers with base prefix correctly
290+
int_token = parse_int(token)
291+
parts.append(str(int_token))
292+
except ValueError:
293+
parts.append(token)
289294
parts = "".join(parts)
290295
if not validate_expression(parts):
291296
raise ValueError('Unsupported expression: %s' % parts)
292297
return eval(parts)
293298

294299

300+
def parse_int(literal):
301+
"""
302+
Parses string literals into integers, using base prefixes
303+
0x (hex), 0b (binary), and 0o or legacy 0NNN (octal).
304+
Without prefix will be treated as decimal.
305+
"""
306+
if len(literal) > 2:
307+
prefix_start = 1 if literal[0] == '-' else 0 # skip negative sign if present
308+
309+
if literal[prefix_start] == "0":
310+
prefix = literal[prefix_start + 1]
311+
if prefix == "x":
312+
return int(literal, 16)
313+
elif prefix == "b":
314+
return int(literal, 2)
315+
return int(literal, 8) # legacy octal (e.g. 077)
316+
317+
return int(literal) # implicit base10
318+
319+
295320
def arg_qualify(arg):
296321
"""
297322
look at arg and qualify its type:
@@ -311,7 +336,7 @@ def arg_qualify(arg):
311336
if arg_lower in ['--', 'eq', 'ov', 'lt', 'gt', 'ge', 'le']:
312337
return ARG(COND, arg_lower, arg)
313338
try:
314-
return ARG(IMM, int(arg), arg)
339+
return ARG(IMM, parse_int(arg), arg)
315340
except ValueError:
316341
pass
317342
try:

esp32_ulp/opcodes_s2.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,13 +301,38 @@ def eval_arg(arg):
301301
_, _, sym_value = symbols.get_sym(token)
302302
parts.append(str(sym_value))
303303
else:
304-
parts.append(token)
304+
try:
305+
# attempt to parse, to convert numbers with base prefix correctly
306+
int_token = parse_int(token)
307+
parts.append(str(int_token))
308+
except ValueError:
309+
parts.append(token)
305310
parts = "".join(parts)
306311
if not validate_expression(parts):
307312
raise ValueError('Unsupported expression: %s' % parts)
308313
return eval(parts)
309314

310315

316+
def parse_int(literal):
317+
"""
318+
Parses string literals into integers, using base prefixes
319+
0x (hex), 0b (binary), and 0o or legacy 0NNN (octal).
320+
Without prefix will be treated as decimal.
321+
"""
322+
if len(literal) > 2:
323+
prefix_start = 1 if literal[0] == '-' else 0 # skip negative sign if present
324+
325+
if literal[prefix_start] == "0":
326+
prefix = literal[prefix_start + 1]
327+
if prefix == "x":
328+
return int(literal, 16)
329+
elif prefix == "b":
330+
return int(literal, 2)
331+
return int(literal, 8) # legacy octal (e.g. 077)
332+
333+
return int(literal) # implicit base10
334+
335+
311336
def arg_qualify(arg):
312337
"""
313338
look at arg and qualify its type:
@@ -327,7 +352,7 @@ def arg_qualify(arg):
327352
if arg_lower in ['--', 'eq', 'ov', 'lt', 'gt', 'ge', 'le']:
328353
return ARG(COND, arg_lower, arg)
329354
try:
330-
return ARG(IMM, int(arg), arg)
355+
return ARG(IMM, parse_int(arg), arg)
331356
except ValueError:
332357
pass
333358
try:

tests/opcodes.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from uctypes import UINT32, BFUINT32, BF_POS, BF_LEN
99
from esp32_ulp.opcodes import make_ins, make_ins_struct_def
10-
from esp32_ulp.opcodes import get_reg, get_imm, get_cond, arg_qualify, eval_arg, ARG, REG, IMM, SYM, COND
10+
from esp32_ulp.opcodes import get_reg, get_imm, get_cond, arg_qualify, parse_int, eval_arg, ARG, REG, IMM, SYM, COND
1111
from esp32_ulp.assemble import SymbolTable, ABS, REL, TEXT
1212
import esp32_ulp.opcodes as opcodes
1313

@@ -39,13 +39,37 @@ def test_make_ins():
3939
assert _delay.all == 0x40000023
4040

4141

42+
def test_parse_int():
43+
# decimal
44+
assert parse_int("5") == 5, "5 == 5"
45+
assert parse_int("-5") == -5, "-5 == -5"
46+
# hex
47+
assert parse_int("0x5") == 5, "0x5 == 5"
48+
assert parse_int("0x5a") == 90, "0x5a == 90"
49+
assert parse_int("-0x5a") == -90, "-0x5a == -90"
50+
# binary
51+
assert parse_int("0b1001") == 9, "0b1001 == 9"
52+
assert parse_int("-0b1001") == -9, "-0b1001 == 9"
53+
# octal
54+
assert parse_int("0100") == 64, "0100 == 64"
55+
assert parse_int("0o210") == 136, "0o210 == 136"
56+
assert parse_int("-0100") == -64, "-0100 == -64"
57+
assert parse_int("-0o210") == -136, "-0o210 == -136"
58+
# negative cases
59+
assert_raises(ValueError, parse_int, '0b123', message="invalid syntax for integer with base 2: '123'")
60+
assert_raises(ValueError, parse_int, '0900', message="invalid syntax for integer with base 8: '0900'")
61+
assert_raises(ValueError, parse_int, '0o900', message="invalid syntax for integer with base 8: '900'")
62+
assert_raises(ValueError, parse_int, '0xg', message="invalid syntax for integer with base 16: 'g'")
63+
64+
4265
def test_arg_qualify():
4366
assert arg_qualify('r0') == ARG(REG, 0, 'r0')
4467
assert arg_qualify('R3') == ARG(REG, 3, 'R3')
4568
assert arg_qualify('0') == ARG(IMM, 0, '0')
4669
assert arg_qualify('-1') == ARG(IMM, -1, '-1')
4770
assert arg_qualify('1') == ARG(IMM, 1, '1')
4871
assert arg_qualify('0x20') == ARG(IMM, 32, '0x20')
72+
assert arg_qualify('0100') == ARG(IMM, 64, '0100')
4973
assert arg_qualify('0o100') == ARG(IMM, 64, '0o100')
5074
assert arg_qualify('0b1000') == ARG(IMM, 8, '0b1000')
5175
assert arg_qualify('eq') == ARG(COND, 'eq', 'eq')
@@ -96,6 +120,11 @@ def test_eval_arg():
96120
assert eval_arg('const >> 1') == 21
97121
assert eval_arg('(const|4)&0xf') == 0xe
98122

123+
assert eval_arg('0x7') == 7
124+
assert eval_arg('010') == 8
125+
assert eval_arg('-0x7') == -7 # negative
126+
assert eval_arg('~0x7') == -8 # complement
127+
99128
assert_raises(ValueError, eval_arg, 'evil()')
100129
assert_raises(ValueError, eval_arg, 'def cafe()')
101130
assert_raises(ValueError, eval_arg, '1 ^ 2')
@@ -105,14 +134,17 @@ def test_eval_arg():
105134
opcodes.symbols = None
106135

107136

108-
def assert_raises(exception, func, *args):
137+
def assert_raises(exception, func, *args, message=None):
109138
try:
110139
func(*args)
111-
except exception:
140+
except exception as e:
112141
raised = True
142+
actual_message = e.args[0]
113143
else:
114144
raised = False
115145
assert raised
146+
if message:
147+
assert actual_message == message, '%s == %s' % (actual_message, message)
116148

117149

118150
def test_reg_direct_ulp_addressing():
@@ -208,6 +240,7 @@ def test_reg_address_translations_sens():
208240

209241
test_make_ins_struct_def()
210242
test_make_ins()
243+
test_parse_int()
211244
test_arg_qualify()
212245
test_get_reg()
213246
test_get_imm()

tests/opcodes_s2.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from uctypes import UINT32, BFUINT32, BF_POS, BF_LEN
99
from esp32_ulp.opcodes_s2 import make_ins, make_ins_struct_def
10-
from esp32_ulp.opcodes_s2 import get_reg, get_imm, get_cond, arg_qualify, eval_arg, ARG, REG, IMM, SYM, COND
10+
from esp32_ulp.opcodes_s2 import get_reg, get_imm, get_cond, arg_qualify, parse_int, eval_arg, ARG, REG, IMM, SYM, COND
1111
from esp32_ulp.assemble import SymbolTable, ABS, REL, TEXT
1212
import esp32_ulp.opcodes_s2 as opcodes
1313

@@ -39,13 +39,37 @@ def test_make_ins():
3939
assert _delay.all == 0x40000023
4040

4141

42+
def test_parse_int():
43+
# decimal
44+
assert parse_int("5") == 5, "5 == 5"
45+
assert parse_int("-5") == -5, "-5 == -5"
46+
# hex
47+
assert parse_int("0x5") == 5, "0x5 == 5"
48+
assert parse_int("0x5a") == 90, "0x5a == 90"
49+
assert parse_int("-0x5a") == -90, "-0x5a == -90"
50+
# binary
51+
assert parse_int("0b1001") == 9, "0b1001 == 9"
52+
assert parse_int("-0b1001") == -9, "-0b1001 == 9"
53+
# octal
54+
assert parse_int("0100") == 64, "0100 == 64"
55+
assert parse_int("0o210") == 136, "0o210 == 136"
56+
assert parse_int("-0100") == -64, "-0100 == -64"
57+
assert parse_int("-0o210") == -136, "-0o210 == -136"
58+
# negative cases
59+
assert_raises(ValueError, parse_int, '0b123', message="invalid syntax for integer with base 2: '123'")
60+
assert_raises(ValueError, parse_int, '0900', message="invalid syntax for integer with base 8: '0900'")
61+
assert_raises(ValueError, parse_int, '0o900', message="invalid syntax for integer with base 8: '900'")
62+
assert_raises(ValueError, parse_int, '0xg', message="invalid syntax for integer with base 16: 'g'")
63+
64+
4265
def test_arg_qualify():
4366
assert arg_qualify('r0') == ARG(REG, 0, 'r0')
4467
assert arg_qualify('R3') == ARG(REG, 3, 'R3')
4568
assert arg_qualify('0') == ARG(IMM, 0, '0')
4669
assert arg_qualify('-1') == ARG(IMM, -1, '-1')
4770
assert arg_qualify('1') == ARG(IMM, 1, '1')
4871
assert arg_qualify('0x20') == ARG(IMM, 32, '0x20')
72+
assert arg_qualify('0100') == ARG(IMM, 64, '0100')
4973
assert arg_qualify('0o100') == ARG(IMM, 64, '0o100')
5074
assert arg_qualify('0b1000') == ARG(IMM, 8, '0b1000')
5175
assert arg_qualify('eq') == ARG(COND, 'eq', 'eq')
@@ -96,6 +120,11 @@ def test_eval_arg():
96120
assert eval_arg('const >> 1') == 21
97121
assert eval_arg('(const|4)&0xf') == 0xe
98122

123+
assert eval_arg('0x7') == 7
124+
assert eval_arg('010') == 8
125+
assert eval_arg('-0x7') == -7 # negative
126+
assert eval_arg('~0x7') == -8 # complement
127+
99128
assert_raises(ValueError, eval_arg, 'evil()')
100129
assert_raises(ValueError, eval_arg, 'def cafe()')
101130
assert_raises(ValueError, eval_arg, '1 ^ 2')
@@ -105,14 +134,17 @@ def test_eval_arg():
105134
opcodes.symbols = None
106135

107136

108-
def assert_raises(exception, func, *args):
137+
def assert_raises(exception, func, *args, message=None):
109138
try:
110139
func(*args)
111-
except exception:
140+
except exception as e:
112141
raised = True
142+
actual_message = e.args[0]
113143
else:
114144
raised = False
115145
assert raised
146+
if message:
147+
assert actual_message == message, '%s == %s' % (actual_message, message)
116148

117149

118150
def test_reg_direct_ulp_addressing():
@@ -258,6 +290,7 @@ def test_reg_address_translations_s3_sens():
258290

259291
test_make_ins_struct_def()
260292
test_make_ins()
293+
test_parse_int()
261294
test_arg_qualify()
262295
test_get_reg()
263296
test_get_imm()

0 commit comments

Comments
 (0)