{"id":13779180,"url":"https://github.com/jpoirier/picoc","last_synced_at":"2026-01-23T04:47:16.540Z","repository":{"id":33315652,"uuid":"36960394","full_name":"jpoirier/picoc","owner":"jpoirier","description":"A very small C interpreter","archived":false,"fork":false,"pushed_at":"2022-06-02T23:16:20.000Z","size":1594,"stargazers_count":401,"open_issues_count":26,"forks_count":75,"subscribers_count":20,"default_branch":"master","last_synced_at":"2025-05-27T08:50:41.350Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jpoirier.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":"2015-06-05T23:33:21.000Z","updated_at":"2025-05-20T11:31:53.000Z","dependencies_parsed_at":"2022-07-29T19:19:13.096Z","dependency_job_id":null,"html_url":"https://github.com/jpoirier/picoc","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/jpoirier/picoc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jpoirier%2Fpicoc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jpoirier%2Fpicoc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jpoirier%2Fpicoc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jpoirier%2Fpicoc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jpoirier","download_url":"https://codeload.github.com/jpoirier/picoc/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jpoirier%2Fpicoc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28680623,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-23T04:33:33.518Z","status":"ssl_error","status_checked_at":"2026-01-23T04:33:30.433Z","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":[],"created_at":"2024-08-03T18:01:02.182Z","updated_at":"2026-01-23T04:47:16.511Z","avatar_url":"https://github.com/jpoirier.png","language":"C","funding_links":[],"categories":["C"],"sub_categories":[],"readme":"Originally forked from https://github.com/zsaleeba/picoc\n\n# Description\n\nPicoC is a very small C interpreter for scripting. It was originally written\nas a script language for a UAV on-board flight system. It's also very\nsuitable for other robotic, embedded and non-embedded applications.\n\nThe core C source code is around 3500 lines of code. It's not intended to be\na complete implementation of ISO C but it has all the essentials. When\ncompiled it only takes a few k of code space and is also very sparing of\ndata space. This means it can work well in small embedded devices. It's also\na fun example of how to create a very small language implementation while\nstill keeping the code readable.\n\nIt's been tested on x86-32, x86-64, powerpc, arm, ultrasparc, HP-PA and blackfin\nprocessors and is easy to port to new targets.\n\n\n# Running files from the command line\n\nYou can run standard C programs straight from the command line:\n\n```C\n$ picoc file.c\n```\n\nIf your program is split into multiple files you can list them all on the\ncommand line.\n\n```C\n$ picoc file1.c file2.c file3.c\n```\n\nIf your program takes arguments you add them after a '-' character.\n\n```C\n$ picoc file.c - arg1 arg2\n```\n\n\n# Running script files\n\nScripts are slightly simpler than standard C programs because, A) all the system\nheaders are included automatically for you so you don't need to include them\nin your file/s and B) scripts don't require a main() function; they have\nstatements that are run directly from the top of a file to the bottom.\n\n```C\n$ picoc -s file.c\n```\n\nHere's an example script:\n\n```C\nprintf(\"Starting my script\\n\");\n\nint i;\nint total = 0;\nfor (i = 0; i \u003c 10; i++) {\n    printf(\"i = %d\\n\", i);\n    total += i;\n}\n\nprintf(\"The total is %d\\n\", total);\n```\n\nHere's the output from this script:\n\n```C\n$ ./picoc -s script.c\nStarting my script\ni = 0\ni = 1\ni = 2\ni = 3\ni = 4\ni = 5\ni = 6\ni = 7\ni = 8\ni = 9\nThe total is 45\n```\n\n\n# Interactive mode\n\n```C\n\u003e picoc -i\n```\n\nHere's an example session:\n\n```C\n$ ./picoc -i\nstarting picoc v2.1\npicoc\u003e char inbuf[80];\npicoc\u003e gets(inbuf);\nhello!\npicoc\u003e printf(\"I got: %s\\n\", inbuf);\nI got: hello!\n```\n\nDeleting variables and functions.\n\nSometimes in interactive mode you want to change a function or redeclare a\nvariable. You can do this using the \"delete\" statement:\n\n```C\n$ ./picoc -i\nstarting picoc v2.1\npicoc\u003e int fred = 1234;\npicoc\u003e printf(\"fred = %d\\n\", fred);\nfred = 1234\npicoc\u003e delete fred;\npicoc\u003e char *fred = \"hello\";\npicoc\u003e printf(\"fred = '%s'\\n\", fred);\nfred = 'hello'\n```\n\nNote, you can quit picoc's interactive mode using control-D.\n\n\n# Environment variables\n\nIn some cases you may want to change the picoc stack space. The default stack\nsize is 512KB (see PICOC_STACK_SIZE in picoc.c) which should be large enough\nfor most programs.\n\nTo change the stack size you can set the STACKSIZE environment variable to a\ndifferent value. The value is in bytes.\n\n\n# Compiling PicoC\n\npicoc can be compiled for a UNIX/Linux/POSIX host by typing \"make\".\n\nThe test suite can be run by typing \"make test\".\n\nOn Windows, use the MSVC++ sln file in the msvc/picoc folder.\n\n\n# Porting PicoC\n\nplatform.h is where you select your platform type and specify the includes\netc. for your platform.\n\nplatform_XXX.c contains support functions so the compiler can work on\nyour platform, such as how to write characters to the console etc..\n\nplatform_library.c contains your library of functions you want to make\navailable to user programs.\n\nThere's also a clibrary.c which contains user library functions like\nprintf() which are platform-independent.\n\nPorting the system will involve setting up suitable includes and defines\nin platform.h, writing some I/O routines in platform_XXX.c, putting\nwhatever user functions you want in platform_library.c and then changing\nthe main program in picoc.c to whatever you need to do to get programs\ninto the system.\n\nplatform.h is set to UNIX_HOST by default so tests can be easily run on\na UNIX system. You'll need to specify your own host setup dependent on\nyour target platform.\n\n\n# Copyright\n\nPicoC is published under the \"New BSD License\", see the LICENSE file.\n\n\n\n# Adding native C functions\n\n## Introduction\npicoc allows you to define your own library functions. These functions are\nwritten in C using your system's native C compiler. Since the native C compiler\ncan access the hardware this means you can add functions which give picoc control\nof your hardware.\n\n## How libraries work\nYour picoc distribution contains two files which are used to define library\nfunctions for your system. If your system is called \"foobar\" you'll be using:\n\n* library_foobar.c - this is where the foobar-specific library functions go\n* clibrary.c - this is where standard C library functions like printf() are defined\n\nWe'll start by defining a simple function in library_foobar.c. We need to do two things:\n\n* add the function prototype to our list of picoc library functions\n* define the native C implementation of the function\n\n## The prototype list\nEach of the library_XXX.c files defines a list of picoc prototypes for each of\nthe functions it defines. For example:\n\n```C\nstruct LibraryFunction PlatformLibrary[] =\n{\n     {ShowComplex,  \"void ShowComplex(struct complex *)\"},\n     {Cpeek,        \"int peek(int, int)\"},\n     {Cpoke,        \"void poke(int, int, int)\"},\n     {Crandom,      \"int random(int)\"},\n     {NULL,         NULL}\n};\n```\n\nThe first column is the name of the C function. The second column is the function\nprototype. The \"{ NULL, NULL }\" line at the end is required.\n\n## The native C function\nThe native C function is called with these parameters:\n\n```C\nvoid MyCFunc(struct ParseState *Parser,\n\t\t\t struct Value *ReturnValue,\n\t\t\t struct Value **Param,\n\t\t\t int NumArgs);\n```\n\n* struct ParseState *Parser - this contains internal information about the progress of parsing. It's mostly used here so error messages from your function can report the line number where an error occurred.\n* struct Value *ReturnValue - this points to the place you can put your return value. This is pre-created as a value of the correct return type so all you have to do is store your result here.\n* struct Value **Param - this points to an array of parameters. These are all pre-checked as being the correct type.\n* int NumArgs - this is the number of parameters. Normally this will already have been checked and will be exactly what you've defined in your function prototype. It is however possible to define functions with variable numbers of arguments using a stdarg-like \"...\" method and this is where you find out how many parameters were passed in if you're doing that.\n\nHere's an example function definition of \"random\" (as defined above):\n\n```C\nvoid Crandom(struct ParseState *Parser,\n\t\t\t struct Value *ReturnValue,\n\t\t\t struct Value **Param,\n\t\t\t int NumArgs)\n{\n    ReturnValue-\u003eVal-\u003eInteger = random() % Param[0]-\u003eVal-\u003eInteger;\n}\n```\n\nThis function calls \"random()\" from the C standard library. It accesses an integer\nparameter and returns an integer value.\n\n## Passing parameters\nWe've seen how to pass integers into functions. What about passing other data types?\n\n\n| Type\t\t| Method\t\t\t\t | Comment\n|-----------|------------------------|-------\n| int\t\t| Param[x]-\u003eVal-\u003eInteger |\n| char\t\t| Param[x]-\u003eVal-\u003eInteger | Treated as 'int' here\n| double\t| Param[x]-\u003eVal-\u003eFP\t\t | Only available on some systems\n| float\t\t| Param[x]-\u003eVal-\u003eFP\t\t | Same as 'double'\n| enum\t\t| Param[x]-\u003eVal-\u003eInteger | Gives integer value of enum\n| pointers\t| See section below\t\t | Slightly more complicated\n| char *\t| See section below\t\t | Slightly more complicated\n| arrays\t| See section below\t\t | Slightly more complicated\n| struct\t| See section below\t\t | Slightly more complicated\n| union\t\t| See section below\t\t | Slightly more complicated\n\n## Passing pointers\nPointer parameters are slighty more complicated to access since you have to\ndereference the pointer to get at the underlying data.\n\nHere's how we dereference a pointer parameter. In this example I'll be reading\nan 'int *' parameter:\n\n```C\nint IntValue = *(int*)Param[0]-\u003eVal-\u003eNativePointer;\n```\n\n## Passing strings/char*\nIn this example I'll be reading a 'char *' parameter. It's pretty similar to\nthe 'int *' example above:\n\n```C\nchar *CharPtr = (char*)Param[0]-\u003eVal-\u003eNativePointer;\n```\n\npicoc strings work like C strings - they're pointers to arrays of characters,\nterminated by a null character. Once you have the C char * you can use it just\nlike a normal C string.\n\nPointers to arrays of other data types work the same way.\n\n## Passing pointers to structures and unions\nIf you're defining library functions which take structures as parameters you'll\nhave to do a little more work. You need to pre-define the structure so the\nfunction prototype can refer to it.\n\nIn library_XXX.c you'll find a function called PlatformLibraryInit(). This is\ncalled before the library prototypes are defined. Here's a quick way to define\na complex number structure as if it was defined in an include file:\n\n```C\nIncludeRegister(\"win32.h\",\n\t\t\t\t\u0026win32SetupFunc,\n\t\t\t\t\u0026win32Functions[0],\n\t\t\t\t\"struct complex {int i; int j;};\");\n```\n\nOr you could just parse the structure directly:\n\n```C\nconst char *definition = \"struct complex {int i; int j;};\";\nPicocParse(\"my lib\", definition, strlen(definition), true, false, false);\n```\n\nThe same method works for defining macros too:\n\n```C\nconst char *definition = \"#define ABS(a) ((a) \u003c (0) ? -(a) : (a))\";\nPicocParse(\"my lib\", definition, strlen(definition), true, false, false);\n```\n\nHere's a more sophisticated method, using the internal functions of picoc directly:\n\n```C\nvoid PlatformLibraryInit()\n{\n    struct ParseState Parser;\n    char *Identifier;\n    struct ValueType *ParsedType;\n    void *Tokens;\n    char *IntrinsicName = TableStrRegister(\"complex library\");\n    const char *StructDefinition = \"struct complex { int i; int j; }\";\n\n    /* define an example structure */\n    Tokens = LexAnalyse(IntrinsicName, StructDefinition, strlen(StructDefinition), NULL);\n    LexInitParser(\u0026Parser, StructDefinition, Tokens, IntrinsicName, true, false);\n    TypeParse(\u0026Parser, \u0026ParsedType, \u0026Identifier, \u0026IsStatic);\n    HeapFree(Tokens);\n}\n```\n\nThis code takes the structure definition in StructDefinition and runs the lexical\nanalyser over it. This returns some lexical tokens. Then we initialize the parser\nand have it parse the type of the structure definition from the tokens we made.\nThat's enough to define the structure in the system. Finally we free the tokens.\n\nNow let's say we're going to define a function to display a complex number.\nOur prototype will look like:\n\n```C\n{ShowComplex,   \"void ShowComplex(struct complex *)\"},\n```\n\nAnd finally we can define the library function:\n\n```C\nstruct complex {int i; int j;};  /* make this C declaration match the picoc one */\n\nvoid ShowComplex(struct ParseState *Parser,\n\t\t\t\t struct Value *ReturnValue, struct Value **Param, int NumArgs)\n{\n    struct complex *ComplexVal = Param[0]-\u003eVal-\u003eNativePointer;  /* casts the pointer */\n\n    /* print the result */\n    PrintInt(ComplexVal-\u003ei, PlatformPutc);\n    PlatformPutc(',');\n    PrintInt(ComplexVal-\u003ej, PlatformPutc);\n}\n```\n\nUnions work exactly the same way as structures. Define the prototype as \"union\"\nrather than \"struct\" and you're away.\n\n## Returning values\nReturning values from library functions is very much like accessing parameters.\nThe type of return values is already set before your native C function is called\nso all you have to do is fill in the value.\n\nJust as with parameters, ints, chars and enums are stored in ReturnValue-\u003eVal-\u003eInteger\nand floating point values are returned in ReturnValue-\u003eVal-\u003eFP.\n\n## Returning pointers\nReturning a pointer to a static string or some other allocated data is easy.\nYour return code will look something like:\n\n```C\nReturnValue-\u003eVal-\u003eNativePointer = \"hello\";\n```\n\n## Variable numbers of parameters\nYou can define your own stdarg-style library functions like printf(). Your\nfunction prototype should use \"...\" in the parameter list to indicate the potential\nextra parameters just like the standard stdarg system. Here's an example from clibrary.c:\n\n```C\n{LibPrintf, \"void printf(char *, ...)\"},\n```\n\nThe NumArgs parameter to the native C function lets you know how many parameters\nwere passed in. You access the variable parameters just like normal parameters\nusing the Param[] array.\n\nTake a look at clibrary.c for the full definition of LibPrintf() if you need a\nmore complete example.\n\n## Sharing native values with PicoC\nSometimes you have native variables you'd like to share with picoc. We can\ndefine a picoc value which shares memory with a native variable. Then we store\nthis variable in the picoc symbol table so your programs can find it by name.\nThere's an easy way to do this:\n\n```C\nint RobotIsExploding = 0;\n\nvoid PlatformLibraryInit()\n{\n    VariableDefinePlatformVar(NULL,\n    \t\t\t\t\t\t  \"RobotIsExploding\",\n    \t\t\t\t\t\t  \u0026IntType,\n    \t\t\t\t\t\t  (union AnyValue*)\u0026RobotIsExploding,\n    \t\t\t\t\t\t  false);\n}\n```\n\nThe variable RobotIsExploding can be written by your native C program and read\nby PicoC just like any other PicoC variable. In this case it's protected from\nbeing written by the last parameter \"IsWritable\" being set to FALSE. Set it to\nTRUE and PicoC will be able to write it too.\n\n\n# How PicoC differs from C90\n\nPicoC is a tiny C language, not a complete implementation of C90. It doesn't\naim to implement every single feature of C90 but it does aim to be close enough\nthat most programs will run without modification.\n\nPicoC also has scripting abilities which enhance it beyond what C90 offers.\n\n## C preprocessor\nThere is no true preprocessor in PicoC. The most popular preprocessor features\nare implemented in a slightly limited way.\n\n## `#define`\nMacros are implemented but have some limitations. They can only be used\nas part of expressions and operate a bit like functions. Since they're used in\nexpressions they must result in a value.\n\n## `#if/#ifdef/#else/#endif`\nThe conditional compilation operators are implemented, but have some limitations.\nThe operator \"defined()\" is not implemented. These operators can only be used at\nstatement boundaries.\n\n## `#include`\nIncludes are supported however the level of support depends on the specific port\nof PicoC on your platform. Linux/UNIX and Windows support #include fully.\n\n## Function declarations\nThese styles of function declarations are supported:\n\n```C\nint my_function(char param1, int param2, char *param3)\n{\n   ...\n}\n\nint my_function(char param1, int param2, char *param3) {\n   ...\n}\n```\n\nThe old \"K\u0026R\" form of function declaration is not supported.\n\n## Predefined macros\nA few macros are pre-defined:\n\n* PICOC_VERSION - gives the picoc version as a string eg. \"v2.1 beta r524\"\n\n## Function pointers\nPointers to functions are currently not supported.\n\n## Storage classes\nMany of the storage classes in C90 really only have meaning in a compiler so\nthey're not implemented in picoc. This includes: static, extern, volatile,\nregister and auto. They're recognised but currently ignored.\n\n## struct and unions\nStructs and unions can only be defined globally. It's not possible to define\nthem within the scope of a function.\n\nBitfields in structs are not supported.\n\n## Linking with libraries\nBecause picoc is an interpreter (and not a compiler) libraries must be linked\nwith picoc itself. Also a glue module must be written to interface to picoc.\nThis is the same as other interpreters like python.\n\nIf you're looking for an example check the interface to the C standard library\ntime functions in cstdlib/time.c.\n\n## goto\nThe goto statement is implemented but only supports forward gotos, not backward.\nThe rationale for this is that backward gotos are not necessary for any\n\"legitimate\" use of goto.\n\nSome discussion on this topic:\n\n* http://www.cprogramming.com/tutorial/goto.html\n* http://kerneltrap.org/node/553/2131\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjpoirier%2Fpicoc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjpoirier%2Fpicoc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjpoirier%2Fpicoc/lists"}