unified the block endings

This commit is contained in:
IgorCielniak
2026-01-03 09:04:50 +00:00
parent c696898436
commit c3fc9cf831
12 changed files with 99 additions and 89 deletions

9
aa.sl Normal file
View File

@@ -0,0 +1,9 @@
import stdlib/stdlib.sl
import stdlib/io.sl
: main
mem 5 swap !
mem 8 + 6 swap !
mem @ puti cr
mem 8 + @ puti cr
;

4
fib.sl
View File

@@ -9,7 +9,7 @@ import stdlib/io.sl
22 dup >r for 22 dup >r for
2dup + dup puti cr 2dup + dup puti cr
rot rot
next end
"-------" puts "-------" puts
r> 3 + puti r> 3 + puti
" numbers printed from the fibonaci sequence" puts " numbers printed from the fibonaci sequence" puts
@@ -19,6 +19,6 @@ import stdlib/io.sl
1 2 while over 100 < do 1 2 while over 100 < do
over puti cr over puti cr
swap over + swap over +
repeat end
; ;

80
fn.sl
View File

@@ -1,14 +1,14 @@
: call-syntax-rewrite # ( fnameToken -- handled ) : call-syntax-rewrite # ( fnameToken -- handled )
dup token-lexeme identifier? 0 == if drop 0 exit then dup token-lexeme identifier? 0 == if drop 0 exit end
peek-token dup nil? if drop drop 0 exit then peek-token dup nil? if drop drop 0 exit end
dup token-lexeme "(" string= 0 == if drop drop 0 exit then dup token-lexeme "(" string= 0 == if drop drop 0 exit end
swap >r # stash fnameTok swap >r # stash fnameTok
drop # discard peeked '(' drop # discard peeked '('
next-token drop # consume '(' next-token drop # consume '('
list-new # out list-new # out
list-new # out cur list-new # out cur
begin begin
next-token dup nil? if "unterminated call expression" parse-error then next-token dup nil? if "unterminated call expression" parse-error end
dup token-lexeme ")" string= if dup token-lexeme ")" string= if
drop drop
# flush current arg # flush current arg
@@ -16,13 +16,13 @@ begin
r> list-append # out'' r> list-append # out''
inject-tokens inject-tokens
1 exit 1 exit
then end
dup token-lexeme "," string= if dup token-lexeme "," string= if
drop drop
list-extend # out' list-extend # out'
list-new # out' cur list-new # out' cur
continue continue
then end
# default: append tok to cur # default: append tok to cur
list-append list-append
again again
@@ -39,11 +39,11 @@ compile-only
: fn-op-prec : fn-op-prec
dup "+" string= if drop 1 exit then dup "+" string= if drop 1 exit end
dup "-" string= if drop 1 exit then dup "-" string= if drop 1 exit end
dup "*" string= if drop 2 exit then dup "*" string= if drop 2 exit end
dup "/" string= if drop 2 exit then dup "/" string= if drop 2 exit end
dup "%" string= if drop 2 exit then dup "%" string= if drop 2 exit end
drop 0 drop 0
; ;
compile-only compile-only
@@ -59,10 +59,10 @@ compile-only
begin begin
over list-length swap >= if # params flag over list-length swap >= if # params flag
r> exit r> exit
then end
dup >r # params idx (r: idx name) dup >r # params idx (r: idx name)
over swap list-get # params elem over swap list-get # params elem
1 rpick string= if "duplicate parameter names in fn definition" parse-error then 1 rpick string= if "duplicate parameter names in fn definition" parse-error end
drop # drop comparison flag when no error drop # drop comparison flag when no error
r> 1 + # params idx+1 r> 1 + # params idx+1
again again
@@ -76,18 +76,18 @@ compile-only
begin begin
0 rpick lexer-pop token-lexeme # params lex 0 rpick lexer-pop token-lexeme # params lex
swap drop # params lex (drop returned lexer) swap drop # params lex (drop returned lexer)
dup ")" string= if drop r> exit then dup ")" string= if drop r> exit end
dup "int" string= 0 == if "only 'int' parameters are supported in fn definitions" parse-error then dup "int" string= 0 == if "only 'int' parameters are supported in fn definitions" parse-error end
drop # params drop # params
0 rpick lexer-pop token-lexeme # params lexer pname 0 rpick lexer-pop token-lexeme # params lexer pname
swap drop # params pname swap drop # params pname
dup identifier? 0 == if "invalid parameter name in fn definition" parse-error then dup identifier? 0 == if "invalid parameter name in fn definition" parse-error end
fn-check-dup # params pname fn-check-dup # params pname
list-append # params list-append # params
0 rpick lexer-pop token-lexeme # params lexer sep 0 rpick lexer-pop token-lexeme # params lexer sep
swap drop # params sep swap drop # params sep
dup "," string= if drop continue then dup "," string= if drop continue end
dup ")" string= if drop r> exit then dup ")" string= if drop r> exit end
"expected ',' or ')' in parameter list" parse-error "expected ',' or ')' in parameter list" parse-error
again again
; ;
@@ -106,7 +106,7 @@ compile-only
begin begin
0 rpick list-empty? if 0 rpick list-empty? if
rdrop exit rdrop exit
then end
0 rpick list-pop-front # acc tokens' first 0 rpick list-pop-front # acc tokens' first
rdrop # acc tokens' rdrop # acc tokens'
swap # acc first tokens' swap # acc first tokens'
@@ -118,13 +118,13 @@ again
compile-only compile-only
: fn-validate-body : fn-validate-body
dup list-length 0 == if "empty function body" parse-error then dup list-length 0 == if "empty function body" parse-error end
dup 0 list-get token-lexeme "return" string= 0 == if "function body must start with 'return'" parse-error then dup 0 list-get token-lexeme "return" string= 0 == if "function body must start with 'return'" parse-error end
dup list-last ";" string= 0 == if "function body must terminate with ';'" parse-error then dup list-last ";" string= 0 == if "function body must terminate with ';'" parse-error end
list-clone # body body' list-clone # body body'
list-pop drop # body expr' (trim trailing ';') list-pop drop # body expr' (trim trailing ';')
list-pop-front drop # body expr (trim leading 'return') list-pop-front drop # body expr (trim leading 'return')
dup list-length 0 == if "missing return expression" parse-error then dup list-length 0 == if "missing return expression" parse-error end
; ;
compile-only compile-only
@@ -135,19 +135,19 @@ begin
dup list-empty? if dup list-empty? if
drop # out drop # out
exit exit
then end
list-pop-front # out body' tok list-pop-front # out body' tok
swap >r # out tok (r: body') swap >r # out tok (r: body')
dup "return" string= if dup "return" string= if
drop drop
r> r>
continue continue
then end
dup ";" string= if dup ";" string= if
drop drop
r> r>
continue continue
then end
list-append # out' list-append # out'
r> # out' body' r> # out' body'
continue continue
@@ -157,14 +157,14 @@ compile-only
: fn-body->tokens # bodyLexemes -- tokens : fn-body->tokens # bodyLexemes -- tokens
dup list-length 0 == if "empty function body" parse-error then dup list-length 0 == if "empty function body" parse-error end
dup 0 list-get token-lexeme "return" string= if dup 0 list-get token-lexeme "return" string= if
fn-validate-body # expr fn-validate-body # expr
shunt # postfix shunt # postfix
exit exit
then end
fn-filter-raw-body fn-filter-raw-body
dup list-length 0 == if "empty function body" parse-error then dup list-length 0 == if "empty function body" parse-error end
; ;
compile-only compile-only
@@ -177,7 +177,7 @@ begin
">r" list-append # params out' ">r" list-append # params out'
r> # params out' n-1 r> # params out' n-1
continue continue
then end
drop # params out drop # params out
exit exit
again again
@@ -191,7 +191,7 @@ begin
1 - >r 1 - >r
"rdrop" list-append "rdrop" list-append
continue continue
then end
drop # drop counter drop # drop counter
swap drop # out swap drop # out
exit exit
@@ -204,7 +204,7 @@ compile-only
1 - 1 -
0 rpick ">r" list-append drop 0 rpick ">r" list-append drop
fn-translate-prologue-loop fn-translate-prologue-loop
then end
drop drop
; ;
compile-only compile-only
@@ -214,7 +214,7 @@ compile-only
1 - 1 -
0 rpick "rdrop" list-append drop 0 rpick "rdrop" list-append drop
fn-translate-epilogue-loop fn-translate-epilogue-loop
then end
drop drop
; ;
compile-only compile-only
@@ -228,13 +228,13 @@ begin
drop # params drop # params
r> drop # drop name r> drop # drop name
-1 0 exit # params -1 0 -1 0 exit # params -1 0
then # params idx end # params idx
over over list-get # params idx elem over over list-get # params idx elem
0 rpick string= # params idx flag 0 rpick string= # params idx flag
if if
r> drop # drop name r> drop # drop name
1 exit # params idx 1 1 exit # params idx 1
then end
drop # params idx drop # params idx
1 + # params idx+1 1 + # params idx+1
again again
@@ -250,7 +250,7 @@ compile-only
over swap >= if # params map idx flag over swap >= if # params map idx flag
drop # params map drop # params map
exit exit
then # params map idx end # params map idx
2 pick over list-get # params map idx name 2 pick over list-get # params map idx name
swap # params map name idx swap # params map name idx
dup >r # params map name idx (r: idx) dup >r # params map name idx (r: idx)
@@ -274,7 +274,7 @@ compile-only
list-append # out' list-append # out'
r> # out' map r> # out' map
exit exit
then end
drop # out map tok drop # out map tok
# param? # param?
@@ -292,7 +292,7 @@ compile-only
# drop saved tok # drop saved tok
r> drop # out'' map r> drop # out'' map
exit exit
then end
# not a param: drop idx|nil, append original tok # not a param: drop idx|nil, append original tok
drop # out map drop # out map
r> # out map tok r> # out map tok
@@ -308,7 +308,7 @@ compile-only
dup list-empty? if dup list-empty? if
drop drop
exit exit
then end
list-pop-front # map out postfix' tok list-pop-front # map out postfix' tok
swap >r # map out tok (r: postfix') swap >r # map out tok (r: postfix')
>r swap r> # out map tok (r: postfix') >r swap r> # out map tok (r: postfix')
@@ -354,7 +354,7 @@ compile-only
dup lexer-pop # lexer nameTok dup lexer-pop # lexer nameTok
dup >r # save nameTok dup >r # save nameTok
token-lexeme # lexer name token-lexeme # lexer name
dup identifier? 0 == if "invalid function name for 'fn'" parse-error then dup identifier? 0 == if "invalid function name for 'fn'" parse-error end
>r # save name string >r # save name string
drop # leave lexer only for params drop # leave lexer only for params
"(" lexer-expect drop # consume '(' keep lexer "(" lexer-expect drop # consume '(' keep lexer

61
main.py
View File

@@ -258,7 +258,7 @@ class ForBegin(ASTNode):
@dataclass @dataclass
class ForNext(ASTNode): class ForEnd(ASTNode):
loop_label: str loop_label: str
end_label: str end_label: str
@@ -396,6 +396,29 @@ class Parser:
def most_recent_definition(self) -> Optional[Word]: def most_recent_definition(self) -> Optional[Word]:
return self.last_defined return self.last_defined
def _handle_end_control(self) -> None:
"""Handle unified 'end' for all block types"""
if not self.control_stack:
raise ParseError("unexpected 'end' without matching block")
entry = self.control_stack.pop()
if entry["type"] == "if":
# For if without else
if "false" in entry:
self._append_node(Label(name=entry["false"]))
elif entry["type"] == "else":
self._append_node(Label(name=entry["end"]))
elif entry["type"] == "while":
self._append_node(Jump(target=entry["begin"]))
self._append_node(Label(name=entry["end"]))
elif entry["type"] == "for":
# Emit ForEnd node for loop decrement
self._append_node(ForEnd(loop_label=entry["loop"], end_label=entry["end"]))
elif entry["type"] == "begin":
self._append_node(Jump(target=entry["begin"]))
self._append_node(Label(name=entry["end"]))
# Parsing ------------------------------------------------------------------ # Parsing ------------------------------------------------------------------
def parse(self, tokens: Iterable[Token], source: str) -> Module: def parse(self, tokens: Iterable[Token], source: str) -> Module:
self.tokens = [] self.tokens = []
@@ -440,23 +463,17 @@ class Parser:
if lexeme == "else": if lexeme == "else":
self._handle_else_control() self._handle_else_control()
continue continue
if lexeme == "then":
self._handle_then_control()
continue
if lexeme == "for": if lexeme == "for":
self._handle_for_control() self._handle_for_control()
continue continue
if lexeme == "next":
self._handle_next_control()
continue
if lexeme == "while": if lexeme == "while":
self._handle_while_control() self._handle_while_control()
continue continue
if lexeme == "do": if lexeme == "do":
self._handle_do_control() self._handle_do_control()
continue continue
if lexeme == "repeat": if lexeme == "end":
self._handle_repeat_control() self._handle_end_control()
continue continue
if self._maybe_expand_macro(token): if self._maybe_expand_macro(token):
continue continue
@@ -714,23 +731,12 @@ class Parser:
self._append_node(Label(name=entry["false"])) self._append_node(Label(name=entry["false"]))
self._push_control({"type": "else", "end": end_label}) self._push_control({"type": "else", "end": end_label})
def _handle_then_control(self) -> None:
entry = self._pop_control(("if", "else"))
if entry["type"] == "if":
self._append_node(Label(name=entry["false"]))
else:
self._append_node(Label(name=entry["end"]))
def _handle_for_control(self) -> None: def _handle_for_control(self) -> None:
loop_label = self._new_label("for_loop") loop_label = self._new_label("for_loop")
end_label = self._new_label("for_end") end_label = self._new_label("for_end")
self._append_node(ForBegin(loop_label=loop_label, end_label=end_label)) self._append_node(ForBegin(loop_label=loop_label, end_label=end_label))
self._push_control({"type": "for", "loop": loop_label, "end": end_label}) self._push_control({"type": "for", "loop": loop_label, "end": end_label})
def _handle_next_control(self) -> None:
entry = self._pop_control(("for",))
self._append_node(ForNext(loop_label=entry["loop"], end_label=entry["end"]))
def _handle_while_control(self) -> None: def _handle_while_control(self) -> None:
begin_label = self._new_label("begin") begin_label = self._new_label("begin")
end_label = self._new_label("end") end_label = self._new_label("end")
@@ -742,11 +748,6 @@ class Parser:
self._append_node(BranchZero(target=entry["end"])) self._append_node(BranchZero(target=entry["end"]))
self._push_control(entry) self._push_control(entry)
def _handle_repeat_control(self) -> None:
entry = self._pop_control(("begin",))
self._append_node(Jump(target=entry["begin"]))
self._append_node(Label(name=entry["end"]))
def _begin_definition(self, token: Token) -> None: def _begin_definition(self, token: Token) -> None:
if self._eof(): if self._eof():
raise ParseError(f"definition name missing after ':' at {token.line}:{token.column}") raise ParseError(f"definition name missing after ':' at {token.line}:{token.column}")
@@ -1300,7 +1301,7 @@ class CompileTimeVM:
self.loop_stack.append({"remaining": count, "begin": ip, "initial": count}) self.loop_stack.append({"remaining": count, "begin": ip, "initial": count})
ip += 1 ip += 1
continue continue
if isinstance(node, ForNext): if isinstance(node, ForEnd):
if not self.loop_stack: if not self.loop_stack:
raise ParseError("'next' without matching 'for'") raise ParseError("'next' without matching 'for'")
frame = self.loop_stack[-1] frame = self.loop_stack[-1]
@@ -1326,7 +1327,7 @@ class CompileTimeVM:
for idx, node in enumerate(nodes): for idx, node in enumerate(nodes):
if isinstance(node, ForBegin): if isinstance(node, ForBegin):
stack.append(idx) stack.append(idx)
elif isinstance(node, ForNext): elif isinstance(node, ForEnd):
if not stack: if not stack:
raise ParseError("'next' without matching 'for'") raise ParseError("'next' without matching 'for'")
begin_idx = stack.pop() begin_idx = stack.pop()
@@ -1641,7 +1642,7 @@ class Assembler:
if isinstance(node, ForBegin): if isinstance(node, ForBegin):
self._emit_for_begin(node, builder) self._emit_for_begin(node, builder)
return return
if isinstance(node, ForNext): if isinstance(node, ForEnd):
self._emit_for_next(node, builder) self._emit_for_next(node, builder)
return return
raise CompileError(f"unsupported AST node {node!r}") raise CompileError(f"unsupported AST node {node!r}")
@@ -1737,7 +1738,7 @@ class Assembler:
builder.emit(" mov [r13], rax") builder.emit(" mov [r13], rax")
builder.emit(f"{node.loop_label}:") builder.emit(f"{node.loop_label}:")
def _emit_for_next(self, node: ForNext, builder: FunctionEmitter) -> None: def _emit_for_next(self, node: ForEnd, builder: FunctionEmitter) -> None:
builder.emit(" mov rax, [r13]") builder.emit(" mov rax, [r13]")
builder.emit(" dec rax") builder.emit(" dec rax")
builder.emit(" mov [r13], rax") builder.emit(" mov [r13], rax")
@@ -2463,7 +2464,7 @@ PY_EXEC_GLOBALS: Dict[str, Any] = {
"Jump": Jump, "Jump": Jump,
"Label": Label, "Label": Label,
"ForBegin": ForBegin, "ForBegin": ForBegin,
"ForNext": ForNext, "ForEnd": ForEnd,
"StructField": StructField, "StructField": StructField,
"Definition": Definition, "Definition": Definition,
"Module": Module, "Module": Module,

View File

@@ -10,7 +10,7 @@
dup pick dup pick
puti cr puti cr
1 + 1 +
next end
drop drop
; ;

View File

@@ -62,7 +62,7 @@ import stdlib/io.sl
c! c!
drop drop
swap swap
next end
swap swap
nip nip
r> dup -rot - swap r> dup -rot - swap

10
test.sl
View File

@@ -104,7 +104,7 @@ fn fancy_add(int a, int b){
111 puti cr 111 puti cr
else else
222 puti cr 222 puti cr
then end
; ;
: test-else-if : test-else-if
@@ -116,8 +116,8 @@ fn fancy_add(int a, int b){
60 puti cr 60 puti cr
else else
70 puti cr 70 puti cr
then end
then end
drop drop
; ;
@@ -125,7 +125,7 @@ fn fancy_add(int a, int b){
0 0
5 for 5 for
1 + 1 +
next end
puti cr puti cr
; ;
@@ -133,7 +133,7 @@ fn fancy_add(int a, int b){
123 123
0 for 0 for
drop drop
next end
puti cr puti cr
; ;

View File

@@ -8,24 +8,24 @@ import stdlib/io.sl
write_buf # print file contents (file_len file_addr) write_buf # print file contents (file_len file_addr)
0 0
exit exit
then end
dup -2 == if # open() failed dup -2 == if # open() failed
drop drop
"open() failed: errno=" puts "open() failed: errno=" puts
swap puti cr swap puti cr
exit exit
then end
dup -1 == if # fstat() failed dup -1 == if # fstat() failed
drop drop
"fstat() failed: errno=" puts "fstat() failed: errno=" puts
swap puti cr swap puti cr
exit exit
then end
dup -3 == if # mmap() failed dup -3 == if # mmap() failed
drop drop
"mmap() failed" puts "mmap() failed" puts
exit exit
then end
"unknown read_file failure" puts "unknown read_file failure" puts
dup # file_len file_len file_addr dup # file_len file_len file_addr
exit # Exit with returned file_len as the program exit code (debug) exit # Exit with returned file_len as the program exit code (debug)

View File

@@ -7,7 +7,7 @@ import stdlib/io.sl
dup 0 > if dup 0 > if
write_buf write_buf
0 exit 0 exit
then end
"read_stdin failed" puts "read_stdin failed" puts
exit exit
; ;

View File

@@ -10,7 +10,7 @@ import stdlib/io.sl
puti cr puti cr
0 0
exit exit
then end
"write failed errno=" puts "write failed errno=" puts
puti cr puti cr
exit exit

View File

@@ -59,7 +59,7 @@ import {ROOT / 'stdlib/io.sl'}
0 0
5 for 5 for
1 + 1 +
next end
puti cr puti cr
5 5 == puti cr 5 5 == puti cr
5 4 == puti cr 5 4 == puti cr

View File

@@ -8,6 +8,6 @@ import stdlib/io.sl
do do
dup puti cr dup puti cr
1 - 1 -
repeat end
drop drop
; ;