{"id":19839841,"url":"https://github.com/themarlboroman/ascript","last_synced_at":"2025-08-12T15:06:33.480Z","repository":{"id":100037104,"uuid":"326824983","full_name":"TheMarlboroMan/ascript","owner":"TheMarlboroMan","description":"Ascript, a script language to be used by host environments, short for \"annoying script\"","archived":false,"fork":false,"pushed_at":"2024-02-08T11:04:07.000Z","size":281,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-05T14:44:42.119Z","etag":null,"topics":["annoying","cpp","interpreter","language","script","scripting"],"latest_commit_sha":null,"homepage":"","language":"C++","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/TheMarlboroMan.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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}},"created_at":"2021-01-04T22:41:10.000Z","updated_at":"2022-09-12T17:52:56.000Z","dependencies_parsed_at":"2024-11-12T12:38:55.870Z","dependency_job_id":null,"html_url":"https://github.com/TheMarlboroMan/ascript","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/TheMarlboroMan/ascript","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheMarlboroMan%2Fascript","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheMarlboroMan%2Fascript/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheMarlboroMan%2Fascript/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheMarlboroMan%2Fascript/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TheMarlboroMan","download_url":"https://codeload.github.com/TheMarlboroMan/ascript/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheMarlboroMan%2Fascript/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270082327,"owners_count":24523709,"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","status":"online","status_checked_at":"2025-08-12T02:00:09.011Z","response_time":80,"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":["annoying","cpp","interpreter","language","script","scripting"],"created_at":"2024-11-12T12:24:34.260Z","updated_at":"2025-08-12T15:06:33.426Z","avatar_url":"https://github.com/TheMarlboroMan.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"#ascript\n\nShort for annoying script. An annoying script language...\n\n## fun stuff\n\n- To allow for yielding there's no recursive interpreter... this opens the door for all sorts of unpleasantries.\n- When calling interpreter::run with a script is imperative that the script object is not gone out of scope. This could be fixed, but it wouldn't be annoying.\n- Scripts can't be copied. That's right. There's an unique_ptr down there, could be fixed but again, would not be annoying.\n\n##BUGS:\n\n- out [\"something\", var, \", \", othervar]; causes the parser to explode, it cannot fathom a comma following a quote.\n\n##TODO:\n\n- Check for memory leaks.\n- Cleanup arguments / parameters name, they should not be interchangeable in a readable codebase.\n- Check TODOs.\n- As an interesting exercise, try separating \"variable\" into \"type\", which would have subtypes for int, bool and such.\n\t- An alternative is to leave variable to exist, but have separate storage classes for each type and have the class point to them when retrieving data.\n- Study homogeneous arrays, with a syntax like let crap be {1,2,3}; and then array_size[crap], array_get[crap, 1], array_set[crap, 2, 33], and so on.\n- Study structures (symbol tables on themselves, custom types). they would be... interesting, let crap be packed type [member:value, member:value, member:value];\n\n##manual\n\nascript is an script language implemented in C++ as a library. Its purpose is to act as a simple script language for other applications, mainly games, which is the main purpose why I wrote it.\n\nascript stands for \"annoying script\". There are small annoyances but it's mainly for fun.\n\nascript needs to be run from a C++ program. There are a few example programs bundled.\n\nOf course, ascript is for my own amusement. There are better, more stable and time-tested alternatives out there.\n\n###the input files\n\nInput files are plain text files that must contain ascript functions. There's no official extension, I use \".ann\".\n\n###the host\n\nThe host is not exactly the program from within ascript runs, but a facility inside such program that is designed to interact with it. On its own, ascript supports only very simple arithmetic and logic but through a host interface it can access host calculations and instruct the host to perform any implementation-defined action.\n\nThe advantage against implementing the same functionality in the program itself is that behaviour implemented in ascript is evaluated at run-time and needs not to be recompiled. In other words, it should allow me to add non-hardcoded functionality to my games.\n\n###the output interface\n\nThe output interface is whatever exists in the calling environment that allows ascript to output stuff. It must implement \"out_interface\".\n\nFor convenience, a stdout_out class is provided with ascript.\n\n###general syntax notes\n\nascript supports a very simple syntax with very few concessions... every single instruction must end with a ; (be it a function declaration or a more complex equality test), in layman's terms, there should be a ; at the end of every line.\n\nascript is case sensitive and all of its keywords and functions are lowercase.\n\nValid language tokens are separated by a space, function parameters are separated by commas and put between brackets. You must separate the brackets from the function name by whitespace, but may put the parameters next to commas and brackets.\n\nComments are lines starting with #. I don't think a # symbol at the end of a line will be anything but a syntax error.\n\nLine breaks and tabs mean nothing.\n\nascript does not adhere to BNF form, that is, it does not support token resolution a la \"callfunction [1, 2, otherfunction [3, 4]]\". The inner function must be resolved separatedly.\n\nIn the same way, boolean logic is performed through functions. It follows that a valid statement is \"if func [1, 2]\". However, \"let val be func [1,2]; if val\" is not a valid form, even if the redundant \"let val be func [1,2]; if is_same [val, true]\" is valid. There is a \"not\" keyword that allows for \"if not is_equal [true, false]\" but there are not \"and\", or \"or\" keywords (again, no BNF). Given that is_* functions accept multiple arguments, simple cases can be covered (\"if is_equal[true, val1, val2, val3, val4]\") but compound statements will be neccesary for others.\n\nBuilt-in functions cannot be found outside assignments and boolean statements. Built-in procedures (functions that return no values), however can only be found outside these. User-defined functions can appear in both places.\n\n###types\n\nascript is typed. Declaring a variable will automatically set its type to integer, boolean, double or string. Its type must be the same through all its lifetime. Resetting the variable to another type will cause an error.\n\nFunction parameters are also typed, even if the keyword \"any\" allows an argument to be of any type (basically an \"I really don't care about anything in life\" case). Oddly enough, a function can return different types.\n\nThere are functions to identify each type (is_int, is_string...).\n\n####a note on type mismatches\n\nascript is annoyingly typed. A call to \"is_lesser_than [integer, double]\" will cause a type mismatch, as \"is_equal [true, 1, 1.0]\" will. \n\n###declaring functions\n\nFunctions are the entrypoint to ascript from the calling environment. They are declared on the following form:\n\nbeginfunction *functionname* [*paramname* as *type*, *paramname* as *type*];\n\n\tstatements;\n\nendfunction;\n\nThe parameter list is optional, it does not need to appear if a function takes no parameters. A function can accept any number of parameters, whose names should not be repeated. The types can be \"int\", \"bool\", \"string\", \"double\" and \"any\" (just in case you are feeling funky).\n\nAll parameters are copies, there are no reference parameters.\n\nFunctions can also call other functions. Recursion is theoretically supported.\n\n###variables\n\nVariables can be declared to hold statically defined values in the following form:\n\nlet *variablename* be *variablevalue*;\n\nThe variable value determines its type: integers are simple numbers, strings use double quotes, booleans use the literals \"true\" and \"false\" and doubles expect to have a dot somewhere for the decimal part. Anything else is supposed to be the name of another variable, which copies the value.\n\nThere are no reference types.\n\nVariables can be declared to hold the return value of a function:\n\nlet *variablename* be built_in_fn [param, param];\nlet *variablename* be custom_function [param, param];\n\nVariables can be reset with the form:\n\nset *variablename* to *variablevalue*;\nset *variablename* to built_in_fn [param, param];\nset *variablename* to custom_function [param, param];\n\nVariables do not share their space with host symbol tables, but do share it with function parameters.\n\n####variable scope\n\nA variable will exist for as long as its block does. That is, everything declared after beginfunction, if, elseif, else or loop will exist until its corresponding closing statement.\n\n###returning values from functions\n\nA function can exit by using the \"return statement\". If a function must return a (single) value, if must do it like this:\n\nreturn [value];\n\n##running ascript from C++:\n\nFirst, load up a tokenizer and instruct it to decompose a program:\n\tascript::tokenizer tk;\n\tconst auto tokens=tk.from_file(_argv[1]);\n\nNext, load a parser and generate functions from it:\n\tascript::parser p;\n\tconst auto functions=p.parse(tokens);\n\nThese functions must exist for as long as any interpreter wants to make use of them!!! If they go out of scope first, it's boom time.\n\nImplement the \"host\" and \"out_interface\" interfaces. Create instances of them, plus an interpreter.\n\tscript_host sh;\n\tascript::interpreter i;\n\tascript::stdout_out outfacility;\n\nLoad the functions into the interpreter. Yes, this is a neccesary step.\n\tfor(const auto\u0026 f : functions) {\n\t\ti.add_function(f);\n\t}\n\nFinally run whatever function you want. The two last parameters are the function name and a vector of parameters (of \"ascript::variable\" type).\n\n\tauto result=i.run(sh, outfacility, funcname, {});\n\n###accessing return values from the calling environment\n\nOnce the interpreter ends, it returns a \"return_value\" type which might hold:\n\n- A real value.\n- A \"yield\" value, meaning that the function has yielded and no return value is yet available.\n- A \"nothing\" value, meaning that the function returns nothing.\n\n\tif(result) {\n\n\t\t//result.get() will return a \"variable\" type.\n\t}\n\telse if(result.is_yield()) {\n\n\t\t//the function is not done and can be resumed with interpreter::resume()\n\t}\n\telse if(result.is_nothing()) {\n\n\t\t//no return value.\n\t}\n\nIf a yielded value is returned the interpreter can be queried to whether or not it is blocked by a timed yield and how many milliseconds are left with get_yield_ms_left A return value of 0 (most likely) means a non timed yield. Positive values are for functions whose yield timer has not expired, and negative values are for those who expired.\n\n###handling errors on the calling enviroment:\n\nAll failures will throw an exception whose base type is \"ascript_error\".\n\n###control flow\n\nThe following control flow mechanisms are available:\n\n####conditionals\n\nAll conditionals follow of the following form:\n\nif (not) *evaluation_function*;\n\tstatements;\n(elseif *evaluation_function*);\n\tstatements;\n(elseif *evaluation_function*);\n\tstatements;\n(elseif *evaluation_function*);\n\tstatements;\n(else)\n\tstatements;\nendif;\n\nIt follows that this the shortest valid if statement:\n\nif fn [];\n\tstatements;\nendif;\n\nEvaluation functions can either be built-ins or user-defined functions.\n\nif is_equal [true, true];\n\tstatements;\nendif;\n\nif funcname [true, true];\n\tstatements;\nendif;\n\nA user-defined function that returns no value will cause a runtime error if used within an if statement:\n\nbeginfunction test;\n\tout [\"fail\"];\nendfunction;\n\nif test[];\n\tout [\"will never get here\"];\nendif;\nout [\"nor here\"];\n\n####loops\n\nAll loops are of the following form:\n\nloop;\n\tstatements;\nendloop;\n\nLoops can be exited by:\n\n\t- return\n\t- break\n\t- fail\n\nascript runs in a single thread: an infinite loop will freeze the calling environment unless \"yield\" is used!.\n\nThere's no for or while loops. All loops expect to have some if clause that breaks (or yield and loop forever).\n\n####yielding functions\n\nThe keyword \"yield\" causes the execution of a function to be halted with no return value and returns control to the point where \"run\" was called. The interpreter can resume the function with a call to \"resume\" and the method \"is_finished\" can be used to query its state.\n\nThe purpose of non-timed \"yield\" is to be able to run infinite loops without freezing the calling environment. It could be argued that the same functionality can be obtained with repeated \"run\" calls, but I wanted \"yield\" to exist anyhow.\n\nYield can also be used to halt execution for at least a number of milliseconds using:\n\n\tyield for *variable_solving_to_milliseconds*;\n\tyield for 1000;\n\nThis form prevents the interpreter from resuming until the time has elapsed. Execution does not resume automatically: the interpreter must be queried about the time left (if any) before calling \"resume\". Calling \"resume\" on a time-yielding interpreter will return the yield value.\n\n###calling built-in and user-defined functions\n\nCalling other functions is done using the function identifier and brackets for the parameter lists:\n\nfunctioname [param, param];\n\nIf a function needs no parameters, ascript still needs the brackets for disambiguation purposes.\n\nfunctionname [];\n\nShould the function return a value, it can be captured with:\n\nlet *variablename* be functioname [param, param];\nset *variablename* to functioname [param, param];\n\nFunction names cannot be captured as strings, thus this:\n\nlet a be \"myfunc\";\nlet b be a [true];\n\nWill fail unless there's a \"a\" function defined.\n\n###built in functions\n\nThese are the built-in functions. All these functions can appear at variable assignment or declaration. Those returning boolean values can also appear in branch statements.\n\n####is_equal\n\nReturns true if all of the parameters passed to it are of the same type and hold the same value.\n\nlet a be 1;\nlet b be 1;\nif is_equal [1, a, b];\n\tout [\"ok\"];\nendif;\n\n####is_greater_than\n\nReturns true if the first parameter is of numeric type and is greater than the rest, which must be of the same numeric type:\n\nlet a be 1;\nlet b be 2;\nif is_greater [100, a, b];\n\tout [\"ok\"];\nendif;\n\n####is_lesser_than\n\nThe opposite of \"is_greater_than\".\n\n####add\n\nReturns a numeric value that results of adding all of its parameters, which must be of the same numeric type.\n\nlet a be 1;\nlet b be 2;\nlet c be add [a, b, 3];\nif is_equal [c, 6];\n\tout [\"ok\"];\nendif;\n\n####substract\n\nLike add, but with substractions and using the first parameter as a base.\n\n####concatenate\n\nAdd for strings, annoyingly typed.\n\n####is_int\n\nReturns true if all the given parameters are of integer type.\n\n####is_bool\n\nReturns true if all the given parameters are of boolean type.\n\n####is_double\n\nReturns true if all the given parameters are of double type.\n\n####is_string\n\nReturns true if all the given parameters are of string type.\n\n####host_has\n\nReturns true if the host has all given names on its symbol table. All names must be expressed as strings or as variables that solve to strings.\n\n####host_get\n\nTakes a single value (a string or a variable that solves to one) and returns the value that exits in the host's symbol table with that name. If the name does not exist, it should throw (the host behaviour is actually implementation-defined).\n\n####host_query\n\nTakes any number of values to make the host produce a single variable. Everything about this function is implementation-defined. Semantics imply that this function must be used to query the host for information, whereas host_do must be used to ask the host do change its state, for example:\n\nbeginfunction check_for_mistic_key [keytype as string];\n\n\tlet result be host_query [\"check_key\", keytype];\n\treturn [result];\nendfunction;\n\nbeginfunction check_for_opened_door [door_id as int];\n\n\tlet result be host_query [\"is_door_open\", door_id];\n\treturn [result];\nendfunction;\n\nbeginfunction open_door [keytype as string, door_id as int];\n\n\tif check_for_opened_door [door_id];\n\n\t\treturn;\n\tendif;\n\n\tif not check_for_mistic_key [keytype];\n\n\t\thost_do [\"show_message\", \"key needed\", keytype];\n\telse;\n\n\t\thost_do [\"open_door\", door_id];\n\tendif;\nendfunction;\n\nIn the host program, a door object can be programmed to hold its id and the keytype as a string and, on interaction, fire up an interpreter and call \"open door\";\n\n\tinterpreter.run(host, outfacility, \"opendoor\", {keytype, id]);\n\nThe host function could be implemented as follows:\n\n\tvariable host_query(const std::vector\u003cvariable\u003e\u0026 _arguments) const {\n\n\t\t//type and size checking is skipped for brevity.\n\n\t\tstd::string message=_arguments[0].str_val;\n\n\t\tif(message==\"check_key\") {\n\n\t\t\treturn player_instance.has_key(_arguments[1].str_val);\n\t\t}\n\t\telse if(message==\"is_door_open\") {\n\n\t\t\treturn current_map.get_door(_arguments[1].int_val).is_open();\n\t\t}\n\n\t\t//throw something...\n\t}\n\n\tvoid host_do(const std::vector\u003cvariable\u003e _arguments) {\n\n\t\t//type and size checking is skipped for brevity.\n\n\t\tstd::string message=_arguments[0].str_val;\n\n\t\tif(message==\"open_door\") {\n\t\t\t\n\t\t\tcurrent_map.get_door(_arguments[1].int_val).open();\n\t\t}\n\t\telse if(message==\"show_message\") {\n\n\t\t\t//blah blah blah\n\t\t}\n\t}\n\n###built in procedures \n\nThese are the built-in procedures. None of them return any value and they can only appear on their own, never as part of any other statement.\n\n####host_delete\n\nDeletes the given symbols (as strings or variables that solve to strings) in the host. Should throw if the symbols do not exist, but that's implementation-defined.\n\n####host_set\n\nTakes two parameters, asking the host to set the symbol on the first parameter (a string or a variable that solves as one) to the value on the second. Semantics imply that the host should throw if the symbol does not exist, but nothing forces that. Language conventions also imply that the type of the symbol should not change, but ultimately that's defined by the host implementation.\n\n####host_add\n\nTakes two parameters, asking the host to add a new the symbol (the first parameter, a string or a variable that solves as one) with the value of the second. Semantics imply that the host should throw if the symbol already exists, but nothing forces that. \n\n####host_do\n\nTakes any number of parameters to ask the host to perform completely implementation-defined actions. Semantics imply that the host is able to change its state as a consequence of a call to host_do.\n\n####out\n\nUses the out_interface to output whatever values it is passed (integers, strings, booleans and doubles). Takes any number of parameters.\n\n####fail\n\nHalts the execution and throws an user error, whose message is the concatenation of all parameters passed (of any type).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthemarlboroman%2Fascript","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthemarlboroman%2Fascript","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthemarlboroman%2Fascript/lists"}