{"id":16081987,"url":"https://github.com/l1mey112/tl-v","last_synced_at":"2026-01-19T14:33:38.255Z","repository":{"id":113618982,"uuid":"575708839","full_name":"l1mey112/tl-v","owner":"l1mey112","description":"A tiny, proof of concept, programming language.","archived":false,"fork":false,"pushed_at":"2022-12-08T07:25:11.000Z","size":17,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-05T11:43:34.085Z","etag":null,"topics":["ast","compiler","compiler-design","gas","programming-language","vlang","x86-64"],"latest_commit_sha":null,"homepage":"","language":"V","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/l1mey112.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2022-12-08T05:47:04.000Z","updated_at":"2024-05-17T20:05:48.000Z","dependencies_parsed_at":null,"dependency_job_id":"5ef76442-28ee-4e27-a576-ac6fe5b3d6c1","html_url":"https://github.com/l1mey112/tl-v","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/l1mey112/tl-v","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/l1mey112%2Ftl-v","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/l1mey112%2Ftl-v/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/l1mey112%2Ftl-v/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/l1mey112%2Ftl-v/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/l1mey112","download_url":"https://codeload.github.com/l1mey112/tl-v/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/l1mey112%2Ftl-v/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28571804,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-19T12:50:50.164Z","status":"ssl_error","status_checked_at":"2026-01-19T12:50:42.704Z","response_time":67,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["ast","compiler","compiler-design","gas","programming-language","vlang","x86-64"],"created_at":"2024-10-09T11:24:55.867Z","updated_at":"2026-01-19T14:33:38.237Z","avatar_url":"https://github.com/l1mey112.png","language":"V","funding_links":[],"categories":[],"sub_categories":[],"readme":"# t(iny)l(ang)\n\nA tiny, proof of concept, programming language.\n\n```\nproc main {\n\tr = 0;\n}\n```\n\nThe creation of `tl` and it's compiler were used to learn the basics of conventional AST based compilers after the completion of the stack based [`stas`](https://github.com/l1mey112/stas) programming language.\n\n## Syntax\n\nThe syntax of of `tl` is a mingle of C with some alterations. For example, if statements and while loops do not need parentheses circling the condition.\n\nYou cannot declare any variables, as there are a limited amount of predefined ones.\n\n```\ns0, s1, s2, s3, s4, s5 | Stack variables\na0, a1, a2, a3, a4, a5 | Argument variables\nr                      | Return variable\n```\n\nThe stack variables are used to store local values. Use the argument variables to pass values to function calls, expect them to be destroyed on return. The return variable is used to pass return values on return.\n\nSince function calls to not return values, they are not expressions. Use the `call` keyword to call a function.\n\n```\ncall function;\n\ns0 = function;\ncall s0;\n```\n\nThere is only one builtin function, `print`. It is used to print the value of one number in `a0`.\n\nPredefining an array of values is also supported using the `data` keyword, mainly used to test out pointer indexing.\n\nExamples of `tl` programs are in the [`examples/`](./examples/) folder. \n\n\u003c!-- talk about for learning --\u003e\n\n## The `tl` compiler\n\nThe `tl` compiler is split into 3 equally small files, the [lexer](./lexer.v), [parser](./parser.v) and [code generator](./gas.v).\n\n## Lexer\n\nThe lexer is a construct that performs lexical analysis on a string of characters read from a file. The [`Lexer.get()`](./lexer.v#L23) function is used to scan and return a single token. It scans tokens linearly, with zero backtracking. No past tokens are stored, the parser constantly requests new tokens directly with the functions [`Lexer.expect()`](./lexer.v#142), [`Lexer.next()`](./lexer.v#147) and [`Lexer.curr()`](./lexer.v#153).\n\n## Parser\n\nThe parser parses the stream of tokens into an [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) representation. The entrypoint to the parser is the [`Parser.parse()`](./parser.v#281) function, returning the root AST node in the tree.\n\nIt is a recursive parser, starting by parsing toplevel statements such as function declaration and hardcoded arrays. Whilst inside a function body it then constantly calls the [`Parser.stmt()`](./parser.v#180) to build up all statements inside it. This function handles all language statements/constructs, like if statements and while loops. The [`Parser.expr()`](./parser.v#128) function is used to parse and build up an expression tree with respect to operator precedence. Each specific expression parser works by eating up a operator token until it encounters once with a higher precedence, then calling up the chain of expression parsing functions.\n\n- [`Parser.expr()`](./parser.v#128) - Highest Right precedence (start here)\n\t- `=`\n- [`Parser.expr1()`](./parser.v#104) - Lowest Left precedence\n\t- `\u003e`, `\u003e=`, `\u003c`, `\u003c=`, `==`, `!=`\n- [`Parser.expr2()`](./parser.v#90) - Left precedence\n\t- `+`, `-`\n- [`Parser.expr3()`](./parser.v#76) - Left precedence\n\t- `*`, `/`\n- [`Parser.expr4()`](./parser.v#59) - Left precedence\n\t- `index[]`\n- [`Parser.term()`](./parser.v#42) - Highest precedence\n\t- `123`, `variable`, `(paren expr)`\n\nFor example, this line of code will generate this AST.\n\n```\nproc main {\n\ts0 = 10 + 2 * 4;\n}\n```\n```\ns_proc\n  expr\n   assign\n    ident `s0`\n    add\n     dec `10`\n     mul\n      dec `2`\n      dec `4`\n```\n\n## Code Generator\n\nThe code generator takes the tree representation of the program and generates x86_64 assembly of GNU dialect to be used with GCC.\n\nThe AST tree format is especially useful as it is an abstraction over the original source code, so the code generator can simply recurse over all nodes in the tree generating code. The entrypoint to the code generator is the [`Gen.generate_all()`](./gas.v#250).\n\nThe code generator takes inspiration from V's native backend, specifically with how expressions are handled. It is extremely simple, and generates a lot of useless code, however it gets the job done. Modern compiler optimisations require the AST to be converted to a lower level format first, adding a large amount of complexity and translations.\n\nExpressions are handled in the [`Gen.expr()`](./gas.v#64) function. They are generated by traversing the left path of the current node first. On return, the left path's expression ends up in the `rax` register, it is then moved to the `rcx` register and right path of the current node is traversed. When done, the `rax` and `rcx` register contains the left path and the right path respectively and are used to perform an operation such as arithmetic or a comparison. This form of divide and conquer recursion allows the code generator to be quite lightweight.\n\n```\nproc main {\n\t2 + 4 * 10;\n}\n```\n\n```nasm\n    mov rax, 10\n    push rax\n    mov rax, 4\n    pop rcx\n    imul rax, rcx\n    push rax\n    mov rax, 2\n    pop rcx\n    add rax, rcx\n```\n\n## Building and compilation\n\nUse the [`v`](https://vlang.io/) compiler to generate the compiler. Call the compiler with the program, then pipe the output to GCC.\n\n```\nv -g . -o tl\n./tl main.tl | gcc -x assembler -\n./a.out\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fl1mey112%2Ftl-v","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fl1mey112%2Ftl-v","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fl1mey112%2Ftl-v/lists"}