A minimalistic programming language written in C89.
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
Ludovic 'Archivist' Lagouardette 07a43ec54e Updated the documentation a little 2 дні тому
include Storage of names is now fully internal, the NOSTRINGLITERALS and NOARRAY flags are no longer supported 5 дні тому
test Fixed garbage collection mistakes 1 тиждень тому
.gitignore Storage of names is now fully internal, the NOSTRINGLITERALS and NOARRAY flags are no longer supported 5 дні тому
9build better shell, starting to port shell to 9front 1 тиждень тому
9sh.c better shell, starting to port shell to 9front 1 тиждень тому
CMakeLists.txt A build of the shell can now print the current version that was built if built with CMake 2 дні тому
LICENSE Added README.md contents 1 рік тому
README.md Updated the documentation a little 2 дні тому
bench.c Reorganized the project to move headers in their own directory 1 рік тому
lib.c Removed the flags mentioned in the previous commit 4 дні тому
main.c Fixed cosmetic incongruities 1 тиждень тому
sh.c A build of the shell can now print the current version that was built if built with CMake 2 дні тому

README.md

ink

Any information here is subject to change and may already not be inaccurate anymore

Here be dragons, you may have dug too deep

ink is a minimalistic interpreted programming language, tentatively implemented exclusively in C89. It features coroutines and can currently manipulate integers and arrays of integers, which can be interpreted as strings of Unicode codepoints. Part of the code may not be compliant with C89 and I will try to fix that in time.

It is fully self-contained and doesn't rely on a working standard library beyond the following:

  • malloc
  • realloc
  • free
  • putchar

These functions need to be wrapped, the wrapper allows to make them stateful to keep individual heaps per context, allowing to clean the context by cleaning up its allocations.

To make the library not use the standard library, build it with NOSTDLIB defined as a preprocessor directive.

All of these functions need to work for ink to work. It is easy to add new functions to the interpreter. I added a garbage collector to handle cleaning dynamically allocated resources.

It is possible to segregate unsafe allocations (allocations that should be hidden from the interpreter) by setting the inner_ versions of the library functions to different allocation functions.

Limits

  • Token size is limited to 127 bytes (see ink_lex)
  • Values and indices are limited to the platform size of int
  • Main function has a size limit of 256 tokens (see ink_compile)
  • Functions have a size limit of 256 tokens (see ink_parse)
  • Functions have a count limit 128 labels (see ink_parse)
  • Only non-main functions can use labels

Roadmap

  • Greater homoiconicity
    • Can get a list of the words
    • Can get words as arrays
    • Can execute arrays as words
    • Can manually create or modify
    • Can read a routine program stack
    • Can read a routine instruction counter stack
    • Can read a routine stack
    • Can set a routine status
  • Word redefinition (with a flag)
  • 9front port
    • 9front shell
  • Shell able to run external program and parse IO from other programs
  • Shell can import files (requires waiting for a routine in a routine)
  • Waiting for another routine
  • Executing other routines
  • Move shell words to base implementation iff they do not have any external dependency (e.g.: version)

Examples

Hello World

[ 72 101 108 108 111 32 87 111 114 108 100 10 ] 
    array.print_utf8

Clone array

# Clones an array, creating a new array
# 
# @param array The array to clone into a new array
# @return a new array that contains the same elements as the source array
# 
# array -> new_array
fn array.clone do
    array.new 2 pluck array.size 0
    # array new_array end it
    2 pluck 2 pluck == end_loop jump_if
    # array new_array end it
    loop:
        dup 5 pluck
        # array new_array end it it array
        array.index 4 pluck
        # array new_array end it v new_array
        array.push
        # array new_array end it
        1 +
   2 pluck 2 pluck > loop jump_if
   end_loop: drop drop swap drop
   # new_array
end

+% encryption

Encrypts a string with (v + add_key) % modulo_key. It modifies the array that was passed in.

# Encrypts things by doing `(v + add_key) % modulo_key`
#
# @param array An array of ints representing a string
# @param add_key Should be lower than the add key
# @param modulo_key Should ke higher than all the codepoints of the array
#
# array add_key modulo_key
fn encrypt do
    3 pluck array.size
    # array add_key modulo_key index
    loop:
        1 - dup 5 pluck
        # array add_key modulo_key index index array
        array.index
        # array add_key modulo_key index v
        4 pluck +
        # array add_key modulo_key index (v + add_key)
        3 pluck %
        # array add_key modulo_key index ((v + add_key) % modulo_key)
        2 pluck
        # array add_key modulo_key index ((v + add_key) % modulo_key) index
        6 pluck
        # array add_key modulo_key index ((v + add_key) % modulo_key) index array
        array.set
        # array add_key modulo_key index
    dup 0 != loop jump_if drop drop drop drop
end

# Prints a string as an array of ints
#
# @param array An array of ints representing a string
#
# array
fn string.dump do
    dup array.size 0
    # array end it
    91 print_utf8
    32 print_utf8
    loop:
        dup
        # array end it it
        4 pluck
        # array end it it array
        array.index print_int
        32 print_utf8
        1 +
        # array end it
    2 pluck 2 pluck > loop jump_if
    # array end it
    93 print_utf8
end
[ 72 101 108 108 111 32 87 111 114 108 100 10 ] 
    dup 
    32 128 encrypt 
    string.dump