{"id":16912228,"url":"https://github.com/skius/stringlang","last_synced_at":"2026-02-06T21:32:12.416Z","repository":{"id":57559341,"uuid":"324174437","full_name":"skius/stringlang","owner":"skius","description":"An interpreted, expression-oriented language where everything evaluates to strings","archived":false,"fork":false,"pushed_at":"2021-05-14T20:30:13.000Z","size":1702,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-01T12:20:00.838Z","etag":null,"topics":["golang","hacktoberfest","interpreter","language","programming-language"],"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/skius.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}},"created_at":"2020-12-24T14:31:02.000Z","updated_at":"2023-12-29T15:06:24.000Z","dependencies_parsed_at":"2022-08-28T14:02:45.496Z","dependency_job_id":null,"html_url":"https://github.com/skius/stringlang","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/skius/stringlang","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skius%2Fstringlang","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skius%2Fstringlang/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skius%2Fstringlang/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skius%2Fstringlang/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/skius","download_url":"https://codeload.github.com/skius/stringlang/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skius%2Fstringlang/sbom","scorecard":{"id":829739,"data":{"date":"2025-08-18","repo":{"name":"github.com/skius/stringlang","commit":"10c60c92f99b6ee45d88b74c5cfbf04e96f62e47"},"scorecard":{"version":"v5.2.1-41-g40576783","commit":"40576783fda6698350fcbbeaea760ff827433034"},"score":2.7,"checks":[{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#binary-artifacts"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#token-permissions"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#sast"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#license"}},{"name":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact v0.1.0 not signed: https://api.github.com/repos/skius/stringlang/releases/35710538","Warn: release artifact v0.1.0 does not have provenance: https://api.github.com/repos/skius/stringlang/releases/35710538"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-23T17:26:25.742Z","repository_id":57559341,"created_at":"2025-08-23T17:26:25.742Z","updated_at":"2025-08-23T17:26:25.742Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29177514,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-06T20:14:21.878Z","status":"ssl_error","status_checked_at":"2026-02-06T20:14:21.443Z","response_time":59,"last_error":"SSL_read: 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":["golang","hacktoberfest","interpreter","language","programming-language"],"created_at":"2024-10-13T19:08:58.960Z","updated_at":"2026-02-06T21:32:12.402Z","avatar_url":"https://github.com/skius.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# StringLang\n\nAn interpreted, expression-oriented language where everything evaluates to strings.\n\n## Usage\n\n### Installation\n\nYou need Go to build the official interpreter from this repository.  \nRun `go get github.com/skius/stringlang/cmd/stringlang` to install the CLI on your machine.\n\n### CLI\n\nRun StringLang programs using `stringlang \u003cprogram.stringlang\u003e \u003carg0\u003e \u003carg1\u003e ...`,\nor alternatively run the StringLang REPL by running `stringlang` with no arguments.\n\n### Running from code\n\nTo interpret StringLang code from your Go program, all you need is the following:\n\n```go\npackage main\n\nimport (\n   \"github.com/skius/stringlang\"\n   \"os\"\n   \"strconv\"\n)\n\nfunc main() {\n   source := `\"Replace me with the \" + \"source code of your StringLang program\"`\n   expr, err := stringlang.Parse([]byte(source))\n   if err != nil {\n      panic(err)\n   }\n   // The built-in functions your StringLang program will have access to\n   funcs := map[string]func([]string) string{\n      \"length\": func(as []string) string { return strconv.Itoa(len(as[0])) },\n      // Add more built-in functions here\n   }\n   // Arguments to your StringLang program\n   args := os.Args\n   ctx := stringlang.NewContext(args, funcs)\n   result := expr.Eval(ctx)\n}\n```\n\nSee the CLI's [main.go](cmd/stringlang/main.go) for a more advanced example.\n\n### Contributing\n\nFeel free to open Issues and Pull Requests! The language specification and interpreter is by no means final.\nTo change the syntax of the language, you'll need to modify `lang.bnf` and also `ast/ast.go` if you need new structures.\nEvery time you change `lang.bnf`, you need to run `gocc -o \"./internal/frontend\" -p \"github.com/skius/internal/frontend\" lang.bnf` to generate the new parser and lexer.\n\nGet the parser generator `gocc` used in this project from: [goccmack/gocc](https://github.com/goccmack/gocc)\n\n### Example StringLang programs\n\nSee [`stringlang_programs/`](stringlang_programs)\n\n## Syntax\n\nThe following code block contains a simple (and not 100% correct, see Caveats section below the block) overview of what\nconstitutes a `StringLang` source file. The complete grammar can be found in `lang.bnf`.\n\n```\nidentifier: any string of alphanumeric and underscore characters not beginning with a digit\nnumber: non-negative integer\nstring_literal: a \"double-quoted\" string, containing any sequence of characters, escapes allowed using '\\'\ncomment: any text enclosed by /* and */, except '/*' and '*/' (no nested comments)\n\n\nprogram:\n     header block           \n\nheader:\n    function1 function2 ... functionN                       // N may be 0  \n\nfunction:\n    fun identifier(param1, param2, ..., paramN) { block }   // paramX are identifiers, N may be 0\n\nblock:\n    expression1; expression2; ...; expressionN              // no trailing semi-colon and N cannot be 0\n\nexpression:\n    string_literal\n    identifier\n    identifier = expression\n    $number\n    expression[expression]\n    expression[number]\n    expression(expression1, expression2, ..., expressionN)  // N can be 0\n    \n    expression || expression\n    expression \u0026\u0026 expression\n    expression != expression\n    expression == expression\n    expression + expression\n    \n    (expression)\n    \n    ifelse\n    while\n    \n    lambda\n    \nifelse:\n    if (expression) { block } else { block }\n    if (expression) { block } else ifelse                   // This effectively allows else-if \n    \nwhile:\n    while (expression) { block }\n\nlambda:\n    fun(param1, param2, ..., paramN) { block }              // paramX are identifiers, N may be 0\n```\n\n### Caveats\nWhile the above description gives a good overview of `StringLang`, there are some important notes to be made:\n1) `identifier = expression` may only appear (but possibly repeated) at the beginning of an expression without parentheses, e.g. \n   `a = b = c = \"foo\";`. Otherwise they need to be inside parentheses, e.g. `\"foo\" + (a = c = \"bar\")`.\n2) A block is a non-empty, `;` -separated list of expressions, with no trailing `;`.\n3) A call (`identifier(expr1, ..., exprN)`) may also be without arguments, i.e. `identifier()`\n\n## Semantics\n\n### Values\n\nAll expressions/values in `StringLang` are Strings.\n\n### Functions\n\nFunctions in `StringLang` can either be user-defined (i.e. they are written in `StringLang` and reside\nas a top-level function in the header of the interpreted file, or are lambdas), or they can be built-in \n(i.e. supplied to the interpreter via the [context](#context-functions-and-arguments)). \nBecause built-in functions are written in Go, they can accomplish anything Go can accomplish. \n\nUser-defined `StringLang` functions are evaluated with their own completely separate variable scope and\nare mutually recursive. The arguments passed to them are bound to the variables in the corresponding parameter lists.\n\nAll functions in `StringLang` are pass-by-value, hence also strict.\n\n#### Calls\n\nIn a `expr(args...)` call, finding the function corresponding to `expr` has the following order:\n1. Check if `expr` is an `identifier` equal to some `f` and that there exists a *top-level, user-defined* function \n   `fun f(params) { block }`, if so, call that function.\n2. Check if `expr` is an `identifier` equal to some `f` and that there exists a *built-in* \n   function that is mapped to that `f` in the provided [context](#context-functions-and-arguments), if so, call that.\n3. If `expr` is not an `identifier` with corresponding named function, then [evaluate](#evaluation) `expr` and treat the\n   resulting String as source code of a `lambda`, then call that lambda.\n\n#### Lambdas\n\nLambdas in StringLang capture their environment by-value, and encode it as simple assignments at the top of the `block`.\nEvaluating them (which is different to calling them) results in a canonical source representation of the lambda.  \n\nExample: The expression `fun(x) { x + y }` is evaluated differently depending on its context. In particular,\nthe run-time value of the `identifier` `y` will be copied into the block, e.g. assuming `y` evaluates to `\"value\"`\nin the current context the lambda becomes: `fun(x) { y = \"value\"; x + y }`, a context-insensitive lambda. Now it can\nbe evaluated into a String and returned: \n```\n\"fun(x) {\n   y = \\\"value\\\";\n   x + y\n}\"\n```\nAt the site of a [call](#calls), this String can now be interpreted as the source code of a lambda, and called as such.  \nNote that because we stored the value of `y` inside the lambda, the context in which we call this \"lambda-turned-String\"\ndoes not matter.\n\nAlso note that lambdas are still user-defined functions, which are evaluated using a separate variable scope. Hence the\n`y = \"value\"` in the block will not cause the caller's `y` to be set to `\"value\"`.\n\nIf you need more examples, see [lambdas.stringlang](stringlang_programs/lambdas.stringlang) or\n[ski_combinator.stringlang](stringlang_programs/ski_combinator.stringlang) to see how the SK-calculus looks like in\nStringLang.\n\n\n\n### Binary Operators\n\nThe following list gives the binary operators, ordered from low to high precedence:\n```\n||   Or              Interprets operands as booleans           \n\u0026\u0026   And             Interprets operands as booleans  \n!=   Not Equals      Compares the values of the operands\n==   Equals          Compares the values of the operands\n+    Concatenation   Concatenates the operands\n```\n#### Note: \n1) The boolean value of a `StringLang` value is `true` if and only if the String is not `\"\"` and \n   not `\"false\"`, else it is `false`.\n2) The `StringLang` value of a boolean result of a logic operation is always either `\"true\"` or `\"false\"` \n\n### Evaluation \n```\nstring_literal ------------ The value of 'string_literal'\nidentifier ---------------- The current value of the variable with identifier 'identifier'.\nidentifier = expression --- The value of 'expression'. \n                            Side effect: Variable 'identifier' now has that value.\n$number ------------------- The value of the 'number'-th (zero-indexed) argument to the program.\nexpr1[expr2 or number] ---- The character at position 'expr2' resp. 'number' of the value \n                            that 'expr1' evaluates to.\nexpr(expr1, ...) ---------- Function call to function 'expr' (See 'Functions' section) with arguments 'expr1, ...'.\n                            Arguments are evaluated before passed to the function.\n                            Evaluates to the function's return value.\nexpr1 \u003cbinop\u003e expr2 ------- The value of the corresponding binary operation. \n\nifelse -------------------- The value of the then-block if the condition evaluates to a true value,\n                            or the value of the else-block if the condition evaluates to a false value.\nwhile --------------------- The value of the last iteration of the block if it gets executed once, else \"\".\n                            Side effects: All iterations might cause side effects\n                            \nlambda -------------------- The canonical source representation of the whole lambda as string, but with the blocks's\n                            used variables captured by-value in the lambda.\n                            \nblock --------------------- The value of the last expression in the block.\n                            Side effects: Evaluates all expressions in the list.\n\n```\n\n#### Exceptions, errors, defaults\n\nIn the case of an invalid (out-of-bounds or NaN) character-access or argument expression,\nthe returned value is always `\"\"`. The values of all variables are initialized to `\"\"`.\n\n### Context (functions and arguments)\n\nTo evaluate a `StringLang` program, the interpreter needs a `stringlang.Context` object.\nIt contains fields which allow the interpreter's user to supply their custom built-in functions and arguments to the program.\nSee `cmd/stringlang/main.go` for an example.\n\nThere is a channel available with `context.GetExitChannel()` for quickly killing the whole evaluation.\nAdditionally, one can set the (approximate) maximum stack space in bytes the `StringLang` program is allowed to use with\n`context.SetMaxStackSize(int)` to a non-negative number. `cmd/stringlang/main.go` also contains examples\nfor these two features. \n\n## Why?\n\nIt started out as a simple String builder using provided arguments (read: `\"my string\".replace(\"$0\", args[0])...`) \nfor a chat bot, which allowed generating new commands in-chat. \nAfter switching to a parsed grammar for some simple operators, I figured why not just make it Turing-complete.\nWhat could possibly go wrong allowing users to build their own commands...\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskius%2Fstringlang","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fskius%2Fstringlang","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskius%2Fstringlang/lists"}