Skip to content

Instantly share code, notes, and snippets.

@vipulbhj
Created May 26, 2026 03:47
Show Gist options
  • Select an option

  • Save vipulbhj/21e99f78cf0f57105362bfda59fecea7 to your computer and use it in GitHub Desktop.

Select an option

Save vipulbhj/21e99f78cf0f57105362bfda59fecea7 to your computer and use it in GitHub Desktop.
A simple expression evaluator written in AArch64 ARM assembly for MacOS
; ==============================================================================
; ARCHITECTURE & SYMBOL DEFINITIONS
; ==============================================================================
.global _main
.extern _printf ; (New: Import standard C printf function)
.align 3 ; Clamp all structures onto high-performance 8-byte boundaries
; --- TOKEN IDENTIFIERS ---
.equ TOK_NUMBER, 1
.equ TOK_ADD, 2
.equ TOK_SUB, 3
.equ TOK_MULT, 4
.equ TOK_DIV, 5
.equ TOK_LPAREN, 6
.equ TOK_RPAREN, 7
.equ TOK_EOF, 8
; --- AST NODE METADATA ---
.equ NODE_NUMBER, 1
.equ NODE_ADD, 2
.equ NODE_SUB, 3
.equ NODE_MULT, 4
.equ NODE_DIV, 5
.equ NODE_SIZE, 32 ; Standard 32-byte layout per AST node
.text
; ==============================================================================
; THE LEXER
; ==============================================================================
lexer:
mov x2, #0 ; Clear token counter out to zero
lex_loop:
ldrb w3, [x0] ; Load a single 8-bit character from source stream
; --- Whitespace and Terminator Rules ---
cmp w3, #' '
beq lex_skip
cmp w3, #'\n'
beq lex_eof
cmp w3, #0 ; Defensive verification
beq lex_eof
; --- Structural Token Rules ---
cmp w3, #'+'
beq lex_add
cmp w3, #'-'
beq lex_sub
cmp w3, #'*'
beq lex_mul
cmp w3, #'/'
beq lex_div
cmp w3, #'('
beq lex_lparen
cmp w3, #')'
beq lex_rparen
; --- Numerical Constraint Validation ---
cmp w3, #'0'
blt lex_unknown
cmp w3, #'9'
bgt lex_unknown
b lex_number
lex_skip:
add x0, x0, #1
b lex_loop
lex_unknown:
add x0, x0, #1
b lex_loop
lex_add: mov x4, TOK_ADD
b lex_emit_op
lex_sub: mov x4, TOK_SUB
b lex_emit_op
lex_mul: mov x4, TOK_MULT
b lex_emit_op
lex_div: mov x4, TOK_DIV
b lex_emit_op
lex_lparen: mov x4, TOK_LPAREN
b lex_emit_op
lex_rparen: mov x4, TOK_RPAREN
lex_emit_op:
str x4, [x1] ; Offset 0: Write 64-bit token type identifier
str xzr, [x1, #8] ; Offset 8: Value = 0
add x1, x1, #16 ; Move array forward by 16 bytes
add x2, x2, #1 ; Increment counter
add x0, x0, #1 ; Step source pointer
b lex_loop
lex_eof:
mov x4, TOK_EOF
str x4, [x1]
str xzr, [x1, #8]
add x2, x2, #1
mov x0, x2
ret
lex_number:
mov x5, #0 ; Zero out accumulator
lex_num_loop:
ldrb w3, [x0]
cmp w3, #'0'
blt lex_num_done
cmp w3, #'9'
bgt lex_num_done
sub w3, w3, #'0'
mov x6, #10
mul x5, x5, x6 ; Multiply current total by 10
add x5, x5, x3
add x0, x0, #1
b lex_num_loop
lex_num_done:
mov x4, TOK_NUMBER
str x4, [x1]
str x5, [x1, #8]
add x1, x1, #16
add x2, x2, #1
b lex_loop
; ==============================================================================
; THE PARSER
; ==============================================================================
parse_expr:
stp x29, x30, [sp, #-16]!
mov x29, sp
bl parse_term
parse_expr_loop:
ldp x1, x2, [x19]
cmp x1, TOK_ADD
beq parse_expr_right
cmp x1, TOK_SUB
beq parse_expr_right
ldp x29, x30, [sp], #16
ret
parse_expr_right:
add x19, x19, #16
stp x0, x1, [sp, #-16]!
bl parse_term
mov x3, x0
ldp x2, x1, [sp], #16
mov x7, x20
add x20, x20, NODE_SIZE
str x1, [x7, #0]
str xzr, [x7, #8]
str x2, [x7, #16]
str x3, [x7, #24]
mov x0, x7
b parse_expr_loop
parse_term:
stp x29, x30, [sp, #-16]!
mov x29, sp
bl parse_factor
parse_term_loop:
ldp x1, x2, [x19]
cmp x1, TOK_MULT
beq parse_term_right
cmp x1, TOK_DIV
beq parse_term_right
ldp x29, x30, [sp], #16
ret
parse_term_right:
add x19, x19, #16
stp x0, x1, [sp, #-16]!
bl parse_factor
mov x3, x0
ldp x2, x1, [sp], #16
mov x7, x20
add x20, x20, NODE_SIZE
str x1, [x7, #0]
str xzr, [x7, #8]
str x2, [x7, #16]
str x3, [x7, #24]
mov x0, x7
b parse_term_loop
parse_factor:
stp x29, x30, [sp, #-16]!
mov x29, sp
ldp x1, x2, [x19]
cmp x1, TOK_LPAREN
beq factor_is_paren
cmp x1, TOK_NUMBER
beq factor_is_num
b parse_crash
factor_is_paren:
add x19, x19, #16
bl parse_expr
ldp x1, x2, [x19], #16
cmp x1, TOK_RPAREN
bne parse_crash
ldp x29, x30, [sp], #16
ret
factor_is_num:
add x19, x19, #16
mov x7, x20
add x20, x20, NODE_SIZE
str x1, [x7, #0]
str x2, [x7, #8]
str xzr, [x7, #16]
str xzr, [x7, #24]
mov x0, x7
ldp x29, x30, [sp], #16
ret
; ==============================================================================
; THE AST EVALUATOR ENGINE
; ==============================================================================
evaluate:
stp x29, x30, [sp, #-16]!
mov x29, sp
cbz x0, eval_null
ldr x1, [x0, #0]
ldr x2, [x0, #8]
ldr x3, [x0, #16]
ldr x4, [x0, #24]
cmp x1, NODE_NUMBER
beq eval_number_node
stp x1, x4, [sp, #-16]!
mov x0, x3
bl evaluate
mov x5, x0
ldp x1, x4, [sp], #16
stp x1, x5, [sp, #-16]!
mov x0, x4
bl evaluate
mov x6, x0
ldp x1, x5, [sp], #16
cmp x1, NODE_ADD
beq op_addition
cmp x1, NODE_SUB
beq op_subtraction
cmp x1, NODE_MULT
beq op_multiplication
cmp x1, NODE_DIV
beq op_division
b parse_crash
op_addition:
add x0, x5, x6
b eval_done
op_subtraction:
sub x0, x5, x6
b eval_done
op_multiplication:
mul x0, x5, x6
b eval_done
op_division:
sdiv x0, x5, x6
b eval_done
eval_number_node:
mov x0, x2
b eval_done
eval_null:
mov x0, #0
eval_done:
ldp x29, x30, [sp], #16
ret
parse_crash:
mov x0, #255
mov x16, #1
svc #0x80
; ==============================================================================
; APPLICATION MAIN
; ==============================================================================
_main:
stp x29, x30, [sp, #-16]!
mov x29, sp
; --- Kernel Input Acquisition Sequence ---
adrp x1, buffer@PAGE
add x1, x1, buffer@PAGEOFF
mov x0, #0
mov x2, #256
mov x16, #3
svc #0x80
; --- Configure Lexer operational dependencies ---
adrp x0, buffer@PAGE
add x0, x0, buffer@PAGEOFF
adrp x1, token_buffer@PAGE
add x1, x1, token_buffer@PAGEOFF
bl lexer
; --- Lock Structural Callee-Saved Registers ---
stp x19, x20, [sp, #-16]!
; Initialize Compiler Global Pointer Registers
adrp x19, token_buffer@PAGE
add x19, x19, token_buffer@PAGEOFF
adrp x20, ast_arena@PAGE
add x20, x20, ast_arena@PAGEOFF
; --- RUN ENGINE ---
bl parse_expr
bl evaluate ; Evaluates calculations -> result in x0
; ==========================================================================
; NEW: PRINTF OUTPUT INTERFACING
; ==========================================================================
; For macOS ARM64 variadic calling conventions, we reserve stack room,
; store the arithmetic value (x0) at [sp], and pass the format string in x0.
sub sp, sp, #16 ; Allocate 16-byte aligned chunk on stack
str x0, [sp] ; Place 64-bit signed calculation outcome at bottom of stack
adrp x0, fmt_str@PAGE ; Load format string pointer into x0
add x0, x0, fmt_str@PAGEOFF
bl _printf ; Execute system print call sequence
add sp, sp, #16 ; Clean up stack allocation bounds
ldp x19, x20, [sp], #16 ; Restore callee registers
ldp x29, x30, [sp], #16 ; Restore entry frame
; --- Exit Program cleanly with 0 status ---
mov x0, #0
mov x16, #1
svc #0x80
; ==============================================================================
; STATIC ARCHITECTURAL MEMORY SECTIONS
; ==============================================================================
.data
.align 3
fmt_str:
.asciz "%lld\n" ; Format string: Prints a 64-bit signed integer decimal + newline
.bss
.align 3
buffer:
.space 256
token_buffer:
.space 2048
ast_arena:
.space 4096
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment