{"id":23535067,"url":"https://github.com/ripta/1s","last_synced_at":"2025-11-01T00:30:28.301Z","repository":{"id":218457149,"uuid":"743740367","full_name":"ripta/1s","owner":"ripta","description":"Stack-oriented toy programming language that is peak inefficient.","archived":false,"fork":false,"pushed_at":"2024-04-03T17:11:18.000Z","size":250,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-16T09:43:03.413Z","etag":null,"topics":["interpreter","language","stack-based-language","toy-language"],"latest_commit_sha":null,"homepage":"","language":"Rust","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/ripta.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}},"created_at":"2024-01-15T22:08:49.000Z","updated_at":"2024-02-14T05:05:25.000Z","dependencies_parsed_at":"2024-02-18T10:36:45.854Z","dependency_job_id":null,"html_url":"https://github.com/ripta/1s","commit_stats":null,"previous_names":["ripta/1s"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ripta%2F1s","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ripta%2F1s/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ripta%2F1s/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ripta%2F1s/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ripta","download_url":"https://codeload.github.com/ripta/1s/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239242112,"owners_count":19605954,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["interpreter","language","stack-based-language","toy-language"],"created_at":"2024-12-26T01:14:39.534Z","updated_at":"2025-11-01T00:30:28.272Z","avatar_url":"https://github.com/ripta.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 1s \n\nA toy implementation of a stack-oriented language, where I keep the syntax and\nnative (rust) implementation minimal. There is a prelude, `lib/prelude.1s`,\nthat contains definitions in 1s.\n\n1s is named so, because it has a single primary memory stack. It is also\nreferred to as `one_stack` in parts of the codebase that require a valid\nidentifier that starts with an alpha character.\n\nThe entrypoint is its Makefile, which contains a few useful targets:\n\n```\n# To run the rust REPL with a basis library:\nmake repl\n\n# To run the rust REPL with optimizations:\nmake repl-opt\n\n# To run a wasm32 REPL in your browser, you'll need to build the wasm32 code\n# (\"wasm\") and start the development server (\"web\"):\nmake wasm web\n```\n\n\n## Stack\n\nA stack is described as an ordered list of elements, illustrated from left\n(bottom of the stack / BOS) to right (top of the stack / TOS). For example:\n\n```\n1 2 3 4\n```\n\ndenotes a stack with four elements, the integers 1 through 4. The integer 1 is\nat the bottom of the stack, the integer 2 is on top of 1, the integer 3 is on\ntop of 2, and lastly, the integer 4 is at the top of stack.\n\nA stack has a few operations:\n\n* \"pop\" will take the element at the top of the stack, while preserving the\n  rest of the stack;\n* \"push\" will add an element to the top of the stack, while preserving all\n  elements already on the stack; and\n* \"clear\" will remove all elements from the stack.\n\n## Stack Elements\n\nAn element of the stack may be one of:\n\n* an integer literal, internally represented with a 64-bit signed integer;\n* a float literal, interally represented with a 64-bit floating point\n  conforming to the binary64 type defined in IEEE 754-2008;\n* a string literal, internally represented by a collection of UTF-8 codepoints;\n* a symbol, which is a memory representation of an interned string;\n* a word, which is internally represented by a pair of string and a block; and\n* a block, which is internally represented as a new queue.\n\n## Queue\n\nA queue is described as an ordered list of elements from left (front of the\nqueue / FOQ) to right (back of the queue / BOQ). A queue has a few operations:\n\n* \"dequeue\" will take the element at the front of the queue, preserving the\n  rest of the queue;\n* \"prepend\" will add an element to the front of the queue, preserving all\n  elements already in the queue;\n* \"enqueue\" will add an element to the back of the queue, preserving all\n  elements already in the queue; and\n* \"clear\" will remove all elements from the queue.\n\n## Execution Environment\n\nThe execution environment for 1s consists of:\n\n* a program queue,\n* a memory stack,\n* a collection of words, and\n* a set of symbols representing interned strings.\n\nThe state of the execution environment can be thought of as specific values for\neach of the above.\n\nWhile all parts of the execution environment are important, illustrating all\nwords and symbols may prove cumbersome and overwhelming to the reader. Instead,\nit may be useful to elide everything but the memory stack and program queue.\n\nIn fact, the memory stack and the program queue are useful to think\nside-by-side. They can be illustrated by placing the stack to the left of a\nwhite diamond `◇` and the program to the right of the white diamond `◇`.\n\nAs such, the program queue `1 2 +` containing an integer literal 1, integer\nliteral 2, and word `+`, with an empty stack can be shown like so:\n\n```\n◇ 1 2 +\n```\n\n## Program Queue\n\nThe program queue is evaluated from the FOQ. Additional program tokens are\nenqueued to the BOQ. Each token evaluated affects the stack.\n\nA word causes the word's program to be prepended to the program queue, while\nliterals cause the literal to be pushed onto the memory stack when evaluated.\n\n## Evaluation\n\nA single evaluation step happens by dequeueing from the program queue, and\nperforming an operation depending on the token that was dequeued.\n\nFor example, in the environment:\n\n```\n◇ 1 2 +\n```\n\nthe next step would be to dequeue the integer literal 1, and then--as is the\ncase with integer literals--to push the integer literal onto the stack:\n\n```\n1 ◇ 2 +\n```\n\nThe same with the integer literal 2 at the next step:\n\n```\n1 2 ◇ +\n```\n\nLast but not least is to dequeue the word `+`, which translates to the\noperation of popping two elements from the top of the stack, adding them\ntogether, and then pushing the result onto the stack.\n\nSince `+` is a primitive word (described in the next section), its operation is\nconsidered atomic from the point of view of the environment, and as such only\ntakes one step, resulting in:\n\n```\n3 ◇\n```\n\nThe program halts when no tokens are available in the program queue. The result\nof the evaluation is the entire stack.\n\nIn our example, the result of the evaluation is the integer literal 3.\n\nAnother way to represent the result of the evaluation is with `==`, where the\nprogram to the left of `==` terminates and results in the stack on the right of\nthe `==`. The above example can be written:\n\n```\n1 2 + == 3\n```\n\nwhich shows that the program `1 2 +` is equivalent to `3`. This can be further\ngeneralized by replacing any instance of `1 2 +`, even as part of a different\nprogram, with `3`. For example:\n\n```\n4 1 2 + * == 4 3 * == 12\n```\n\nbecause:\n\n```\n◇ 4 1 2 + *\n4 ◇ 1 2 + *\n4 1 ◇ 2 + *\n4 1 2 ◇ + *\n4 3 ◇ *\n12 ◇\n```\n\n## Word\n\nA word is either:\n\n* a primitive, which is a word implemented in native (rust) code; or\n* a compound, which is a word implemented in 1s itself.\n\nA primitive word, by convention, is surrounded by curly braces `{` and `}`. A\nprimitive word cannot be overridden at runtime.\n\nA compound word defined in 1s is associated with a program stack, which is the\nimplementation of the word using other words. There is no theoretical limit as\nto the number of compound words that a compound word may rely on. A compound\nword may contain alphanumeric characters and symbols. A compound word may\ninclude `{` and `}`, though their use is discouraged to reduce confusion\nbetween primitive words (that cannot be overridden at runtime) and compound\nwords (that _can_ be overridden at runtime).\n\nEvery primitive word has an alias of one or more compound words. These aliases\nare defined as part of the prelude. The aliases look like any other compound\nword, and may be overridden at runtime.\n\n## Binding and Quoting\n\nBinding is the operation of associating one or more words with a block (program\nqueue). The binding operation is denoted by `{:}` and is aliased as `:`.\n\nFor example, consider the doubling of an integer.\n\n```\n1 2 * == 2\n3 2 * == 6\n9 2 * == 18\n```\n\nand so on. Such an operation could be generalized as the word `double`:\n\n```\n[ 2 * ] [ double ] :\n```\n\nwhere we associate the program block `[ 2 * ]` to the word `double`. Both the\nprogram and the word being bound must appear in blocks `[` and `]` to prevent\nthem from being evaluated.\n\nIt can then be used like so:\n\n```\n1 double == 2\n3 double == 6\n9 double == 18\n```\n\nFor illustrative purposes, the evaluation might look like:\n\n```\n◇ 9 double\n9 ◇ double\n9 ◇ 2 *\n9 2 ◇ *\n18 ◇\n```\n\nwhere the word `double` is expanded to `2 *` when it is evaluated, because\nevaluating a word causes the word's program to be prepended to the program\nqueue.\n\nThe fact that the word being bound must also appear in a block is useful to\nallow one to bind multiple words to the same program. For example, to allow\n`double` to also be used as `times-two`:\n\n```\n[ 2 * ] [ double times-two ] :\n```\n\nsuch aliasing has the same effect as:\n\n```\n[ 2 * ] [ double ] :\n[ double ] [ slower-times-two ] :\n```\n\nbut is more efficient. `slower-times-two` takes one more evaluation step,\nbecause the word must be expanded from `slower-times-two` to `double` to `2 *`:\n\n```\n◇ 9 slower-times-two\n9 ◇ slower-times-two\n9 ◇ double\n9 ◇ 2 *\n9 2 ◇ *\n18 ◇\n```\n\nBecause words can contain special characters, including numbers, one can define\nwords that look like sequences of other words. Discretion is advised.\n\n```\n[ 2 * ] [ 2* ] :\n\n◇ 4 2*\n4 ◇ 2*\n4 ◇ 2 *\n4 2 ◇ *\n8 ◇\n```\n\n## Binary Arithmetic Operations\n\nBy convention, binary arithmetic operations are evaluated in reverse order with\nrespect to the top of the stack. That is,\n\n```\n1 2 +\n```\n\nis evaluated as `1 + 2`. This technical distinction matters in noncommutative\noperations, such as division:\n\n```\n12 3 / == 4\n```\n\n## Numeric Types\n\nNumeric types are not automatically convertible or casted. Mixing numeric types\nwill cause the \"incompatible value on stack\" error:\n\n```\n12.0 3 / == #error\nError: incompatible value on stack: expected 'integer', but found 'FloatValue(12.0)'\n```\n\n## Errors\n\nErrors automatically halt execution, and leave the program queue and memory\nstack as is. There is no automatic way to recover from errors and continue\nexecution.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fripta%2F1s","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fripta%2F1s","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fripta%2F1s/lists"}