Skip to content

Commit 466a5aa

Browse files
Expand Standard Library
1 parent ef71cb1 commit 466a5aa

File tree

10 files changed

+1313
-260
lines changed

10 files changed

+1313
-260
lines changed

asmln.exe

520 Bytes
Binary file not shown.

asmln.py

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,32 +65,37 @@ def __init__(self, text: str, filename: str) -> None:
6565
self.column = 1
6666
def tokenize(self) -> List[Token]:
6767
tokens: List[Token] = []
68-
while(not self._eof):
69-
ch:str = self._peek()
68+
# Cache frequently used methods/values locally for speed.
69+
_peek = self._peek
70+
_advance = self._advance
71+
_is_identifier_start = self._is_identifier_start
72+
symbols = SYMBOLS
73+
while not self._eof:
74+
ch: str = _peek()
7075
if(ch in " \t"):
71-
self._advance()
76+
_advance()
7277
continue
7378
if(ch == "\r"):
74-
self._advance()
79+
_advance()
7580
continue
7681
if(ch == "\n"):
7782
tokens.append(Token("NEWLINE","\n",self.line,self.column))
78-
self._advance()
83+
_advance()
7984
continue
8085
if(ch == "#"):
8186
self._consume_comment()
8287
continue
8388
if(ch in SYMBOLS):
84-
tokens.append(Token(SYMBOLS[ch],ch,self.line,self.column))
85-
self._advance()
89+
tokens.append(Token(symbols[ch], ch, self.line, self.column))
90+
_advance()
8691
continue
8792
if(ch == "-"):
8893
tokens.append(self._consume_signed_number())
8994
continue
9095
if(ch in "01"):
9196
tokens.append(self._consume_unsigned_number())
9297
continue
93-
if(self._is_identifier_start(ch)):
98+
if _is_identifier_start(ch):
9499
tokens.append(self._consume_identifier())
95100
continue
96101
raise ASMParseError(f"Unexpected character '{ch}' at {self.filename}:{self.line}:{self.column}")
@@ -321,8 +326,16 @@ def _parse_expression(self) -> Expression:
321326
token = self._peek()
322327
if(token.type == "NUMBER"):
323328
number:Token = self._consume("NUMBER")
324-
value:int = int(number.value,2)
325-
return(Literal(location=self._location_from_token(number),value=value))
329+
# NUMBER token may be prefixed with '-' for negative literals.
330+
sval = number.value
331+
if sval.startswith("-"):
332+
# safe parse of negative binary literal
333+
if len(sval) == 1:
334+
raise ASMParseError(f"Invalid numeric literal at line {number.line}")
335+
value = -int(sval[1:], 2)
336+
else:
337+
value = int(sval, 2)
338+
return Literal(location=self._location_from_token(number), value=value)
326339
if(token.type == "IDENT"):
327340
ident:Token = self._consume("IDENT")
328341
location:SourceLocation = self._location_from_token(ident)
@@ -503,6 +516,8 @@ def __init__(self) -> None:
503516
self._register_fixed("BOR", 2, lambda a, b: a | b)
504517
self._register_fixed("BXOR", 2, lambda a, b: a ^ b)
505518
self._register_fixed("BNOT", 1, lambda a: ~a)
519+
self._register_fixed("SHL", 2, self._shift_left)
520+
self._register_fixed("SHR", 2, self._shift_right)
506521
# Return the inclusive bit-slice [hi:lo] of `a` as an unsigned integer.
507522
# Bits are numbered starting at 0 for the least-significant bit.
508523
# Implementation note: uses masking so negative values yield their
@@ -563,7 +578,12 @@ def invoke(
563578
builtin = self.table.get(name)
564579
if builtin is None:
565580
raise ASMRuntimeError(f"Unknown function '{name}'", location=location)
566-
builtin.validate(len(args))
581+
# Inline validation to avoid a method call in hot path.
582+
supplied = len(args)
583+
if supplied < builtin.min_args:
584+
raise ASMRuntimeError(f"{name} expects at least {builtin.min_args} arguments", rewrite_rule=name)
585+
if builtin.max_args is not None and supplied > builtin.max_args:
586+
raise ASMRuntimeError(f"{name} expects at most {builtin.max_args} arguments", rewrite_rule=name)
567587
return builtin.impl(interpreter, args, arg_nodes, env, location)
568588
def _safe_div(self, a: int, b: int) -> int:
569589
if b == 0:
@@ -587,6 +607,16 @@ def _safe_pow(self, a: int, b: int) -> int:
587607
if b < 0:
588608
raise ASMRuntimeError("Negative exponent not supported", rewrite_rule="POW")
589609
return pow(a, b)
610+
611+
def _shift_left(self, value: int, amount: int) -> int:
612+
if amount < 0:
613+
raise ASMRuntimeError("SHL amount must be non-negative", rewrite_rule="SHL")
614+
return value << amount
615+
616+
def _shift_right(self, value: int, amount: int) -> int:
617+
if amount < 0:
618+
raise ASMRuntimeError("SHR amount must be non-negative", rewrite_rule="SHR")
619+
return value >> amount
590620
def _lcm(self, a: int, b: int) -> int:
591621
if a == 0 or b == 0:
592622
return 0

asmln.spec

Lines changed: 0 additions & 38 deletions
This file was deleted.

lib/collection.asmln

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@ FUNC POW2(k)[
22
RETURN( POW(10, k) )
33
]
44

5-
FUNC SHL(x, k)[
6-
RETURN( MUL(x, POW(10, k)) )
7-
]
8-
95
FUNC COL_EMPTY()[
106
RETURN(0)
117
]
@@ -66,6 +62,7 @@ FUNC READ_ITEM_LEN_AT(payload, offset)[
6662
]
6763

6864
FUNC COL_GET(coll, index)[
65+
# get item at index from collection coll
6966
count = GET_COUNT(coll)
7067
ASSERT( LT(index, count) )
7168
payload = GET_PAYLOAD(coll)
@@ -99,20 +96,26 @@ FUNC COL_GET(coll, index)[
9996
]
10097

10198
FUNC COL_PUSH(coll, v)[
99+
# append item v to collection coll
102100
count = GET_COUNT(coll)
103101
pbl = GET_PBL(coll)
104102
payload = GET_PAYLOAD(coll)
105103

106104
enc = ENCODE_ITEM(v)
107-
enc_len = MOD(enc, POW(10, 100000)) # low 32 bits = length
105+
enc_len = MOD(enc, POW(10, 100000)) # low 32 bits = m (header+sign+mag params)
108106
enc_code = DIV(enc, POW(10, 100000)) # high part = code
107+
108+
# compute actual encoded bit length: total_len = 2*m + 1 (note: MUL(10, enc_len) == 2*enc_len)
109+
enc_bitlen = ADD( MUL(10, enc_len), 1 )
110+
109111
new_payload = ADD( payload, MUL(enc_code, POW(10, pbl)) ) # append at LSB end
110-
new_pbl = ADD(pbl, enc_len)
112+
new_pbl = ADD(pbl, enc_bitlen)
111113
new_count = ADD(count, 1)
112114
RETURN( MAKE_COLL(new_payload, new_pbl, new_count) )
113115
]
114116

115117
FUNC COL_POP(coll)[
118+
# remove and return last item from collection coll
116119
count = GET_COUNT(coll)
117120
ASSERT( GT(count, 0) )
118121
payload = GET_PAYLOAD(coll)
@@ -151,19 +154,47 @@ FUNC COL_POP(coll)[
151154
]
152155
]
153156

154-
FUNC COL_SET(coll, index, v)[ # set item at index to v
157+
FUNC READ_ITEM_AT(payload, offset)[
158+
pos = 0
159+
WHILE(1)[
160+
b = SLICE(payload, ADD(offset, pos), ADD(offset, pos))
161+
IF(EQ(b, 1))[
162+
pos = ADD(pos, 1)
163+
] ELSE [
164+
header_count = pos
165+
m = ADD(header_count, 1)
166+
sign = SLICE(payload, ADD(offset, m), ADD(offset, m))
167+
mag = SLICE(payload, ADD(offset, ADD(m, m)), ADD(offset, ADD(m, 1)))
168+
IF(EQ(sign, 1))[
169+
item = NEG(mag)
170+
] ELSE [
171+
item = mag
172+
]
173+
item_len = ADD( MUL(10, m), 1 )
174+
next_offset = ADD(offset, item_len)
175+
RETURN( ADD( MUL(next_offset, POW(10, 1000000)), item ) )
176+
]
177+
]
178+
]
179+
180+
FUNC COL_SET(coll, index, v)[
181+
# set item at index to v (non-recursive)
155182
count = GET_COUNT(coll)
156183
ASSERT( LT(index, count) )
157-
new_coll = COL_EMPTY()
184+
payload = GET_PAYLOAD(coll)
185+
offset = 0
158186
i = 0
187+
new_coll = COL_EMPTY()
159188
WHILE( LT(i, count) )[
160-
cur = COL_GET(coll, i)
189+
pair = READ_ITEM_AT(payload, offset)
190+
item = MOD(pair, POW(10, 1000000))
191+
offset = DIV(pair, POW(10, 1000000))
161192
IF(EQ(i, index))[
162-
cur = v
193+
item = v
163194
] ELSE [
164-
cur = cur
195+
item = item
165196
]
166-
new_coll = COL_PUSH(new_coll, cur)
197+
new_coll = COL_PUSH(new_coll, item)
167198
i = ADD(i, 1)
168199
]
169200
RETURN(new_coll)

0 commit comments

Comments
 (0)