Skip to content

knight-lang/knight-lang

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Knight

Knight is a minimalistic programming language designed to be easily implementable in a variety of languages. While it is a fully-functional programming language, it's main purpose is to be a somewhat-easy-to-implement language.

Unofficial Tag-line: "Knight: Runs everywhere. Not because it's cross-platform, but because it works in any almost any language you have."

Checkout the community, and join us on discord: https://discord.gg/SE3TjsewDk.

Implementations

I've implemented Knight in quite a few languages over the years. Some of the implementations only implement older versions of the spec (eg Knight 1.0, which was before lists); my eventual plan is to upgrade them all. There are three implementations of special note:

  • go: The exemplar implementation, intentionally written without any fancy tricks or optimizations, and is thoroughly documented. If you don't know how to get started writing a Knight program, take a look at this one!
  • rust: The fully-functional implementation. It has all the suggested extensions (and a few of its own) implemented (--features=all-extensions), and has the ability to check for all forms of undefined behaviour (--features=strict-compliance). If you're not sure if a piece of Knight code is spec compliant, you can run it with cargo run --no-default-features --features=strict-compliance -- -e ...!
  • c: The fastest, currently. When compiled with -DKN_RECKLESS, it will assume that every input program is 100% spec-compliant (and won't ever error).

List

The following is the list of all languages that I've written it in:

Language Version 100% Spec Conformance Documented Notes
AWK 2.0.1 X X Unfortunately, MacOS's awk segfaults on DUMP PROMPT sometimes. Other than that, it works.
Assembly (x86) 1.0 Functional enough to run the benchmark. A few auxiliary functions (eg * STRING NUM) are left.
C 2.0.1 X X Fully functional
C++ 2.0.1 X X Works with C++17
C# 1.0 X X Simple version without any documentation. It can be cleaned up slightly though.
Go 2.1.0 X X The "exemplar" Knight implementation: Simply made, fully documented.
Haskell pre-1.0 ish
Java 1.0 X Simple version without any documentation. It can be cleaned up slightly though.
JavaScript 1.0 X X Fully Functional, although it requires Node.js for the OS-related functions.
Knight 2.0.1 Yes, this is a Knight interpreter, written in Knight.
Kotlin 1.0 X X Fully funcitonal, and barring `, can be compiled to native.
Perl 1.1 X X Fully Functional on at least v5.18.
PHP 1.0 X X Fully Functional, with type annotations.
POSIX-Compliant SH 1.0 Mostly works, but has some bug fixes that need to be done. It could definitely use some TL&C, though.
Python 1.0 X X Fully Functional, though setrecursionlimit is needed to ensure "FizzBuzz in Knight in Python" works.
Raku 1.1 X X Fully Functional, but quite slow. But hey, it was fun to write in.
Ruby 1.0 X A hacky version currently exists; a more sophisticated one is being worked on.
Rust 2.0.1 X Captures all UB if you enable strict-compliance. Also has most extensions
ZSH 2.0.1 X X

Personal Languages

I love language design, and have written quite a few programming languages. I generally try to get the languages fleshed out enough so that I can write a fully compliant Knight interpreter in it.

Language 100% Spec Conformance Documented Mostly Functional Begun Notes
Brick X
Squire X
Quest X Implemented in quest 1, so not entirely working.
Stick X
Lance Few bugs left to workout
Mercenary

Future

In addition, the following is a list of languages which I want to write an implementation in at some point.

Language Notes
Elixir Probably the next one I'll do.
Lua Planned for somepoint soon.
SML Eventually. I used this in college, and enjoyed it.
Racket Eventually. I used this in college, and enjoyed it.
LaTeX Eventually. Because why not? I did a lot of LaTeX in college.
Scratch My first language! Might be fun to implement it in this
Prolog The very beginnings of a Prolog implementation.
Fortran This might be fun to try, but there's not a lot of documentations about it.

Time Comparisons

NOTE: These are outdated. I no longer own the computer I tested them on, and the timeit file doesn't actually work. I'll be updating them in the future. The following able describes how fast each implementation (in user time) was at running examples/fizzbuzz.kn in knight.kn in knight.kn in their implementation, on my machine. You can test it yourself via the timeit script provided.

Note that these are simply benchmarks of my implementations of Knight, and not a reflection of the efficiency of the languages themselves.

Language Time <implementation> Notes
C 3.31s c/ast/knight Compiled using COMPUTED_GOTOS=1 CFLAGS='-DKN_RECKLESS -DKN_USE_EXTENSIONS' make optimized; See https://github.com/knight-lang/c/ast/README.md for details.
Kotlin 5.84s kotlin/knight
x86 Assembly 6.29s asm/knight Currently only has AST caching. (The C impl without caching runs 10.22s)
Java 6.99s java/knight Requires a larger stack to prevent overflow; java -Xss515m was used.
Rust 10.06s rust/target/release/knight Built with cargo build --release --no-default-features --features=unsafe-optimized. Still being improved.
C# 11.82s csharp/bin/Release/netcoreapp2.1/<impl>/Knight
C++ 13.61s cpp/knight Copiled using make optimized
Go 14.17s go/knight/knight
JavaScript 30.64s node --stack-size=1000000 javasript/bin/knight.js Default stack's too small, so we had to bump it up.
PHP 64.73s php/knight.php
Ruby 110.04s ruby/knight.rb Default stack's too small, so RUBY_THREAD_VM_STACK_SIZE=10000000 was needed.
Python 236.01s python/main.py Default stack's too small, so setrecursionlimit(100000) was needed.
Perl 436.55s perl/bin/knight.pl

Examples

Here's some examples of the syntax to give you a feel for it:

Guessing Game

; = max 100                                   # max = 100
; = secret + 1 (% RAND max)                   # secret = rand(1, max)
; = nguess 0                                  # nguess = 0
; = guess 0                                   # guess = 0
; OUTPUT (+ 'guess 1-' max)                   # print('pick from 1-' + m)
; WHILE (| (< guess secret) (> guess secret)) # while guess != s:
  ; OUTPUT '> \'                              #   print('> ', end='')
  ; = guess (+ 0 PROMPT)                      #   guess = int(prompt())
  ; = nguess (+ nguess 1)                     #   nguess += 1
  : OUTPUT (                                  #   print(
     IF (< guess secret) 'too low'            #     if guess < secret: 'too low'
     IF (> guess secret) 'too high'           #     if guess > secret: 'too high'
                         'correct')           #     else: 'correct')
: OUTPUT (+ 'tries: ' nguess)                 # print('tries: ' + n)

Fibonacci

; = fib BLOCK                           # function fib:
    ; = a 0                             #    a = 0
    ; = b 1                             #    b = 1
    ; WHILE n                           #    while n != 0:
        ; = b + a (= tmp b              #       b = a + (tmp = b)
        ; = a tmp                       #       a = tmp
        : = n - n 1                     #       n -= 1
    : a                                 #    return a
; = n 10                                # n = 10
: OUTPUT +++ 'fib(' n ')=' CALL fib     # print "fib(" + n + ")=" + fib()
# => fib(10)=55

Spec Overview

The following is just a rough overview, and is not to be taken as authoritative; for exact details please see specs.md.

Syntax

Every Knight program is a single expression. (The ; function can be used to write more than one expression, sequentially.) Because of this, parsing is extremely simple: Parse a token, then parse as many arguments as that expression dictates.

Non-symbol functions are defined by their first character: additional uppercase characters following it are ignored. Because of this, OUTPUT is the same as OUT, which is the same as OFOOBARBAZ.

Tokens may follow directly one after another (eg 1a is parsed as 1 and then a), except in the following four cases (which must have whitespace or comments):

  1. Between two "word" keywords, such as IF PROMPT
  2. Between two numbers, such as + 1 2
  3. Between two identifiers, such as * a b
  4. Between an identifier and a number, such as + a 3.

Additionally, while not technically whitesapce, (, ), and : can be safely interpreted as whitespace as well. As such, expressions such as OUTPUT * a (IF b 3 b) can be written as O*aIb 3b.

EBNF

Knight's ENBF is as follows:

program := expr ;
expr := integer-literal
      | string-literal
      | identifier
      | nullary
      | unary      expr
      | binary     expr expr
      | ternary    expr expr expr
      | quaternary expr expr expr expr ;

integer-literal := DIGIT {DIGIT} ;
string-literal  := `'` {NON_SINGLE} `'` | `"` {NON_DOUBLE} `"` ;
identifier      := LOWER {LOWER | DIGIT} ;

nullary := '@'
         | ('T' | 'F' | 'N' | 'P' | 'R') {UPPER} ;

unary := ':' | '!' | '~' | ',' | '[' | ']'
       | ('B' | 'C' | 'Q' | 'D' | 'O' | 'L' | 'A') {UPPER} ;

binary := '+' | '-' | '*' | '/' | '%' | '^'
        | '<' | '>' | '?' | '&' | '|' | ';' | '='
        | 'W' {UPPER} ;

ternary := ('I' | 'G') {UPPER} ;

quaternary := 'S' {UPPER} ;

UPPER := [A-Z] | '_' ;
LOWER := [a-z] | '_' ;
DIGIT := [0-9] ;
NON_SINGLE := (? any character except single quote (`'`) *) ;
NON_DOUBLE := (? any character except double quote (`"`) *) ;

About

The Knight Programming Language

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published