summaryrefslogtreecommitdiff
path: root/parse.asm
diff options
context:
space:
mode:
authorSebastian G. Kirmayer <gloria@gloria-mundi.eu>2026-07-07 23:54:15 +0200
committerSebastian G. Kirmayer <gloria@gloria-mundi.eu>2026-07-07 23:54:15 +0200
commit9a6c7d85eb1be819b94d0feb0cb84de8546e36fd (patch)
tree73bab5bccdeffed245bd596cdeadde5cfddeb42f /parse.asm
Diffstat (limited to 'parse.asm')
-rw-r--r--parse.asm996
1 files changed, 996 insertions, 0 deletions
diff --git a/parse.asm b/parse.asm
new file mode 100644
index 0000000..1b23745
--- /dev/null
+++ b/parse.asm
@@ -0,0 +1,996 @@
+# Copyright (C) 2026 Sebastian G. Kirmayer <gloria@gloria-mundi.eu>
+#
+# This file is part of sysf-i386.
+#
+# sysf-i386 is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# sysf-i386 is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along
+# with sysf-i386. If not, see <https://www.gnu.org/licenses/>.
+#
+
+bss
+align
+label lineno
+ long 0
+label error_flag
+ long 0
+label macro_flag
+ long 0
+
+label parse_stack_top
+ skip $((PARSE_STACK_SIZE*4))
+label parse_stack_bottom
+label macro_types_start
+ skip $((MACROS_SIZE*4))
+label macro_types_end
+label macro_start
+ skip $((PARSE_STACK_SIZE*16))
+label macro_end
+label input
+ long 0
+label input_end
+ long 0
+data
+align
+label parse_stack_ptr
+ long parse_stack_bottom
+label macro_type_ptr
+ long macro_types_start
+label macro_ptr
+ long macro_start
+text
+
+# STK
+# { IDENT START, IDENT END } (if STK & STK_HAVE_IDENT)
+# LINENO (if STK & STK_HAVE_LINENO)
+
+# On the type stack:
+# callee type (if STK & STK_HAVE_VALUE)
+# argument type (if STK & STK_SMALL_LAMBDA)
+
+# stack control words:
+STK_BOTTOM=1
+STK_PAREN=2
+STK_SMALL_LAMBDA=4
+STK_BIG_LAMBDA=8
+
+STK_TYPE_BOTTOM=256
+STK_TYPE_ARROW=512
+STK_TYPE_PAREN=1024
+STK_TYPE_FORALL=2048
+
+STK_HAVE_VALUE=16
+STK_HAVE_IDENT=$((STK_SMALL_LAMBDA|STK_BIG_LAMBDA|STK_TYPE_FORALL))
+STK_HAVE_LINENO=$((STK_PAREN|STK_TYPE_PAREN))
+
+BACKSLASH=0x5C
+SLASH=0x2F
+LPAREN=0x28
+RPAREN=0x29
+LBRACKET=0x5B
+RBRACKET=0x5D
+COLON=0x3A
+SEMICOLON=0x3B
+OCTOTHORPE=0x23
+SPACE=0x20
+TAB=0x09
+NEWLINE=0x0A
+HYPHEN=0x2D
+GREATER=0x3E
+UNDERSCORE=0x5F
+
+label parse_push # (val: i32) -> ()
+ movl @parse_stack_ptr %ecx
+ cmpl parse_stack_top %ecx
+ je overflow.
+ subl 4 %ecx
+ movl %eax @%ecx
+ movl %ecx @parse_stack_ptr
+ ret
+
+label parse_peek # () -> (val: i32)
+ movl @parse_stack_ptr %eax
+ movl @%eax %eax
+ ret
+
+label parse_pop # () -> (val: i32)
+ movl @parse_stack_ptr %eax
+ addl 4 @parse_stack_ptr
+ movl @%eax %eax
+ ret
+
+L overflow
+ movl msg_parse_overflow %eax
+ movl msg_parse_overflow_end %ecx
+ call report_error
+ jmp error
+rodata
+string msg_parse_overflow "Parse stack overflow
+"
+text
+
+label parse # (start: *i8, end: *i8) -> (term: *term)
+ pushl %ebx
+ pushl %edi
+ pushl %esi
+ pushl %ebp
+ movl %eax @input
+ movl %ecx @input_end
+ call align_heap
+ movl $STK_BOTTOM %eax
+ call parse_push
+ # push two bottoms -- this simplifies handling of unmatched )
+ movl $STK_BOTTOM %eax
+ call parse_push
+ # push an extra poison to the type stack
+ call ty_poison
+ movl 0 @lineno
+ movl 0 @error_flag # this must occur after the ty_poison above
+L loop
+ call skip_white
+ movl @input %eax
+ cmpl @input_end %eax
+ je eof.
+ cmpb $RPAREN @%eax
+ je right_paren.
+ call parse_peek
+ testl $STK_HAVE_VALUE %eax
+ jz parse_value.
+ call ty_top_is_forall
+ jne parse_value.
+ call parse_type
+ call ty_apply_forall
+ jmp loop^
+
+L parse_value
+ movl @input %eax
+ movb @%eax %al
+ cmpb $BACKSLASH %al
+ je small_lambda.
+ cmpb $SLASH %al
+ je big_lambda.
+ cmpb $LPAREN %al
+ je left_paren.
+ # Variable terms
+ call read_ident
+ movl $STK_SMALL_LAMBDA %edx
+ call find_ident
+ movl %eax %esi
+ movl %ecx %ebp
+ movl %edx %edi
+ movl @heap_ptr %ecx
+ call push_value
+ testl %esi %esi
+ js bad_ident.
+ movl %edi %eax
+ movl %ebp %ecx
+ call ty_copy
+ jmp value_done.
+L bad_ident
+ movl %esp %eax
+ call ty_poison
+L value_done
+ movl %esp %eax
+ call apply
+ jmp loop^
+
+L small_lambda
+ incl @input
+ call skip_white
+ call read_ident
+ movl %eax %esi
+ movl %ecx %eax
+ call parse_push
+ movl %esi %eax
+ call parse_push
+ movl $STK_SMALL_LAMBDA %eax
+ call parse_push
+ call skip_white
+ movl @input %eax
+ cmpl @input_end %eax
+ je no_colon.
+ movb @%eax %al
+ cmpb $COLON %al
+ jne no_colon.
+ incl @input
+ call parse_type
+ jmp loop^
+
+L no_colon
+ call stderr_lineno
+ stderr "Missing argument type in lambda abstraction
+"
+ call ty_poison
+ movl 1 @error_flag
+ jmp loop^
+
+L big_lambda
+ movl @input %eax
+ incl %eax
+ cmpl @input_end %eax
+ je bad_char
+ movb @%eax %al
+ cmpb $BACKSLASH %al
+ jne bad_char
+ addl 2 @input
+ call skip_white
+ call read_ident
+ movl %eax %ebx
+ movl %ecx %eax
+ call parse_push
+ movl %ebx %eax
+ call parse_push
+ movl $STK_BIG_LAMBDA %eax
+ call parse_push
+ jmp loop^
+
+L left_paren
+ incl @input
+ movl @lineno %eax
+ call parse_push
+ movl $STK_PAREN %eax
+ call parse_push
+ jmp loop^
+
+L right_paren
+ incl @input
+ movl $STK_PAREN %eax
+ call close_paren
+ jmp loop^
+
+L eof
+ movl $STK_BOTTOM %eax
+ call close_paren
+ popl %ebp
+ popl %esi
+ popl %edi
+ popl %ebx
+ movl @error_flag %eax
+ testl %eax %eax
+ jnz error
+ ret
+
+label close_paren # (type: i32) -> ()
+ pushl %ebx
+ pushl %edi
+ pushl %ebp
+ movl %eax %ebx
+ # %ebx -- type wanted
+ call parse_pop
+ movl %eax %edi
+ # %edi -- type on stack
+ testl $STK_HAVE_VALUE %edi
+ jz no_value.
+L have_value
+ testl $STK_HAVE_IDENT %edi
+ jz skip.
+ call parse_pop
+ call parse_pop
+L skip
+ testl $STK_SMALL_LAMBDA %edi
+ jnz small_lambda.
+ testl $STK_BIG_LAMBDA %edi
+ jz skip.
+L big_lambda
+ call ty_forall
+ jmp skip.
+L small_lambda
+ call ty_arrow
+ movl @heap_ptr %ecx
+ movl @%ecx+-8 %ecx
+ movl $VAL_ABS %eax
+ call push_value
+L skip
+ testl $STK_HAVE_LINENO %edi
+ jz no_lineno.
+ call parse_pop
+ movl %eax %ebp
+ # %ebp -- lineno
+L no_lineno
+ call apply
+ testl %ebx %edi
+ jnz end.
+ testl $STK_BOTTOM %edi
+ jnz unmatched_right.
+ testl $STK_PAREN %edi
+ jnz unmatched_left.
+L continue
+ call parse_pop
+ movl %eax %edi
+ jmp have_value^ # must have value since we just called apply
+
+L end
+ popl %ebp
+ popl %edi
+ popl %ebx
+ ret
+
+L unmatched_right
+ movl 1 @error_flag
+ call stderr_lineno
+ stderr "Unmatched ')'
+"
+ call parse_pop
+ movl $STK_BOTTOM %eax
+ call parse_push
+ movl $((STK_BOTTOM|STK_HAVE_VALUE)) %eax
+ call parse_push
+ jmp end^
+
+L unmatched_left
+ movl 1 @error_flag
+ xchgl %ebp @lineno
+ call stderr_lineno
+ movl %ebp @lineno
+ stderr "Unmatched '('
+"
+ jmp continue^
+
+L no_value
+ # Push a poison value so we can continue
+ orl $STK_HAVE_VALUE %edi
+ movl 1 @error_flag
+ movl @heap_ptr %ecx
+ call push_value
+ call ty_poison
+ call stderr_lineno
+ # %edi -- parse stack element
+ testl $STK_PAREN %edi
+ jnz paren.
+ testl $STK_SMALL_LAMBDA %edi
+ jnz small.
+ testl $STK_BIG_LAMBDA %edi
+ jnz big.
+ testl $STK_BOTTOM %edi
+ jnz bottom.
+ ud2
+L paren
+ stderr "Empty parentheses
+"
+ jmp have_value^
+L small
+ stderr "Empty lambda abstraction
+"
+ jmp have_value^
+L big
+ stderr "Empty type lambda abstraction
+"
+ jmp have_value^
+L bottom
+ stderr "Empty program
+"
+ jmp have_value^
+
+# We have just pushed a value. Check if it is an argument, and push the
+# application if so.
+label apply # () -> ()
+ call parse_peek
+ testl $STK_HAVE_VALUE %eax
+ jnz do_apply.
+ call parse_pop
+ orl $STK_HAVE_VALUE %eax
+ call parse_push
+ ret
+L do_apply
+ call ty_apply
+ movl @heap_ptr %ecx
+ movl @%ecx+-8 %ecx
+ movl @%ecx+-8 %ecx
+ movl $VAL_APP %eax
+ call push_value
+ ret
+
+label parse_type # () -> ()
+ pushl %ebx
+ pushl %edi
+ pushl %esi
+ movl $STK_TYPE_BOTTOM %eax
+ call parse_push
+L loop
+ call skip_white
+ movl @input %eax
+ cmpl @input_end %eax
+ je bad_eof
+ movb @%eax %al
+ cmpb $BACKSLASH %al
+ je forall.
+ cmpb $LPAREN %al
+ je left_paren.
+ # Variable
+ call read_ident
+ movl %eax %edi
+ movl %ecx %esi
+ # %edi..%esi -- ident
+ movl @macro_ptr %ebx
+ # %ebx -- macro iterator
+L find_macro
+ cmpl macro_start %ebx
+ je no_macro.
+ subl 16 %ebx
+ movl %edi %eax
+ movl %esi %ecx
+ movl @%ebx %edx
+ pushl @%ebx+4
+ call streq
+ je found_macro.
+ jmp find_macro^
+
+L found_macro
+ movl @%ebx+12 %eax
+ movl @%ebx+8 %ebx
+ call ty_copy
+ # %ebx -- remaining arguments
+ call skip_white
+ movl @input %eax
+ cmpl @input_end %eax
+ je no_args.
+ cmpb $LBRACKET @%eax
+ jne no_args.
+ incl @input
+L next_arg
+ call parse_type
+ decl %ebx
+ js extra_arg.
+ call ty_apply_forall
+L arg_done
+ call skip_white
+ movl @input %eax
+ cmpl @input_end %eax
+ je bad_eof
+ cmpb $RBRACKET @%eax
+ jne next_arg^
+
+ incl @input
+L no_args
+ testl %ebx %ebx
+ jg missing_args.
+ jl extra_args.
+L have_args
+ jmp op.
+
+L extra_arg
+ call ty_pop
+ jmp arg_done^
+
+L extra_args
+rodata
+string msg_extra_arg "Extraneous macro arguments
+"
+text
+ movl msg_extra_arg %eax
+ movl msg_extra_arg_end %ecx
+ call report_error
+ movl 1 @error_flag
+ jmp have_args^
+
+L missing_args
+rodata
+string msg_missing_args "Missing macro arguments
+"
+text
+ movl msg_missing_args %eax
+ movl msg_missing_args_end %ecx
+ call report_error
+ movl 1 @error_flag
+L fake_args
+ call ty_poison
+ call ty_apply_forall
+ decl %ebx %ebx
+ jnz fake_args^
+ jmp have_args^
+
+L no_macro
+ movl %edi %eax
+ movl %esi %ecx
+ movl $((STK_BIG_LAMBDA|STK_TYPE_FORALL)) %edx
+ call find_ident
+ testl %eax %eax
+ js not_found.
+ movl %ecx %eax
+ call ty_push
+ jmp op.
+L not_found
+ call ty_poison
+ jmp op.
+
+L forall
+ movl @input %eax
+ incl %eax
+ cmpl @input_end %eax
+ je bad_eof
+ addl 2 @input
+ movb @%eax %al
+ cmpb $SLASH %al
+ jne bad_char
+ call skip_white
+ call read_ident
+ movl %eax %ebx
+ movl %ecx %eax
+ call parse_push
+ movl %ebx %eax
+ call parse_push
+ movl $STK_TYPE_FORALL %eax
+ call parse_push
+ jmp loop^
+
+L left_paren
+ incl @input
+ movl @lineno %eax
+ call parse_push
+ movl $STK_TYPE_PAREN %eax
+ call parse_push
+ jmp loop^
+
+L op
+ call skip_white
+ movl @input %eax
+ cmpl @input_end %eax
+ je end.
+ movb @%eax %al
+ cmpb $HYPHEN %al
+ je arrow.
+ cmpb $RPAREN %al
+ je right_paren.
+ jmp end.
+
+L arrow
+ movl @input %eax
+ incl %eax
+ cmpl @input_end %eax
+ je end.
+ movb @%eax %al
+ cmpb $GREATER %al
+ jne end.
+ addl 2 @input
+ movl $STK_TYPE_ARROW %eax
+ call parse_push
+ jmp loop^
+
+L right_paren
+ movl $((STK_TYPE_PAREN|STK_TYPE_BOTTOM)) %eax
+ call close_type_paren
+ testl $STK_TYPE_PAREN %eax
+ jz done.
+ incl @input
+ jmp op^
+
+L end
+ movl $STK_TYPE_BOTTOM %eax
+ call close_type_paren
+L done
+ popl %esi
+ popl %edi
+ popl %ebx
+ ret
+
+label close_type_paren # (types: i32) -> (type: i32)
+ pushl %ebx
+ pushl %edi
+ pushl %esi
+ movl %eax %ebx
+ # %ebx -- wanted types
+L loop
+ call parse_pop
+ movl %eax %edi
+ # %edi -- current type
+ testl $STK_HAVE_IDENT %edi
+ jz no_ident.
+ call parse_pop
+ call parse_pop
+L no_ident
+ testl $STK_HAVE_LINENO %edi
+ jz no_lineno.
+ call parse_pop
+ movl %eax %esi
+ # %esi -- lineno
+L no_lineno
+ testl %ebx %edi
+ jnz done.
+ testl $STK_TYPE_ARROW %edi
+ jnz arrow.
+ testl $STK_TYPE_PAREN %edi
+ jnz paren.
+ testl $STK_TYPE_FORALL %edi
+ jnz forall.
+ ud2
+
+L arrow
+ call ty_arrow
+ jmp loop^
+
+L paren
+ movl 1 @error_flag
+ xchgl %esi @lineno
+ call stderr_lineno
+ movl %esi @lineno
+ stderr "Unmatched '('
+"
+ jmp loop^
+
+L forall
+ call ty_forall
+ jmp loop^
+
+L done
+ movl %edi %eax
+ popl %esi
+ popl %edi
+ popl %ebx
+ ret
+
+label find_ident # (start: *i8, end: *i8, type: i32) -> (vidx: i32, tidx: i32, type: *type)
+ pushl %ebx
+ pushl %esi
+ pushl %edi
+ pushl %ebp
+ movl %edx %ebx
+ pushl 0
+ pushl 0
+ pushl %ecx
+ pushl %eax
+ call ty_top
+ movl %eax %edi
+ movl @parse_stack_ptr %esi
+ # %ebx -- type
+ # @%esp -- ident
+ # @%esp+4 -- ident end
+ # @%esp+8 -- value index
+ # @%esp+12 -- type index
+ # %esi -- pointer running down the stack
+ # %edi -- type stack pointer
+L loop
+ # %ebp -- type & flags
+ movl @%esi %ebp
+ addl 4 %esi
+ testl $STK_HAVE_VALUE %ebp
+ jz no_value.
+ movl %edi %eax
+ call ty_skip
+ movl %eax %edi
+L no_value
+ testl $STK_HAVE_IDENT %ebp
+ jz no_ident.
+ movl @%esi %eax
+ movl @%esi+4 %ecx
+ addl 8 %esi
+ movl @%esp %edx
+ pushl @%esp+4
+ call streq
+ je found.
+L no_ident
+ testl $STK_SMALL_LAMBDA %ebp
+ jz no_type.
+ movl %edi %eax
+ call ty_skip
+ movl %eax %edi
+L no_type
+ leal @%esi+4 %eax
+ testl $STK_HAVE_LINENO %ebp
+ cmovnz %eax %esi
+ testl $((STK_BIG_LAMBDA|STK_TYPE_FORALL)) %ebp
+ jz no_type_inc.
+ incl @%esp+12
+L no_type_inc
+ testl $STK_SMALL_LAMBDA %ebp
+ jz no_value_inc.
+ incl @%esp+8
+L no_value_inc
+ testl $STK_BOTTOM %ebp
+ jz loop^
+ movl @%esp %eax
+ movl @%esp+4 %ecx
+ call missing_ident
+L error
+ movl -1 %eax
+ movl 1 @error_flag
+ jmp end.
+L found
+ testl %ebx %ebp
+ jz bad_ident.
+ movl @%esp+8 %eax
+ movl @%esp+12 %ecx
+ movl %edi %edx
+L end
+ addl 16 %esp
+ popl %ebp
+ popl %edi
+ popl %esi
+ popl %ebx
+ ret
+
+L bad_ident
+ movl @%esp %eax
+ movl @%esp+4 %ecx
+ movl %ebx %edx
+ call bad_ident
+ jmp error^
+
+label read_ident # () -> (start: *i8, end: *i8)
+ movl @input %eax
+ movl %eax %ecx
+L loop
+ cmpl @input_end %ecx
+ je done.
+ movb @%ecx %dl
+ incl %ecx
+ cmpb $UNDERSCORE %dl
+ je loop^
+ cmpb 0x30 %dl
+ jb done_ungetc.
+ cmpb 0x39 %dl
+ jbe loop^
+ cmpb 0x41 %dl
+ jb done_ungetc.
+ cmpb 0x5A %dl
+ jbe loop^
+ cmpb 0x61 %dl
+ jb done_ungetc.
+ cmpb 0x7A %dl
+ jbe loop^
+L done_ungetc
+ decl %ecx
+L done
+ cmpl %eax %ecx
+ je bad_char
+ movl %ecx @input
+ ret
+
+label bad_char
+ movl msg_bad_char %eax
+ movl msg_bad_char_end %ecx
+ call report_error
+ jmp error
+
+label bad_eof
+ cmpl 0 @macro_flag
+ jnz macro.
+ movl msg_bad_eof %eax
+ movl msg_bad_eof_end %ecx
+ call report_error
+ jmp error
+L macro
+ movl msg_bad_eof_macro %eax
+ movl msg_bad_eof_macro_end %ecx
+ call report_error
+ jmp error
+
+rodata
+string msg_bad_char "Unexpected character
+"
+string msg_bad_eof "Unexpected end of file
+"
+string msg_bad_eof_macro "Unexpected end of line
+"
+text
+
+# Skip whitespace including comments and macros.
+label skip_white
+ movl @input %edx
+ xorl %ecx %ecx
+ cmpl %ecx @lineno
+ jne loop.
+ incl @lineno
+ incl %ecx
+L loop
+ cmpl @input_end %edx
+ je done.
+ movb @%edx %al
+ cmpb $SPACE %al
+ je white.
+ cmpb $NEWLINE %al
+ je newline.
+ cmpb $TAB %al
+ je white.
+ cmpb $SEMICOLON %al
+ je comment.
+ cmpb $OCTOTHORPE %al
+ je macro.
+L done
+ movl %edx @input
+ ret
+L comment
+ incl %edx
+ cmpl @input_end %edx
+ je done^
+ cmpb $NEWLINE @%edx
+ jne comment^
+L newline
+ incl @lineno
+ movl 1 %ecx
+ incl %edx
+ jmp loop^
+L white
+ xorl %ecx %ecx
+ incl %edx
+ jmp loop^
+L macro
+ testl %ecx %ecx
+ jz done^
+
+ pushl %ebx
+ pushl %edi
+
+ # Macro handlings.
+ # 1. Find end of line
+ incl %edx
+ movl %edx @input
+L loop
+ cmpl @input_end %edx
+ je eol.
+ cmpb $NEWLINE @%edx
+ je eol.
+ incl %edx
+ jmp loop^
+L eol
+ pushl @input_end
+ movl %edx @input_end
+ movl 1 @macro_flag
+ # Now, the entire input is just this line. So we can use normal parse
+ # functions to parse the line. There is no risk of infinite recursion since
+ # the input consists of a single line, so there are no newlines.
+ movl $STK_BOTTOM %eax
+ call parse_push
+
+ call read_ident
+ movl @macro_ptr %edx
+ cmpl macro_end %edx
+ je macro_overflow.
+ movl %eax @%edx
+ movl %ecx @%edx+4
+ movl @input %eax
+ cmpl @input_end %eax
+ je bad_eof
+ cmpb $LBRACKET @%eax
+ jne no_args.
+ incl @input
+ call skip_white
+L loop
+ call read_ident
+ movl %eax %ebx
+ movl %ecx %eax
+ call parse_push
+ movl %ebx %eax
+ call parse_push
+ movl $STK_BIG_LAMBDA %eax
+ call parse_push
+ call skip_white
+ movl @input %eax
+ cmpl @input_end %eax
+ je bad_eof
+ cmpb $RBRACKET @%eax
+ jne loop^
+ incl @input
+L no_args
+ call parse_type
+ movl @input %eax
+ cmpl @input_end %eax
+ je ok.
+rodata
+string msg_macro_extra "Extraneous input at end of macro
+"
+text
+ movl msg_macro_extra %eax
+ movl msg_macro_extra_end %ecx
+ call report_error
+ movl 1 @error_flag
+
+L ok
+ xorl %edi %edi
+ # %edi -- num args
+L loop
+ call parse_pop
+ testl $STK_BOTTOM %eax
+ jnz done.
+ incl %edi
+ call parse_pop
+ call parse_pop
+ call ty_forall
+ jmp loop^
+L done
+ movl @macro_ptr %eax
+ movl %edi @%eax+8
+ movl @macro_type_ptr %edi
+ movl %edi @%eax+12
+ call ty_top
+ movl %eax %ebx
+ call ty_skip
+ leal @%edi+%eax %ecx
+ subl %ebx %ecx
+ cmpl macro_types_end %ecx
+ ja macro_overflow.
+ movl %ecx @macro_type_ptr
+ # %ebx -- src
+ # %eax -- src end
+ # %edi -- dst
+ # %ecx -- dst end
+L loop
+ movl @%ebx %edx
+ movl %edx @%edi
+ addl 4 %ebx
+ addl 4 %edi
+ cmpl %eax %ebx
+ jne loop^
+
+ call ty_pop
+ addl 16 @macro_ptr
+ movl @input_end %eax
+ movl %eax @input
+ popl @input_end
+ movl 0 @macro_flag
+ popl %edi
+ popl %ebx
+ jmp skip_white
+
+L macro_overflow
+ movl msg_macro_overflow %eax
+ movl msg_macro_overflow_end %ecx
+ call report_error
+ jmp error
+rodata
+string msg_macro_overflow "Too many macros
+"
+text
+
+label push_value # (val: i32, start ptr: *i32)
+ movl @heap_ptr %edx
+ addl 8 %edx
+ movl %edx @heap_ptr
+ cmpl @heap_end %edx
+ ja alloc.
+L have_space
+ movl %eax @%edx+-4
+ movl %ecx @%edx+-8
+ ret
+L alloc
+ pushl %eax
+ pushl %ecx
+ call alloc
+ popl %ecx
+ popl %eax
+ movl @heap_ptr %edx
+ jmp have_space^
+
+label missing_ident # (start: *i8, end: *i8) -> ()
+ pushl %eax
+ pushl %ecx
+ call stderr_lineno
+ stderr "Name not found: "
+ popl %ecx
+ popl %eax
+ call stderr
+ stderr "
+"
+ ret
+
+label bad_ident # (start: *i8, end: *i8, wanted-type: i32)
+ pushl %edx
+ pushl %eax
+ pushl %ecx
+ call stderr_lineno
+ popl %ecx
+ popl %eax
+ call stderr
+ stderr " is not a "
+ popl %eax
+ call stderr_type
+ stderr "
+"
+ ret
+
+label stderr_type # (type: i32)
+ testl $STK_SMALL_LAMBDA %eax
+ jnz small.
+L big
+ stderr "type variable"
+ ret
+L small
+ stderr "term variable"
+ ret