{"id":15527531,"url":"https://github.com/elliotchance/bento","last_synced_at":"2025-04-23T12:28:23.721Z","repository":{"id":137289306,"uuid":"195623589","full_name":"elliotchance/bento","owner":"elliotchance","description":"🍱 bento is an English-based automation language designed to be used by non-technical people.","archived":false,"fork":false,"pushed_at":"2019-08-07T13:53:27.000Z","size":133,"stargazers_count":32,"open_issues_count":22,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-29T23:41:12.290Z","etag":null,"topics":["4gl","automation","english","golang","qa"],"latest_commit_sha":null,"homepage":"","language":"Go","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/elliotchance.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":"2019-07-07T07:39:18.000Z","updated_at":"2024-04-02T17:45:22.000Z","dependencies_parsed_at":"2024-06-20T17:31:08.655Z","dependency_job_id":"4d7514c0-31ba-4bab-b0e1-f1507f3504fa","html_url":"https://github.com/elliotchance/bento","commit_stats":null,"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elliotchance%2Fbento","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elliotchance%2Fbento/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elliotchance%2Fbento/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elliotchance%2Fbento/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elliotchance","download_url":"https://codeload.github.com/elliotchance/bento/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250433447,"owners_count":21429875,"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":["4gl","automation","english","golang","qa"],"created_at":"2024-10-02T11:06:56.875Z","updated_at":"2025-04-23T12:28:23.700Z","avatar_url":"https://github.com/elliotchance.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🍱 bento\n\n   * [Getting Started](#getting-started)\n      * [Installation](#installation)\n      * [Running A Program](#running-a-program)\n   * [Example Use Case](#example-use-case)\n   * [Language](#language)\n      * [File Structure](#file-structure)\n      * [Sentences](#sentences)\n         * [Wrapping Long Sentences](#wrapping-long-sentences)\n      * [Comments](#comments)\n      * [Variables](#variables)\n         * [Blackhole](#blackhole)\n         * [Text](#text)\n         * [Number](#number)\n            * [Mathematical Operations](#mathematical-operations)\n      * [Functions](#functions)\n         * [Arguments](#arguments)\n         * [Questions](#questions)\n      * [Controlling Flow](#controlling-flow)\n         * [Conditions](#conditions)\n         * [Decisions (if/unless)](#decisions-ifunless)\n         * [Loops (while/until)](#loops-whileuntil)\n   * [Backends](#backends)\n      * [Locating and Starting Backends](#locating-and-starting-backends)\n      * [Communication Protocol](#communication-protocol)\n         * [Request](#request)\n         * [Response](#response)\n         * [Special Cases](#special-cases)\n            * [sentences](#sentences-1)\n      * [Examples](#examples)\n         * [PHP](#php)\n      * [System](#system)\n   * [Examples](#examples-1)\n      * [Hello, World!](#hello-world)\n      * [Variables](#variables-1)\n      * [Functions (Custom Sentences)](#functions-custom-sentences)\n\nbento is a\n[forth-generation programming language](https://en.wikipedia.org/wiki/Fourth-generation_programming_language)\nthat is English-based. It is designed to separate orchestration from\nimplementation as to provide a generic, self-documenting DSL that can be managed\nby non-technical individuals.\n\nThat's a bunch of fancy talk that means that the developers are able to\nsetup the complex tasks and make them easily rerunnable by non-technical people.\n\nThe project is still very young, but it has a primary goal for each half of its\nintended audience:\n\n**For the developer:** Programs can be written in any language and easily\nexposed through a set of specific DSLs called \"sentences\".\n\n**For the user:** An English-based language that avoids any nuances that\nnon-technical people would find difficult to understand. The language only\ncontains a handful of special words required for control flow. Whitespace and\ncapitalization do not matter.\n\n# Getting Started\n\n## Installation\n\nBento is available for Mac, Windows and Linux. You can download the latest\nrelease from the [Releases](https://github.com/elliotchance/bento/releases)\npage.\n\n## Running A Program\n\nCreate a file called `hello-world.bento` with the contents:\n\n```bento\nstart:\n\tdisplay \"Hello, World!\"\n```\n\nThen run this file with (you may need to replace the path to the file if you are\nnot in the same directory as the file):\n\n```bash\nbento hello-world.bento\n```\n\n# Example Use Case\n\nThe sales team need to be able to run customer reports against a database. They\ndo not understand SQL, or the reports may need to be generated from multiple\ntools/steps. As the developer, your primary options are:\n\n1. Run the reports for them. This is a waste of engineering time and a blocker\nfor the sales team if they need to do it frequently, or while you're on holiday.\n\n2. Write up some documentation and give them readonly access to what they need.\nThe process could be highly technical. Also, allowing them to access production\nsystems is a bad idea. Also, it's still a waste of time for everyone when things\naren't working as expected.\n\n3. Build a tool that runs the reports for them. This is another rabbit hole of\nwasted engineering time, especially when requirements change frequently or they\nneed a lot of flexibility.\n\n4. Build the reporting queries/tasks once in bento, and provide them with a\nscript that they can edit and rerun. An example might look like:\n\n```\nrun sales report for last 7 days for customer 123\nif there were sales, email report to \"bob@mycompany.com\"\n```\n\nThe developer would implement the following sentences:\n\n```\nrun sales report for last ? days for customer ?\nthere were sales\nemail report to ?\n```\n\n# Language\n\n## File Structure\n\nA `.bento` file consists of one or more functions. Each of those functions can\ncontain zero or more sentences and call other sentences before of after they are\ndefined.\n\nThe file must contain a `start` function. This can be located anywhere in the\\\nfile, but it is recommended to put it as the first function.\n\n## Sentences\n\nA sentence contains a collection of words and values and it is terminated by a\nnew line. For example:\n\n```\ndisplay \"Hello\"\n```\n\n### Wrapping Long Sentences\n\nYou can explicitly use `...` at the end of the line to indicate a continuation:\n\n```bento\nthis is a really long...\n\tsentence that should go...\n\tover multiple lines\n```\n\nIndentation between lines does not make an difference. However, it is easier to\nread when following lines are indented.\n\nSentences can also contains new lines if the line ends with a `,`. This is\nuseful for long inline statements:\n\n```bento\nif my-name != \"John\",\n  display \"oops!\",\n  otherwise display \"All good.\"\n```\n\n## Comments\n\nComments start with a `#` and continue until a new line or the end of the\nprogram is reached. The comment may be on its own line or at the end of a\nsentence:\n\n```\n# A comment looks like this.\ndisplay \"Hello\" # It can also be here\n```\n\n## Variables\n\n```\ndeclare first-name is text\ndeclare counter is a number\n```\n\n1. Only `text` and `number` is supported. See specific documentation below.\n2. The word `a` or `an` may appear before the type. This can make it easier to\nread: \"is a number\" rather than \"is number\". However, the \"a\" or \"an\" does not\nhave any affect on the program.\n3. All variables for a function must be declare before any other sentences.\n4. The same variable name cannot be defined twice within the same function.\nHowever, the same variable name can appear in different functions. These are\nunrelated to each other.\n5. You cannot declare a variable with the same name as one of the function\nparameters.\n6. All types have a default value which is safe to use before it is set to\nanother value.\n7. There is a special variable called `_` which is called the blackhole.\nExplained in more detail below.\n\nVariables can be set with:\n\n```\nset first-name to \"Bob\"\n```\n\n### Blackhole\n\nThe blackhole variable is a single underscore (`_`). It can be used as a\nplaceholder when the value can be ignored. For example:\n\n```bento\ndivide 1.23 by 7.89 into _\n```\n\nIf `_` is used in a place where the value would be read it will return a zero\nvalue (the same as the default value for that type).\n\nThe following lines would function the same. However, you should not rely on the\nblackhole variable as a readable value and it may be made illegal in the future:\n\n```bento\nadd _ and 7.89 into result\nadd 0 and 7.89 into result\n```\n\n### Text\n\n```bento\nmy-variable is text\n```\n\n1. A `text` variable can contain any text, including being empty (zero\ncharacters).\n2. It's perfectly safe to use text variables before they have been given a\nvalue, the default value will be empty.\n\n### Number\n\n```bento\nmy-variable is number\nmy-variable is number with 1 decimal place\nmy-variable is number with 3 decimal places\n```\n\n1. A number variable is exact and has a maximum number of decimal places (this\nis also called the precision).\n2. If the number of decimal places is not specified it will use 6.\n3. For integers you should use `number with 0 decimal places`.\n4. The number of decimal places cannot be negative.\n5. A number has no practical minimum (negative) or maximum (positive) value. You\ncan process incredibly large numbers with absolute precision.\n6. Any calculated value will be rounded at the end of the operation so that it\nnever contains more precision than what is allowed. For example if the number\nhas one decimal place, `5.5 * 6.5 * 11` evaluates to `393.8` because\n`5.5 * 6.5 = 35.75 =\u003e 35.8`, `35.8 * 11 = 393.8`.\n7. Numbers are always displayed without trailing zeroes after the decimal point.\nFor example, `12.3100` is displayed as `12.31` as long as the number of decimal\nplaces is at least 2.\n8. The words `places` and `place` mean the same thing. However, it is easier to\nread when `place` is reserved for when there is only one decimal place.\n9. The default value of a `number` is `0`. This is safe to use use before it has\nbeen set.\n\n#### Mathematical Operations\n\n```bento\nadd a and b into c          # c = a + b\nsubtract a from b into c    # c = b - c\nmultiply a and b into c     # c = a * b\ndivide a and b into c       # c = a / b\n```\n\nNote: Be careful with `subtract` as the operands are in the reverse order of the\nothers.\n\n## Functions\n\nFunctions (custom sentences) can be defined by using the `:` character:\n\n```\nprint everything:\n\tdisplay \"Hello\"\n\tdisplay \"World\"\n```\n\nThe whitespace is not required. However, it is easier to read when content of\nfunctions are indented with spaces or tabs.\n\n### Arguments\n\nVariables can be declared in the function name by specifying their names and\ntypes in `()`, for example:\n\n```\nsay greeting to persons-name (greeting is text, persons-name is text):\n\tdisplay greeting\n\tdisplay persons-name\n```\n\nCan be called with:\n\n```\nsay \"Hi\" to \"Bob\"\n```\n\nThe order in which the arguments are defined is not important.\n\n### Questions\n\nA question is a special type of function that is defined with a `?` instead of a\n`:`:\n\n```bento\nit is ok?\n\tyes\n```\n\nA question is answered with the `yes` or `no` sentences. Once a question is\nanswered it will return immediately.\n\nIf a question is not explicitly answered by the end, it's assumed to be `no`.\n\nQuestions can be asked in conditionals:\n\n```bento\nstart:\n\tif it is ok, display \"All good!\"\n```\n\nQuestions can also take arguments in the same way that functions do:\n\n```bento\nstart:\n\tdeclare x is number\n\n\tset x to 123\n    if x is over 100, display \"It's over 100\", otherwise display \"Not yet\"\n\nthe-number is over threshold (the-number is number, threshold is number)?\n\tif the-number \u003e threshold, yes\n```\n\n## Controlling Flow\n\n### Conditions\n\nA condition is a simple comparison between two variables or values. Some\nexamples are:\n\n```\nname = \"Bob\"\ncounter \u003e 10\nfirst-name != last-name\n```\n\nAll supported operators are:\n\n- `=` - Equal.\n- `!=` - Not equal.\n- `\u003e` - Greater than.\n- `\u003e=` - Greater than or equal.\n- `\u003c` - Less than.\n- `\u003c=` - Less than or equal.\n\nValues can only be compared when they are the same type. For example the\nfollowing is not allowed, and will return an error:\n\n```\n\"123\" = 123\n```\n\n### Decisions (if/unless)\n\nSentences starting with `if` or `unless` can be used to control the flow. The\nsentence takes one of the following forms (each either starting with `if` or\n`unless`):\n\n```\nif/unless \u003ccondition\u003e, \u003ctrue\u003e\n\nif/unless \u003ccondition\u003e, \u003ctrue\u003e, otherwise \u003cfalse\u003e\n```\n\nWhen `unless` is used instead of `if` the comparison is inverted, so that:\n\n```\nif \"Bob\" = \"Bob\"      # true\nunless \"Bob\" = \"Bob\"  # false\n```\n\n### Loops (while/until)\n\nSentences starting with `while` repeat the sentence until while the condition is\ntrue. That is, the loop will only stop once the condition becomes false.\n\nConversely, using `until` will repeat the sentence until the condition becomes\ntrue.\n\nLoops are written in one of the following forms:\n\n```\nwhile/until \u003ccondition\u003e, \u003ctrue\u003e\n\nwhile/until \u003ccondition\u003e, \u003ctrue\u003e, otherwise \u003cfalse\u003e\n```\n\n# Backends\n\nA backend is program controlled by bento. A backend can be any program (compiled\nor interpreted) that implements the bento protocol on the port specified on the\n`BENTO_PORT` environment variable.\n\n## Locating and Starting Backends\n\nA backend is started (that is the program is started) when a variable is\ndeclared with a type that represents the backend. For example:\n\n```bento\ndeclare my-var is my-backend\n```\n\nWill find and start the backend with the name `my-backend`. The process is:\n\n1. `$BENTO_BACKEND` works similar to `$PATH` where it may contain zero or more\npaths split by a `:`. If `$BENTO_BACKEND` is not defined or is empty then it\nwill receive a default value of `.` - the current directory.\n\n2. For each of the backend paths, in order, it will attempt to find a directory\ncalled `my-backend`. The first one that it finds will be the one used, even if\nanother directory of the same name exists in other backend paths.\n\n3. The `my-backend` directory must contain a file called `bento.json`. This\ndescribes the backend, and also how it is to be executed. A minimal `bento.json`\nlooks like:\n\n```json\n{\n  \"run\": \"php myscript.php\"\n}\n```\n\nThe `run` contains the system command that will be executed. The program is\nexpected to open a socket, listening on the `BENTO_PORT` environment variable.\n\nThe program must remain running until the socket is closed by bento. All\ncommunication is defined in the *Backend Protocol*.\n\n## Communication Protocol\n\nAll communication between bento and the backend is done through a socket. The\nport will be provided to the backend with the `BENTO_PORT` environment variable.\n\nBento will always start the communication with a request and wait for a\nresponse. This synchronous process will continue indefinitely until bento closes\nthe connection. You may perform final cleanup if need be, then exit the backend\nprogram.\n\nA request or response will be a JSON object that consists of a single line, then\nterminated by a single new line (`\\n`). The newline is important because it\nsignals to the other side that the end of the message has been reached. It's\nalso important to make sure JSON objects are encoded correctly so that any new\nline characters have been escaped.\n\n### Request\n\nA request object is sent from bento to the backend and looks like:\n\n```json\n{\n  \"sentence\": \"add ? to ?\",\n  \"args\": [\"57\", \"example-scores-php\"]\n}\n```\n\n`sentence` is always a non-empty string. `?` is used as placeholders for the\nrespective order of elements in `args`. `args` will always be an array that will\ncontain the same number elements as their are placeholders.\n\nEach of the `args` will be a string (regardless of the internal type in bento).\n\n### Response\n\nBento will wait for a response after sending a request before proceeding. Like\nthe request, the response must be a valid JSON object encoded in a single line,\nfollow by a new line character to signal termination.\n\nA response can contain the following keys:\n\n```json\n{\n  \"text\": \"something\",\n  \"set\": {\n  \t\"$0\": \"foo\"\n  },\n  \"error\": \"Oh-noes!\"\n}\n```\n\n- `text` - The text representation of the variable. This is what is output with\n`display ?`. You do not need to return a value in other cases. You must\nimplement the `display ?` sentence in your backend for this feature.\n\n- `set` - This will set the value of a variable based on it's index in the\nsentence (`$n` where `n` is an index). The first placeholder (`?`) will have an\nindex of `0`. The value must be a string and a valid valid for the destination\ntype.\n\n- `error` must exist and be a string when an error has occurred. It also must\nnot be empty. The `error` should contain a description of the problem in a\nhuman-readable manner. It should not contain sensitive information such as\npasswords, or details such as stack traces used for debugging.\n\n### Special Cases\n\n#### sentences\n\nAll backends must implement `sentences`, which is used to fetch all of the\nallowed sentences:\n\n```json\n{\n  \"special\": \"sentences\"\n}\n```\n\nThe response must be in the form:\n\n```json\n{\n  \"sentences\": [\"increase ? by ?\", \"display ?\"]\n}\n```\n\nThe `sentences` is allowed to have zero elements.\n\nThe special `sentences` request is sent once, immediately after the socket\nconnection to the backend is successful. However, you should allow this request\nto come at any time and return the same result in all cases.\n\n## Examples\n\nEach of the examples implement the backend for the following:\n\n```bento\nstart:\n\tdeclare total is my-backend\n\tincrease total by 57\n\tincrease total by 13\n\tdisplay total\n```\n\nThe result of running the program in all cases is:\n\n```\nThe total is 70.\n```\n\n### PHP\n\n- [backend/example-scores-php](https://github.com/elliotchance/bento/tree/master/backend/example-scores-php).\n\n## System\n\nThe system backend provides direct access to running programs on the host\nmachine.\n\n- `run system command \u003ccommand\u003e`: Run the `command` and send all stdout and\nstderr to the console.\n\n- `run system command \u003ccommand\u003e output into \u003coutput\u003e`: Run the `command` and\ncapture all of the stdout and stderr into the `output`.\n\n- `run system command \u003ccommand\u003e status code into \u003cstatus\u003e`: Run the `command`\nand discard and stdout and stderr. Instead capture the status code returned in\n`status`.\n\n- `run system command \u003ccommand\u003e output into \u003coutput\u003e status code into \u003cstatus\u003e`:\nRun the `command` and capture the stdout and stderr into `output` as well as the\nstatus code returned into `status`.\n\nExample:\n\n```bento\nstart:\n\tdeclare echo-result is number\n\trun system command \"echo hello\" status code into echo-result\n\tunless echo-result = 0, display \"command failed!\"\n```\n\n# Examples\n\n## Hello, World!\n\n```\nstart:\n\tdisplay \"Hello, World!\"\n```\n\n## Variables\n\n```\nstart:\n\tdeclare first-name is text\n\tset first-name to \"Bob\"\n\tdisplay first-name\n```\n\n## Functions (Custom Sentences)\n\n```\nstart:\n\tprint everything\n\tsay \"Hi\" to \"Bob\"\n\nprint everything:\n\tdisplay \"Hello\"\n\tdisplay \"World\"\n\nsay greeting to persons-name (persons-name is text, greeting is text):\n\tdisplay greeting\n\tdisplay persons-name\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felliotchance%2Fbento","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felliotchance%2Fbento","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felliotchance%2Fbento/lists"}