{"id":50818907,"url":"https://github.com/risqinf/ranf","last_synced_at":"2026-06-13T12:04:46.182Z","repository":{"id":345195558,"uuid":"1184877789","full_name":"risqinf/ranf","owner":"risqinf","description":"ranf is built on Go and inspired by Rust's safety philosophy — without Rust's complexity.","archived":false,"fork":false,"pushed_at":"2026-03-18T03:23:59.000Z","size":58,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-18T18:55:56.127Z","etag":null,"topics":["go","golang","programing-language","ranf"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/risqinf.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-18T02:40:53.000Z","updated_at":"2026-03-18T03:25:31.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/risqinf/ranf","commit_stats":null,"previous_names":["risqinf/ranf"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/risqinf/ranf","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/risqinf%2Franf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/risqinf%2Franf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/risqinf%2Franf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/risqinf%2Franf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/risqinf","download_url":"https://codeload.github.com/risqinf/ranf/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/risqinf%2Franf/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34283414,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-13T02:00:06.617Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","golang","programing-language","ranf"],"created_at":"2026-06-13T12:04:45.150Z","updated_at":"2026-06-13T12:04:46.117Z","avatar_url":"https://github.com/risqinf.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ranf\n\n\u003e A Rust-inspired, safe, expressive programming language — built on Go.\n\n[![CI](https://github.com/risqinf/ranf/actions/workflows/build.yml/badge.svg)](https://github.com/risqinf/ranf/actions)\n[![Go 1.25.8+](https://img.shields.io/badge/Go-1.25.8+-00ADD8.svg?logo=go\u0026logoColor=white)](https://golang.org)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![Rust-Inspired](https://img.shields.io/badge/inspired%20by-Rust-orange.svg?logo=rust)](https://www.rust-lang.org)\n\n---\n\n## Table of Contents\n\n1. [Overview](#overview)\n2. [Architecture](#architecture)\n3. [Pipeline Flowchart](#pipeline-flowchart)\n4. [Lexer Flowchart](#lexer-flowchart)\n5. [Parser Flowchart](#parser-flowchart)\n6. [Analyzer Flowchart](#analyzer-flowchart)\n7. [VM / Evaluator Flowchart](#vm--evaluator-flowchart)\n8. [Language Reference](#language-reference)\n9. [Built-in Functions](#built-in-functions)\n10. [Quick Start](#quick-start)\n11. [CLI Reference](#cli-reference)\n12. [Examples](#examples)\n13. [Extending ranf](#extending-ranf)\n14. [Contributing](#contributing)\n\n---\n\n## Overview\n\n**ranf** is a dynamically-typed scripting language with Rust-inspired syntax and first-class safety primitives — built entirely on Go.\n\n| Property | Description |\n|---|---|\n| **Immutable by default** | `let x = 5` cannot be reassigned; use `let mut x = 5` |\n| **Option / Result types** | `None`, `Some(v)`, `Ok(v)`, `Err(e)` are first-class values |\n| **Pattern matching** | `match` with destructuring, range patterns, wildcard |\n| **f-strings** | `f\"Hello {name}!\"` with arbitrary inline expressions |\n| **Rust-like syntax** | `fn`, `struct`, `-\u003e`, `=\u003e`, `..`, `..=`, `**` |\n| **Zero runtime panics** | type errors surface as `Err` values, not crashes |\n\n---\n\n## Architecture\n\nranf follows a **microservice pipeline architecture**. Each compilation stage is an independent `Service` struct with a single public method. Stages communicate through typed interfaces — never shared mutable state.\n\n### Package Map\n\n```\ngithub.com/risqinf/ranf/\n│\n├── cmd/ranf/            CLI entry point (main.go)\n│\n├── internal/\n│   ├── token/           Token type constants, precedence table\n│   ├── lexer/           Lexer service  — source text → []token.Token\n│   ├── ast/             All AST node types (Statement, Expression)\n│   ├── parser/          Parser service — []token.Token → *ast.Program\n│   ├── analyzer/        Analyzer service — *ast.Program → ErrorList\n│   ├── object/          Runtime value types + Environment (scopes)\n│   ├── builtin/         Built-in function registry (50+ functions)\n│   ├── vm/              VM / evaluator service — *ast.Program → object.Value\n│   └── repl/            Interactive REPL service\n│\n└── pkg/\n    └── errors/          Shared structured error type (RanfError, ErrorList)\n```\n\n### Dependency Graph\n\n```mermaid\ngraph TD\n    CMD[\"cmd/ranf\u003cbr/\u003e\u003ci\u003eCLI entry point\u003c/i\u003e\"]\n\n    LEX[\"internal/lexer\u003cbr/\u003e\u003ci\u003esource → tokens\u003c/i\u003e\"]\n    PAR[\"internal/parser\u003cbr/\u003e\u003ci\u003etokens → AST\u003c/i\u003e\"]\n    ANA[\"internal/analyzer\u003cbr/\u003e\u003ci\u003esemantic checks\u003c/i\u003e\"]\n    VM[\"internal/vm\u003cbr/\u003e\u003ci\u003etree-walk evaluator\u003c/i\u003e\"]\n    REPL[\"internal/repl\u003cbr/\u003e\u003ci\u003einteractive shell\u003c/i\u003e\"]\n\n    OBJ[\"internal/object\u003cbr/\u003e\u003ci\u003eruntime value types\u003c/i\u003e\"]\n    BLT[\"internal/builtin\u003cbr/\u003e\u003ci\u003e50+ built-in fns\u003c/i\u003e\"]\n    AST[\"internal/ast\u003cbr/\u003e\u003ci\u003eAST node types\u003c/i\u003e\"]\n    TOK[\"internal/token\u003cbr/\u003e\u003ci\u003etoken constants\u003c/i\u003e\"]\n    ERR[\"pkg/errors\u003cbr/\u003e\u003ci\u003eRanfError, ErrorList\u003c/i\u003e\"]\n\n    CMD --\u003e LEX\n    CMD --\u003e PAR\n    CMD --\u003e ANA\n    CMD --\u003e VM\n    CMD --\u003e REPL\n\n    VM --\u003e|f-string re-lex| LEX\n    VM --\u003e|f-string re-parse| PAR\n    VM --\u003e OBJ\n    VM --\u003e BLT\n\n    REPL --\u003e LEX\n    REPL --\u003e PAR\n    REPL --\u003e ANA\n    REPL --\u003e VM\n\n    PAR --\u003e AST\n    LEX --\u003e TOK\n\n    LEX --\u003e ERR\n    PAR --\u003e ERR\n    ANA --\u003e ERR\n    VM --\u003e ERR\n\n    style CMD fill:#534AB7,color:#EEEDFE,stroke:#3C3489\n    style LEX fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style PAR fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style ANA fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style VM fill:#BA7517,color:#FAEEDA,stroke:#854F0B\n    style REPL fill:#993C1D,color:#FAECE7,stroke:#712B13\n    style OBJ fill:#5F5E5A,color:#F1EFE8,stroke:#444441\n    style BLT fill:#5F5E5A,color:#F1EFE8,stroke:#444441\n    style AST fill:#5F5E5A,color:#F1EFE8,stroke:#444441\n    style TOK fill:#5F5E5A,color:#F1EFE8,stroke:#444441\n    style ERR fill:#A32D2D,color:#FCEBEB,stroke:#791F1F\n```\n\n---\n\n## Pipeline Flowchart\n\n```mermaid\nflowchart TD\n    START([\"▶  ranf run myprogram.ranf\"])\n    READ[\"📄 Read file\\nfrom filesystem\"]\n\n    LEX[\"Stage 1 — LEX\\nlexer.Service\"]\n    LEX_OK{\"errors?\"}\n    LEX_ERR[\"print \u0026 exit(1)\"]\n\n    PAR[\"Stage 2 — PARSE\\nparser.Service\"]\n    PAR_OK{\"errors?\"}\n    PAR_ERR[\"print \u0026 exit(1)\"]\n\n    ANA[\"Stage 3 — ANALYZE\\nanalyzer.Service\"]\n    ANA_OK{\"errors?\"}\n    ANA_ERR[\"print \u0026 exit(1)\"]\n\n    VM[\"Stage 4 — EVAL\\nvm.Service\"]\n    VM_OK{\"runtime error?\"}\n    VM_ERR[\"print \u0026 exit(1)\"]\n\n    SUCCESS([\"✅  Success\"])\n\n    START --\u003e READ\n    READ --\u003e|raw string| LEX\n    LEX --\u003e LEX_OK\n    LEX_OK --\u003e|yes| LEX_ERR\n    LEX_OK --\u003e|no · token slice| PAR\n    PAR --\u003e PAR_OK\n    PAR_OK --\u003e|yes| PAR_ERR\n    PAR_OK --\u003e|no · ast.Program| ANA\n    ANA --\u003e ANA_OK\n    ANA_OK --\u003e|yes| ANA_ERR\n    ANA_OK --\u003e|no · ErrorList| VM\n    VM --\u003e VM_OK\n    VM_OK --\u003e|yes| VM_ERR\n    VM_OK --\u003e|no · object.Value| SUCCESS\n\n    style START fill:#534AB7,color:#EEEDFE,stroke:#3C3489\n    style SUCCESS fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style LEX fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style PAR fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style ANA fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style VM fill:#BA7517,color:#FAEEDA,stroke:#854F0B\n    style LEX_ERR fill:#A32D2D,color:#FCEBEB,stroke:#791F1F\n    style PAR_ERR fill:#A32D2D,color:#FCEBEB,stroke:#791F1F\n    style ANA_ERR fill:#A32D2D,color:#FCEBEB,stroke:#791F1F\n    style VM_ERR fill:#A32D2D,color:#FCEBEB,stroke:#791F1F\n```\n\n---\n\n## Lexer Flowchart\n\nThe lexer transforms raw UTF-8 source text into a flat `[]token.Token` stream, tracking line and column numbers for every token.\n\n```mermaid\nflowchart TD\n    SRC([\"Source bytes\"])\n    WS[\"skipWhitespace\\nspaces · tabs · newline\\nline comments · block comments\"]\n    PEEK{\"peek current byte\"}\n\n    EOF[\"emit EOF token\"]\n    NL[\"emit NEWLINE\"]\n\n    DIGIT{\"digit 0-9?\"}\n    HEX[\"hex INT\\n0x prefix\"]\n    BIN[\"binary INT\\n0b prefix\"]\n    OCT[\"octal INT\\n0o prefix\"]\n    FLT[\"FLOAT\\ncontains dot or e\"]\n    DEC[\"decimal INT\"]\n\n    FS[\"scanFString\\nf-prefix + quote\"]\n    FSTOK[\"FSTRING token\"]\n\n    IDENT[\"scanIdent\\nletter or underscore\"]\n    KW{\"LookupIdent\"}\n    KEYWORD[\"keyword token\"]\n    IDENTTOK[\"IDENT token\"]\n\n    STR[\"scanString\\nquote-delimited\"]\n    STRTOK[\"STRING token\"]\n\n    OP{\"operator or\\npunctuation\"}\n    OPS[\"emit operator token\\n+= · -\u003e · ** · == · =\u003e · ..= · etc\"]\n\n    SRC --\u003e WS --\u003e PEEK\n    PEEK --\u003e|EOF| EOF\n    PEEK --\u003e|newline| NL\n    PEEK --\u003e|digit| DIGIT\n    DIGIT --\u003e|0x or 0X| HEX\n    DIGIT --\u003e|0b or 0B| BIN\n    DIGIT --\u003e|0o or 0O| OCT\n    DIGIT --\u003e|dot or e found| FLT\n    DIGIT --\u003e|otherwise| DEC\n    PEEK --\u003e|f-prefix before quote| FS --\u003e FSTOK\n    PEEK --\u003e|letter or underscore| IDENT --\u003e KW\n    KW --\u003e|keyword match| KEYWORD\n    KW --\u003e|no match| IDENTTOK\n    PEEK --\u003e|opening quote| STR --\u003e STRTOK\n    PEEK --\u003e|operator| OP --\u003e OPS\n\n    NL --\u003e WS\n    HEX --\u003e WS\n    BIN --\u003e WS\n    OCT --\u003e WS\n    FLT --\u003e WS\n    DEC --\u003e WS\n    FSTOK --\u003e WS\n    KEYWORD --\u003e WS\n    IDENTTOK --\u003e WS\n    STRTOK --\u003e WS\n    OPS --\u003e WS\n    EOF -.-\u003e|loop continues| WS\n\n    style SRC fill:#534AB7,color:#EEEDFE,stroke:#3C3489\n    style PEEK fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style KW fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style DIGIT fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style OP fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style HEX fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style BIN fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style OCT fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style FLT fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style DEC fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style KEYWORD fill:#BA7517,color:#FAEEDA,stroke:#854F0B\n    style IDENTTOK fill:#BA7517,color:#FAEEDA,stroke:#854F0B\n    style FSTOK fill:#993C1D,color:#FAECE7,stroke:#712B13\n    style STRTOK fill:#993C1D,color:#FAECE7,stroke:#712B13\n    style OPS fill:#5F5E5A,color:#F1EFE8,stroke:#444441\n```\n\n---\n\n## Parser Flowchart\n\nThe parser uses **Pratt (top-down operator precedence)** for expressions and **recursive descent** for statements.\n\n```mermaid\nflowchart TD\n    TOKENS([\"[]token.Token\"])\n    PROG[\"parseProgram\\nloop until EOF\"]\n    STMT{\"parseStatement\\ntoken type?\"}\n\n    LET[\"parseLetStatement\\nlet mut name T = expr\"]\n    FN[\"parseFnDeclaration\\nfn name params return block\"]\n    STRUCT[\"parseStructDeclaration\\nstruct Name fields\"]\n    IF[\"parseIfStatement\\nif · else if · else\"]\n    WHILE[\"parseWhileStatement\"]\n    FOR{\"parseForStatement\"}\n    FOR_RANGE[\"ForRange\\nfor i in start..end\"]\n    FOR_EACH[\"ForEach\\nfor item in collection\"]\n    LOOP[\"parseLoopStatement\"]\n    MATCH[\"parseMatchStatement\\nmatch subject · pattern =\u003e body\"]\n    RET[\"parseReturnStatement\"]\n    BRK[\"BreakStatement\"]\n    CONT[\"ContinueStatement\"]\n    EXPR{\"parseExprOrAssign\"}\n    ASSIGN[\"AssignStatement\\n= · += · -= · *= · /=\"]\n    EXPRSTMT[\"ExpressionStatement\"]\n\n    PRATT[\"parseExpression minPrec\\nPratt algorithm\"]\n    PREFIX{\"parsePrefix\\ntoken type?\"}\n    LIT[\"literal node\\nINT · FLOAT · STR · BOOL · NULL\"]\n    IDENTP{\"IDENT token\"}\n    STRUCTLIT[\"parseStructLiteral\\nallowStructLit and next is brace\"]\n    IDENTNODE[\"Identifier node\"]\n    OPTRES[\"option · result wrapper\\nSOME · NONE · OK · ERR\"]\n    UNARY[\"UnaryExpr\\nminus or not\"]\n    GROUP[\"GroupExpr\\nparenthesised expression\"]\n    ARRAY[\"ArrayLiteral\\nbracket-delimited\"]\n\n    INFIX{\"parseInfix\\nwhile prec gt minPrec\"}\n    CALL[\"CallExpr  call parens\"]\n    INDEX[\"IndexExpr  index brackets\"]\n    FIELD[\"FieldExpr  dot access\"]\n    METHOD[\"MethodCallExpr  dot + call\"]\n    BINARY[\"BinaryExpr\\narithmetic · compare · logical\"]\n\n    TOKENS --\u003e PROG --\u003e STMT\n    STMT --\u003e|LET| LET\n    STMT --\u003e|FN| FN\n    STMT --\u003e|STRUCT| STRUCT\n    STMT --\u003e|IF| IF\n    STMT --\u003e|WHILE| WHILE\n    STMT --\u003e|FOR| FOR\n    FOR --\u003e FOR_RANGE\n    FOR --\u003e FOR_EACH\n    STMT --\u003e|LOOP| LOOP\n    STMT --\u003e|MATCH| MATCH\n    STMT --\u003e|RETURN| RET\n    STMT --\u003e|BREAK| BRK\n    STMT --\u003e|CONTINUE| CONT\n    STMT --\u003e|other| EXPR\n    EXPR --\u003e|assign operator| ASSIGN\n    EXPR --\u003e|otherwise| EXPRSTMT\n\n    EXPRSTMT --\u003e PRATT --\u003e PREFIX\n    PREFIX --\u003e|literals| LIT\n    PREFIX --\u003e|IDENT| IDENTP\n    IDENTP --\u003e|allowStructLit + brace| STRUCTLIT\n    IDENTP --\u003e|otherwise| IDENTNODE\n    PREFIX --\u003e|SOME · NONE · OK · ERR| OPTRES\n    PREFIX --\u003e|minus or not| UNARY\n    PREFIX --\u003e|open paren| GROUP\n    PREFIX --\u003e|open bracket| ARRAY\n\n    LIT --\u003e INFIX\n    IDENTNODE --\u003e INFIX\n    STRUCTLIT --\u003e INFIX\n    OPTRES --\u003e INFIX\n    UNARY --\u003e INFIX\n    GROUP --\u003e INFIX\n    ARRAY --\u003e INFIX\n\n    INFIX --\u003e|call parens| CALL\n    INFIX --\u003e|index brackets| INDEX\n    INFIX --\u003e|dot| FIELD\n    FIELD --\u003e|paren follows| METHOD\n    INFIX --\u003e|other operator| BINARY\n\n    style TOKENS fill:#534AB7,color:#EEEDFE,stroke:#3C3489\n    style STMT fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style PREFIX fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style INFIX fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style IDENTP fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style FOR fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style EXPR fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style PRATT fill:#BA7517,color:#FAEEDA,stroke:#854F0B\n    style LET fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style FN fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style STRUCT fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style IF fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style WHILE fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style FOR_RANGE fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style FOR_EACH fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style LOOP fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style MATCH fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style RET fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style BRK fill:#5F5E5A,color:#F1EFE8,stroke:#444441\n    style CONT fill:#5F5E5A,color:#F1EFE8,stroke:#444441\n    style ASSIGN fill:#993C1D,color:#FAECE7,stroke:#712B13\n    style EXPRSTMT fill:#993C1D,color:#FAECE7,stroke:#712B13\n    style CALL fill:#BA7517,color:#FAEEDA,stroke:#854F0B\n    style INDEX fill:#BA7517,color:#FAEEDA,stroke:#854F0B\n    style FIELD fill:#BA7517,color:#FAEEDA,stroke:#854F0B\n    style METHOD fill:#BA7517,color:#FAEEDA,stroke:#854F0B\n    style BINARY fill:#BA7517,color:#FAEEDA,stroke:#854F0B\n```\n\n### Operator Precedence Table\n\n| Level | Precedence | Operators |\n|:---:|:---|:---|\n| 15 | `FIELD` | `.` |\n| 14 | `INDEX` | `[]` |\n| 13 | `CALL` | `()` |\n| 12 | `UNARY` | `! -` *(prefix only)* |\n| 11 | `POWER` | `**` *(right-associative)* |\n| 10 | `PRODUCT` | `* / %` |\n| 9 | `SUM` | `+ -` |\n| 8 | `SHIFT` | `\u003c\u003c \u003e\u003e` |\n| 7 | `BITWISE` | `\u0026 \\| ^` |\n| 6 | `COMPARISON` | `\u003c \u003e \u003c= \u003e=` |\n| 5 | `EQUALITY` | `== !=` |\n| 4 | `AND` | `\u0026\u0026` |\n| 3 | `OR` | `\\|\\|` |\n| 2 | `ASSIGN` | `=` |\n| 1 | `LOWEST` | *(base)* |\n\n---\n\n## Analyzer Flowchart\n\nThe analyzer performs semantic checks in **two passes**.\n\n```mermaid\nflowchart TD\n    AST([\"*ast.Program\"])\n\n    PASS1[\"Pass 1 — collect top-level names\\nDeclare all FnDeclarations + StructDeclarations\\nPre-declare 50+ built-in names\"]\n\n    PASS2[\"Pass 2 — full analysis\\nanalyzeStmt(stmt)\"]\n\n    LET2[\"LetStatement\\ndeclare name\\nanalyzeExpr(val)\\ndefine name\"]\n    RET2[\"ReturnStatement\\nfnDepth == 0?\\n→ error\"]\n    ASSIGN2[\"AssignStatement\\ncheckMutable(target)\\nanalyzeExpr(rhs)\"]\n\n    BRK2[\"BreakStatement\\nloopDepth == 0?\\n→ error\"]\n    CONT2[\"ContinueStatement\\nloopDepth == 0?\\n→ error\"]\n\n    SCOPE[\"Scoped statements\\npush scope on entry\\npop scope on exit\"]\n    FORR[\"ForRange / ForEach\\npush scope · declare var\\nloopDepth++\"]\n    WHILE2[\"While / Loop\\nloopDepth++\"]\n    FN2[\"FnDeclaration\\npush scope · declare params\\nfnDepth++\"]\n    MATCH2[\"MatchStatement\\nper-arm scope\\npattern bindings\"]\n\n    BLOCK[\"Block traversal\\nfor each stmt in block\"]\n    TERM{\"previous stmt\\nterminated?\"}\n    UNREACH[\"emit 'unreachable code' error\\nstop traversal\"]\n    ANALYZE[\"analyzeStmt\\nreturns terminated=true\\nif return/break/continue\"]\n\n    AST --\u003e PASS1 --\u003e PASS2\n    PASS2 --\u003e LET2\n    PASS2 --\u003e RET2\n    PASS2 --\u003e ASSIGN2\n    PASS2 --\u003e BRK2\n    PASS2 --\u003e CONT2\n    PASS2 --\u003e SCOPE\n    SCOPE --\u003e FORR\n    SCOPE --\u003e WHILE2\n    SCOPE --\u003e FN2\n    SCOPE --\u003e MATCH2\n    PASS2 --\u003e BLOCK --\u003e TERM\n    TERM --\u003e|yes| UNREACH\n    TERM --\u003e|no| ANALYZE --\u003e TERM\n\n    style AST fill:#534AB7,color:#EEEDFE,stroke:#3C3489\n    style PASS1 fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style PASS2 fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style TERM fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style LET2 fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style RET2 fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style ASSIGN2 fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style FORR fill:#BA7517,color:#FAEEDA,stroke:#854F0B\n    style WHILE2 fill:#BA7517,color:#FAEEDA,stroke:#854F0B\n    style FN2 fill:#BA7517,color:#FAEEDA,stroke:#854F0B\n    style MATCH2 fill:#BA7517,color:#FAEEDA,stroke:#854F0B\n    style BRK2 fill:#993C1D,color:#FAECE7,stroke:#712B13\n    style CONT2 fill:#993C1D,color:#FAECE7,stroke:#712B13\n    style UNREACH fill:#A32D2D,color:#FCEBEB,stroke:#791F1F\n    style ANALYZE fill:#5F5E5A,color:#F1EFE8,stroke:#444441\n    style BLOCK fill:#5F5E5A,color:#F1EFE8,stroke:#444441\n    style SCOPE fill:#5F5E5A,color:#F1EFE8,stroke:#444441\n```\n\n---\n\n## VM / Evaluator Flowchart\n\nThe VM is a **tree-walking evaluator** — it recursively walks the AST and produces a runtime `object.Value` for each node.\n\n```mermaid\nflowchart TD\n    PROG([\"vm.Service.Run(program, env)\"])\n    STMT2{\"evalStmt(stmt, env)\\nstatement type?\"}\n\n    LET3[\"LetStatement\\nevalExpr(value)\\nenv.Define(name, val, mutable)\"]\n\n    ASSIGN3{\"AssignStatement\\nevalExpr(rhs)\"}\n    IDENT3[\"Identifier → env.Set(name, val)\"]\n    IDX3[\"IndexExpr → arr.Elements[i] = val\"]\n    FIELD3[\"FieldExpr → struct.Fields[f] = val\"]\n\n    SIG[\"Control signals\\nReturn · Break · Continue\"]\n    RETSIG[\"ReturnSignal\\nbubbles to applyFunction\"]\n    BRKSIG[\"BreakSignal\\nbubbles to loop handler\"]\n    CONTSIG[\"ContinueSignal\\ntriggers next iteration\"]\n\n    IF3[\"IfStatement\\nevalExpr(cond).Truthy()\\n→ evalBlock(consequence/alternative)\"]\n    WHILE3[\"WhileStatement\\nloop: cond.Truthy()\\n→ evalBlock(body)\"]\n    FOR_R[\"ForRange\\nfor i = start; i \u003c end; i++\\n→ evalBlock(body)\"]\n    FOR_E[\"ForEach\\nevalExpr(collection) → []Value\\n→ evalBlock per element\"]\n    FN3[\"FnDeclaration\\nFunction{params, body, closure=env}\\nenv.Define(name, fn)\"]\n    MATCH3[\"MatchStatement\\nevalExpr(subject)\\nfor each arm: matchPattern?\"]\n    MATCH_Y[\"yes → evalStmt(body, armEnv)\\nstop\"]\n    MATCH_N[\"no → next arm\"]\n\n    EXPR2[\"evalExpr(expr, env)\"]\n    LIT2[\"Literals\\nobject.Int / Float / Str / Bool / Null\"]\n    IDENT2[\"Identifier\\nenv.Get(name) OR builtins.Get(name)\"]\n    UNARY2[\"UnaryExpr\\nevalExpr(right) → apply - or !\"]\n    BIN2{\"BinaryExpr\"}\n    AND2[\"\u0026\u0026 short-circuit\\nleft falsy → false\"]\n    OR2[\"|| short-circuit\\nleft truthy → true\"]\n    BINOP[\"evalLeft, evalRight\\n→ applyBinaryOp\"]\n    CALL2{\"CallExpr\\neval callee\"}\n    BUILTIN[\"Builtin → fn.Fn(args)\"]\n    USERFN[\"Function\\npush closure env\\nbind params\\nevalBlock(body)\\nunwrap ReturnSignal\"]\n    METHOD2[\"MethodCallExpr\\neval object\\n→ applyMethod(obj, method, args)\"]\n    INDEX2[\"IndexExpr\\narray[int] or str[int]\"]\n    FEXPR[\"FieldExpr\\nstructInstance.Fields[field]\"]\n    FSTR2[\"FStringLiteral\\nfor each Part:\\ntext → as-is\\nexpr → re-lex → re-parse → eval → Inspect()\"]\n\n    PROG --\u003e|for each stmt| STMT2\n    STMT2 --\u003e LET3\n    STMT2 --\u003e ASSIGN3\n    ASSIGN3 --\u003e IDENT3\n    ASSIGN3 --\u003e IDX3\n    ASSIGN3 --\u003e FIELD3\n    STMT2 --\u003e SIG\n    SIG --\u003e RETSIG\n    SIG --\u003e BRKSIG\n    SIG --\u003e CONTSIG\n    STMT2 --\u003e IF3\n    STMT2 --\u003e WHILE3\n    STMT2 --\u003e FOR_R\n    STMT2 --\u003e FOR_E\n    STMT2 --\u003e FN3\n    STMT2 --\u003e MATCH3\n    MATCH3 --\u003e MATCH_Y\n    MATCH3 --\u003e MATCH_N\n\n    LET3 --\u003e EXPR2\n    IF3 --\u003e EXPR2\n    WHILE3 --\u003e EXPR2\n    FOR_R --\u003e EXPR2\n    FOR_E --\u003e EXPR2\n\n    EXPR2 --\u003e LIT2\n    EXPR2 --\u003e IDENT2\n    EXPR2 --\u003e UNARY2\n    EXPR2 --\u003e BIN2\n    BIN2 --\u003e AND2\n    BIN2 --\u003e OR2\n    BIN2 --\u003e BINOP\n    EXPR2 --\u003e CALL2\n    CALL2 --\u003e BUILTIN\n    CALL2 --\u003e USERFN\n    EXPR2 --\u003e METHOD2\n    EXPR2 --\u003e INDEX2\n    EXPR2 --\u003e FEXPR\n    EXPR2 --\u003e FSTR2\n\n    style PROG fill:#534AB7,color:#EEEDFE,stroke:#3C3489\n    style STMT2 fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style ASSIGN3 fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style BIN2 fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style CALL2 fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style MATCH3 fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style LET3 fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style FN3 fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style IF3 fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style WHILE3 fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style FOR_R fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style FOR_E fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style IDENT3 fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style IDX3 fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style FIELD3 fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style EXPR2 fill:#BA7517,color:#FAEEDA,stroke:#854F0B\n    style BUILTIN fill:#BA7517,color:#FAEEDA,stroke:#854F0B\n    style USERFN fill:#BA7517,color:#FAEEDA,stroke:#854F0B\n    style BINOP fill:#BA7517,color:#FAEEDA,stroke:#854F0B\n    style AND2 fill:#5F5E5A,color:#F1EFE8,stroke:#444441\n    style OR2 fill:#5F5E5A,color:#F1EFE8,stroke:#444441\n    style RETSIG fill:#993C1D,color:#FAECE7,stroke:#712B13\n    style BRKSIG fill:#993C1D,color:#FAECE7,stroke:#712B13\n    style CONTSIG fill:#993C1D,color:#FAECE7,stroke:#712B13\n    style FSTR2 fill:#993C1D,color:#FAECE7,stroke:#712B13\n    style MATCH_Y fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style MATCH_N fill:#5F5E5A,color:#F1EFE8,stroke:#444441\n```\n\n---\n\n## Language Reference\n\n### Variables \u0026 Types\n\n```ranf\n// Immutable (default) — cannot be reassigned\nlet name = \"Alice\"\nlet pi: float = 3.14159\n\n// Mutable — declare with 'mut'\nlet mut score = 0\nscore += 10\n\n// Numeric literals\nlet hex    = 0xFF        // 255\nlet binary = 0b1010      // 10\nlet octal  = 0o17        // 15\nlet big    = 1_000_000   // readable separators\n```\n\n**Built-in types:**\n\n| Type | Example | Notes |\n|---|---|---|\n| `int` | `42`, `-7`, `0xFF` | 64-bit signed integer |\n| `float` | `3.14`, `1e10` | 64-bit IEEE-754 |\n| `str` | `\"hello\"` | UTF-8 string |\n| `bool` | `true`, `false` | |\n| `null` | `null` | absence of value |\n| `array` | `[1, 2, 3]` | ordered, heterogeneous |\n| `struct` | `Point { x: 1 }` | user-defined record |\n| `Option\u003cT\u003e` | `Some(v)`, `None` | safe nullable |\n| `Result\u003cT,E\u003e` | `Ok(v)`, `Err(e)` | safe error handling |\n\n### Control Flow\n\n```ranf\n// if / else if / else\nif score \u003e= 90 {\n    println(\"A\")\n} else if score \u003e= 80 {\n    println(\"B\")\n} else {\n    println(\"F\")\n}\n\n// for range (exclusive / inclusive)\nfor i in 0..10  { println(i) }\nfor i in 1..=10 { println(i) }\n\n// for each\nlet items = [\"a\", \"b\", \"c\"]\nfor item in items { println(item) }\n\n// loop + break / continue\nlet mut n = 0\nloop {\n    n += 1\n    if n \u003e= 5 { break }\n}\n```\n\n### Functions\n\n```ranf\nfn add(a: int, b: int) -\u003e int {\n    return a + b\n}\n\nfn divide(a: int, b: int) -\u003e Result\u003cint\u003e {\n    if b == 0 { return Err(\"division by zero\") }\n    return Ok(a / b)\n}\n\nfn factorial(n: int) -\u003e int {\n    if n \u003c= 1 { return 1 }\n    return n * factorial(n - 1)\n}\n```\n\n### Structs\n\n```ranf\nstruct Point {\n    x: int,\n    y: int,\n}\n\nlet p = Point { x: 10, y: 20 }\nprintln(p.x)   // 10\n\nlet mut origin = Point { x: 0, y: 0 }\norigin.x = 5\n```\n\n### Option \u0026 Result\n\n```ranf\nfn safe_head(arr: array) -\u003e Option\u003cint\u003e {\n    if len(arr) == 0 { return None }\n    return Some(arr[0])\n}\n\nmatch safe_head([1, 2, 3]) {\n    Some(v) =\u003e println(f\"first = {v}\"),\n    None    =\u003e println(\"empty array\"),\n}\n\n// Unwrap helpers\nprintln(Some(42).unwrap())       // 42\nprintln(None.unwrap_or(99))      // 99\n```\n\n### Match\n\n```ranf\nmatch x {\n    0       =\u003e println(\"zero\"),\n    1       =\u003e println(\"one\"),\n    2..=9   =\u003e println(\"single digit\"),\n    10..=99 =\u003e println(\"two digits\"),\n    _       =\u003e println(\"large\"),\n}\n\nmatch read_file(\"data.txt\") {\n    Ok(content) =\u003e println(content),\n    Err(e)      =\u003e println(f\"error: {e}\"),\n}\n```\n\n### f-Strings\n\n```ranf\nlet name = \"Alice\"\nlet age  = 30\n\nprintln(f\"Name: {name}, Age: {age}\")\nprintln(f\"Next year: {age + 1}\")\nprintln(f\"Uppercase: {upper(name)}\")\n```\n\n### Operators\n\n```ranf\n// Arithmetic          +  -  *  /  %  ** (power)\n// Comparison          ==  !=  \u003c  \u003e  \u003c=  \u003e=\n// Logical             \u0026\u0026  ||  !\n// Bitwise             \u0026  |  ^  \u003c\u003c  \u003e\u003e\n// Compound assign     +=  -=  *=  /=\n// String concat       \"hello\" + \" world\"\n```\n\n---\n\n## Built-in Functions\n\n### I/O\n\n| Function | Description |\n|---|---|\n| `print(...)` | Print to stdout without newline |\n| `println(...)` | Print to stdout with newline |\n| `eprintln(...)` | Print to stderr with newline |\n| `input(prompt)` | Read a line from stdin |\n\n### Conversion\n\n| Function | Description |\n|---|---|\n| `int(v)` | Convert to integer |\n| `float(v)` | Convert to float |\n| `str(v)` | Convert to string |\n| `bool(v)` | Convert to boolean |\n\n### Math\n\n| Function | Description |\n|---|---|\n| `abs(n)` | Absolute value |\n| `sqrt(n)` | Square root |\n| `pow(base, exp)` | Exponentiation |\n| `floor(n)` / `ceil(n)` / `round(n)` | Rounding |\n| `min(a, b, ...)` / `max(a, b, ...)` | Extremes |\n| `clamp(v, lo, hi)` | Clamp v to [lo, hi] |\n\n### String\n\n| Function | Description |\n|---|---|\n| `len(s)` | Character count |\n| `chars(s)` | Array of single-character strings |\n| `trim(s)` | Remove leading/trailing whitespace |\n| `split(s, sep)` / `join(arr, sep)` | Split / join |\n| `upper(s)` / `lower(s)` | Case conversion |\n| `contains(s, sub)` | Substring check |\n| `starts_with(s, p)` / `ends_with(s, p)` | Prefix / suffix |\n| `replace(s, old, new)` | Replace all occurrences |\n| `repeat(s, n)` | Repeat string n times |\n\n**String methods:**\n\n```ranf\n\"hello\".upper()          // \"HELLO\"\n\"hello\".contains(\"ell\")  // true\n\"a,b,c\".split(\",\")       // [\"a\",\"b\",\"c\"]\n\"42\".parse_int()         // Ok(42)\n\"3.14\".parse_float()     // Ok(3.14)\n```\n\n### Array\n\n| Function | Description |\n|---|---|\n| `len(arr)` | Element count |\n| `push(arr, v)` | New array with v appended |\n| `pop(arr)` | Last element |\n| `insert(arr, i, v)` / `remove(arr, i)` | Insert / remove |\n| `first(arr)` / `last(arr)` | `Some(v)` or `None` |\n| `rest(arr)` | Array without first element |\n| `reverse(arr)` | Reversed array |\n| `contains(arr, v)` | Membership check |\n| `range(n)` / `range(s, e)` / `range(s, e, step)` | Range generation |\n\n### Type Checks \u0026 Unwrap\n\n| Function | Description |\n|---|---|\n| `type_of(v)` | Returns type name as string |\n| `is_some(v)` / `is_none(v)` | Option checks |\n| `is_ok(v)` / `is_err(v)` | Result checks |\n| `unwrap(v)` | Extract value from Some/Ok |\n| `unwrap_or(v, default)` | Extract or return default |\n| `unwrap_err(v)` | Extract error from Err |\n\n### Assertion\n\n| Function | Description |\n|---|---|\n| `assert(cond, msg?)` | Panic if cond is false |\n| `assert_eq(a, b)` | Panic if a != b |\n| `panic(msg)` | Unconditional panic |\n| `exit(code?)` | Exit with status code |\n\n---\n\n## Quick Start\n\n### Prerequisites\n\n- Go **1.25.8** or later\n- `go` in your `PATH`\n\n### Install from Source\n\n```bash\ngit clone https://github.com/risqinf/ranf.git\ncd ranf\ngo build -o ranf ./cmd/ranf\n\n# Optional: install to $GOPATH/bin\ngo install ./cmd/ranf\n```\n\n### Hello World\n\n```bash\necho 'println(\"Hello, World!\")' \u003e hello.ranf\n./ranf run hello.ranf\n# Hello, World!\n```\n\n---\n\n## CLI Reference\n\n```\nUsage:\n  ranf run   \u003cfile.ranf\u003e   Execute a ranf source file\n  ranf check \u003cfile.ranf\u003e   Check syntax and semantics without running\n  ranf repl                Start the interactive REPL\n  ranf version             Print version information\n  ranf help                Print usage\n\nShortcut:\n  ranf \u003cfile.ranf\u003e         Same as 'ranf run \u003cfile.ranf\u003e'\n```\n\n### REPL Commands\n\n```\nranf\u003e :help    — show available commands\nranf\u003e :quit    — exit\nranf\u003e :clear   — reset the environment\n```\n\n---\n\n## Examples\n\nAll examples are in the `examples/` directory:\n\n| File | Demonstrates |\n|---|---|\n| `01_hello.ranf` | Hello World, basic I/O, f-strings |\n| `02_variables.ranf` | Variables, mutability, numeric literals |\n| `03_control_flow.ranf` | if/else, while, for, loop, match |\n| `04_functions.ranf` | Functions, recursion, higher-order patterns |\n| `05_option_result.ranf` | Option\\\u003cT\\\u003e, Result\\\u003cT,E\\\u003e, safe error handling |\n| `06_structs.ranf` | Struct definitions, field access |\n| `07_arrays.ranf` | Arrays, built-in operations, iteration |\n| `08_strings.ranf` | String operations, f-strings, methods |\n| `09_advanced.ranf` | Stack, binary search, FizzBuzz combined |\n\n```bash\nmake run-examples\n```\n\n---\n\n## Extending ranf\n\n### Adding a New Built-in Function\n\nEdit `internal/builtin/builtin.go`:\n\n```go\n// 1. Implement the function\nvar builtinMyFn object.BuiltinFn = func(args []object.Value) (object.Value, error) {\n    if err := checkArity(\"my_fn\", 1, args); err != nil {\n        return nil, err\n    }\n    return \u0026object.Str{V: \"result\"}, nil\n}\n\n// 2. Register it\nr.add(\"my_fn\", builtinMyFn)\n```\n\nAlso add the name to `builtinNames` in `internal/analyzer/analyzer.go`.\n\n### Adding a New Syntax Construct\n\nEach step is fully isolated — changes in one stage never affect another:\n\n```mermaid\nflowchart LR\n    T[\"1. token/\\nadd constant\"] --\u003e\n    A[\"2. ast/\\nadd node struct\"] --\u003e\n    L[\"3. lexer/\\nadd scan logic\"] --\u003e\n    P[\"4. parser/\\nadd parse method\"] --\u003e\n    AN[\"5. analyzer/\\nadd analyzeStmt case\"] --\u003e\n    V[\"6. vm/\\nadd evalStmt case\"]\n\n    style T fill:#534AB7,color:#EEEDFE,stroke:#3C3489\n    style A fill:#185FA5,color:#E6F1FB,stroke:#0C447C\n    style L fill:#0F6E56,color:#E1F5EE,stroke:#085041\n    style P fill:#BA7517,color:#FAEEDA,stroke:#854F0B\n    style AN fill:#993C1D,color:#FAECE7,stroke:#712B13\n    style V fill:#5F5E5A,color:#F1EFE8,stroke:#444441\n```\n\n### Replacing the Evaluator\n\nThe `vm.Service` interface is intentionally minimal:\n\n```go\nfunc (s *Service) Run(prog *ast.Program, env *object.Environment) (object.Value, *errors.RanfError)\n```\n\nTo swap in a bytecode compiler + VM, implement two new services and wire them in `cmd/ranf/main.go` — the lexer, parser, and analyzer remain unchanged.\n\n---\n\n## Contributing\n\n```bash\n# Fork and clone\ngit clone https://github.com/YOUR_USERNAME/ranf.git\n\n# Run tests\nmake test\n\n# Format code\nmake fmt\n\n# Check examples still pass\nmake check-examples \u0026\u0026 make run-examples\n```\n\nAll commits should:\n- Pass `go vet ./...`\n- Pass `gofmt -s -l .` (no formatting differences)\n- Include a test for any new language feature\n\n---\n\n## License\n\nMIT — see [LICENSE](LICENSE)\n\n---\n\n*ranf is built on Go and inspired by Rust's safety philosophy — without Rust's complexity.*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frisqinf%2Franf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frisqinf%2Franf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frisqinf%2Franf/lists"}