Created
May 26, 2026 03:47
-
-
Save vipulbhj/21e99f78cf0f57105362bfda59fecea7 to your computer and use it in GitHub Desktop.
A simple expression evaluator written in AArch64 ARM assembly for MacOS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ; ============================================================================== | |
| ; 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